@wowok/skills 1.1.12 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/examples/Insurance/Insurance.md +142 -23
- package/examples/MyShop/MyShop.md +180 -189
- package/examples/MyShop/myshop_machine_nodes.json +14 -8
- package/examples/MyShop_Advanced/MyShop_Advanced.md +57 -56
- package/examples/ThreeBody_Signature/ThreeBody_Signature.md +431 -111
- package/examples/Travel/Travel.md +64 -18
- package/package.json +1 -1
- package/wowok-guard/SKILL.md +125 -19
|
@@ -23,10 +23,44 @@ This example demonstrates:
|
|
|
23
23
|
|
|
24
24
|
Before running this example, ensure you have:
|
|
25
25
|
|
|
26
|
-
1. **Account Setup**: The `three_body_author`
|
|
26
|
+
1. **Account Setup**: The `three_body_author` and `three_body_customer` accounts with sufficient WOW tokens
|
|
27
27
|
2. **Minimum Balance**: At least 1000 WOW for gas fees and service creation
|
|
28
28
|
|
|
29
|
-
###
|
|
29
|
+
### Step 0a: Generate Accounts
|
|
30
|
+
|
|
31
|
+
If the accounts do not exist locally, generate them first.
|
|
32
|
+
|
|
33
|
+
**Request (generate author account)**:
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"gen": {
|
|
37
|
+
"name": "three_body_author",
|
|
38
|
+
"replaceExistName": true
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Request (generate customer account)**:
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"gen": {
|
|
47
|
+
"name": "three_body_customer",
|
|
48
|
+
"replaceExistName": true
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Expected Result**:
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"gen": {
|
|
57
|
+
"address": "0x...",
|
|
58
|
+
"name": "three_body_author"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Step 0b: Check Account Balance
|
|
30
64
|
|
|
31
65
|
**Request**:
|
|
32
66
|
```json
|
|
@@ -41,15 +75,24 @@ Before running this example, ensure you have:
|
|
|
41
75
|
**Expected Result** (if account has balance):
|
|
42
76
|
```json
|
|
43
77
|
{
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
78
|
+
"result": {
|
|
79
|
+
"query_type": "account_balance",
|
|
80
|
+
"result": {
|
|
81
|
+
"address": "0x...",
|
|
82
|
+
"name_or_address": "three_body_author",
|
|
83
|
+
"balance": {
|
|
84
|
+
"coinType": "0x2::wow::WOW",
|
|
85
|
+
"coinObjectCount": 3,
|
|
86
|
+
"totalBalance": "3000000000",
|
|
87
|
+
"lockedBalance": {},
|
|
88
|
+
"fundsInAddressBalance": "0"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
48
91
|
}
|
|
49
92
|
}
|
|
50
93
|
```
|
|
51
94
|
|
|
52
|
-
|
|
95
|
+
### Step 0c: Fund Accounts via Faucet (If No Balance)
|
|
53
96
|
|
|
54
97
|
**Request**:
|
|
55
98
|
```json
|
|
@@ -67,15 +110,31 @@ Before running this example, ensure you have:
|
|
|
67
110
|
"faucet": {
|
|
68
111
|
"name_or_address": "three_body_author",
|
|
69
112
|
"result": [
|
|
70
|
-
{
|
|
71
|
-
|
|
72
|
-
|
|
113
|
+
{
|
|
114
|
+
"amount": 1000000000,
|
|
115
|
+
"id": "0x...",
|
|
116
|
+
"transferTxDigest": "..."
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"amount": 1000000000,
|
|
120
|
+
"id": "0x...",
|
|
121
|
+
"transferTxDigest": "..."
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"amount": 1000000000,
|
|
125
|
+
"id": "0x...",
|
|
126
|
+
"transferTxDigest": "..."
|
|
127
|
+
}
|
|
73
128
|
],
|
|
74
129
|
"network": "testnet"
|
|
75
130
|
}
|
|
76
131
|
}
|
|
77
132
|
```
|
|
78
133
|
|
|
134
|
+
> **Note**: Each faucet result item includes a `transferTxDigest` field representing the on-chain transaction digest for the faucet transfer. This is useful for tracking and verifying the faucet transaction on a block explorer.
|
|
135
|
+
|
|
136
|
+
> **Important**: Repeat the faucet request for `three_body_customer` as well, since Test 2 requires a funded non-author account to attempt the blocked purchase.
|
|
137
|
+
|
|
79
138
|
---
|
|
80
139
|
|
|
81
140
|
## Step 1: Create Permission Object
|
|
@@ -100,19 +159,40 @@ Create a Permission object to manage the service.
|
|
|
100
159
|
},
|
|
101
160
|
"env": {
|
|
102
161
|
"account": "three_body_author",
|
|
103
|
-
"network": "testnet"
|
|
162
|
+
"network": "testnet",
|
|
163
|
+
"confirmed": true
|
|
104
164
|
}
|
|
105
165
|
}
|
|
106
166
|
```
|
|
107
167
|
|
|
168
|
+
> **Permission Index Explanation**:
|
|
169
|
+
> - **`1000`–`1009`**: User-defined permission indexes (starting from `USER_DEFINED_PERM_INDEX_START = 1000`). In this example, `1000` is used for the "Confirm Delivery" forward and `1001` for the "Complete Signature" forward in the Machine workflow (see Step 3). The remaining indexes (`1002`–`1009`) are reserved for future workflow extensions.
|
|
170
|
+
> - **`306`**: The built-in `SERVICE_MACHINE` permission (Service module permission index `306`), which authorizes binding a Machine to a Service. This is required in Step 5 (Configure Machine) where the Machine is bound to the Service.
|
|
171
|
+
>
|
|
172
|
+
> See `BuiltinPermissionIndex` in `ts-sdk/packages/wowok/src/w/call/permission.ts` for the full list of built-in permission indexes.
|
|
173
|
+
|
|
174
|
+
> **Important**: `env.confirmed: true` is required because `replaceExistName: true` triggers a confirmation prompt (it unbinds any existing name). Without `confirmed: true`, the operation will be blocked pending user confirmation. This applies to all subsequent steps using `replaceExistName: true` or `publish: true`.
|
|
175
|
+
|
|
108
176
|
**Expected Result**:
|
|
109
177
|
```json
|
|
110
|
-
[
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
178
|
+
[
|
|
179
|
+
{
|
|
180
|
+
"type": "Permission",
|
|
181
|
+
"type_raw": "0x2::permission::Permission",
|
|
182
|
+
"object": "0x...",
|
|
183
|
+
"version": "...",
|
|
184
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
185
|
+
"change": "created"
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"type": "TableItem_PermissionPerm",
|
|
189
|
+
"type_raw": "0x2::dynamic_field::Field<address, 0x2::parent_linked_table::Node<address, vector<u16>>>",
|
|
190
|
+
"object": "0x...",
|
|
191
|
+
"version": "...",
|
|
192
|
+
"owner": {"ObjectOwner": "0x..."},
|
|
193
|
+
"change": "created"
|
|
194
|
+
}
|
|
195
|
+
]
|
|
116
196
|
```
|
|
117
197
|
|
|
118
198
|
---
|
|
@@ -157,21 +237,28 @@ Create a Guard that verifies the buyer is the service creator (author). This ens
|
|
|
157
237
|
},
|
|
158
238
|
"env": {
|
|
159
239
|
"account": "three_body_author",
|
|
160
|
-
"network": "testnet"
|
|
240
|
+
"network": "testnet",
|
|
241
|
+
"confirmed": true
|
|
161
242
|
}
|
|
162
243
|
}
|
|
163
244
|
```
|
|
164
245
|
|
|
165
246
|
> **Note**: The Guard uses a `table` to define constant values with identifiers, then references them in the `root` logic using `identifier` node type. The `root` is a direct GuardNode (no wrapper).
|
|
166
247
|
|
|
248
|
+
> **Important**: `env.confirmed: true` is required because `replaceExistName: true` triggers a confirmation prompt.
|
|
249
|
+
|
|
167
250
|
**Expected Result**:
|
|
168
251
|
```json
|
|
169
|
-
[
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
252
|
+
[
|
|
253
|
+
{
|
|
254
|
+
"type": "Guard",
|
|
255
|
+
"type_raw": "0x2::guard::Guard",
|
|
256
|
+
"object": "0x...",
|
|
257
|
+
"version": "...",
|
|
258
|
+
"owner": "Immutable",
|
|
259
|
+
"change": "created"
|
|
260
|
+
}
|
|
261
|
+
]
|
|
175
262
|
```
|
|
176
263
|
|
|
177
264
|
---
|
|
@@ -234,19 +321,50 @@ Create a Machine to define the service workflow: Book Delivery → Signature Com
|
|
|
234
321
|
},
|
|
235
322
|
"env": {
|
|
236
323
|
"account": "three_body_author",
|
|
237
|
-
"network": "testnet"
|
|
324
|
+
"network": "testnet",
|
|
325
|
+
"confirmed": true
|
|
238
326
|
}
|
|
239
327
|
}
|
|
240
328
|
```
|
|
241
329
|
|
|
330
|
+
> **Important**: `env.confirmed: true` is required because `replaceExistName: true` triggers a confirmation prompt. Additionally, `publish: true` on the Machine is an irreversible operation.
|
|
331
|
+
|
|
242
332
|
**Expected Result**:
|
|
243
333
|
```json
|
|
244
|
-
[
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
334
|
+
[
|
|
335
|
+
{
|
|
336
|
+
"type": "TableItem_MachineNode",
|
|
337
|
+
"type_raw": "0x2::dynamic_field::Field<0x1::string::String, 0x2::parent_linked_table::Node<0x1::string::String, vector<0x2::machine::NodePair>>>",
|
|
338
|
+
"object": "0x...",
|
|
339
|
+
"version": "...",
|
|
340
|
+
"owner": {"ObjectOwner": "0x..."},
|
|
341
|
+
"change": "created"
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
"type": "TableItem_MachineNode",
|
|
345
|
+
"type_raw": "0x2::dynamic_field::Field<0x1::string::String, 0x2::parent_linked_table::Node<0x1::string::String, vector<0x2::machine::NodePair>>>",
|
|
346
|
+
"object": "0x...",
|
|
347
|
+
"version": "...",
|
|
348
|
+
"owner": {"ObjectOwner": "0x..."},
|
|
349
|
+
"change": "created"
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
"type": "Machine",
|
|
353
|
+
"type_raw": "0x2::machine::Machine",
|
|
354
|
+
"object": "0x...",
|
|
355
|
+
"version": "...",
|
|
356
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
357
|
+
"change": "created"
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
"type": "TableItem_EntityLinker",
|
|
361
|
+
"type_raw": "0x2::dynamic_field::Field<address, 0x2::registrar::Votes>",
|
|
362
|
+
"object": "0x...",
|
|
363
|
+
"version": "...",
|
|
364
|
+
"owner": {"ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000aaa"},
|
|
365
|
+
"change": "created"
|
|
366
|
+
}
|
|
367
|
+
]
|
|
250
368
|
```
|
|
251
369
|
|
|
252
370
|
---
|
|
@@ -271,19 +389,34 @@ Create the Three-Body signature service without publishing. The Service must be
|
|
|
271
389
|
},
|
|
272
390
|
"env": {
|
|
273
391
|
"account": "three_body_author",
|
|
274
|
-
"network": "testnet"
|
|
392
|
+
"network": "testnet",
|
|
393
|
+
"confirmed": true
|
|
275
394
|
}
|
|
276
395
|
}
|
|
277
396
|
```
|
|
278
397
|
|
|
398
|
+
> **Important**: `env.confirmed: true` is required because `replaceExistName: true` triggers a confirmation prompt.
|
|
399
|
+
|
|
279
400
|
**Expected Result**:
|
|
280
401
|
```json
|
|
281
|
-
[
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
402
|
+
[
|
|
403
|
+
{
|
|
404
|
+
"type": "TableItem_EntityLinker",
|
|
405
|
+
"type_raw": "0x2::dynamic_field::Field<address, 0x2::registrar::Votes>",
|
|
406
|
+
"object": "0x...",
|
|
407
|
+
"version": "...",
|
|
408
|
+
"owner": {"ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000aaa"},
|
|
409
|
+
"change": "mutated"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"type": "Service",
|
|
413
|
+
"type_raw": "0x2::service::Service<0x2::wow::WOW>",
|
|
414
|
+
"object": "0x...",
|
|
415
|
+
"version": "...",
|
|
416
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
417
|
+
"change": "created"
|
|
418
|
+
}
|
|
419
|
+
]
|
|
287
420
|
```
|
|
288
421
|
|
|
289
422
|
---
|
|
@@ -309,12 +442,24 @@ Bind the published Machine to the Service. **Important**: The Service must be un
|
|
|
309
442
|
|
|
310
443
|
**Expected Result**:
|
|
311
444
|
```json
|
|
312
|
-
[
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
445
|
+
[
|
|
446
|
+
{
|
|
447
|
+
"type": "Service",
|
|
448
|
+
"type_raw": "0x2::service::Service<0x2::wow::WOW>",
|
|
449
|
+
"object": "0x...",
|
|
450
|
+
"version": "...",
|
|
451
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
452
|
+
"change": "mutated"
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"type": "TableItem_EntityLinker",
|
|
456
|
+
"type_raw": "0x2::dynamic_field::Field<address, 0x2::registrar::Votes>",
|
|
457
|
+
"object": "0x...",
|
|
458
|
+
"version": "...",
|
|
459
|
+
"owner": {"ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000aaa"},
|
|
460
|
+
"change": "created"
|
|
461
|
+
}
|
|
462
|
+
]
|
|
318
463
|
```
|
|
319
464
|
|
|
320
465
|
---
|
|
@@ -340,12 +485,24 @@ Configure the Buy Guard to restrict purchases to the author only.
|
|
|
340
485
|
|
|
341
486
|
**Expected Result**:
|
|
342
487
|
```json
|
|
343
|
-
[
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
488
|
+
[
|
|
489
|
+
{
|
|
490
|
+
"type": "Service",
|
|
491
|
+
"type_raw": "0x2::service::Service<0x2::wow::WOW>",
|
|
492
|
+
"object": "0x...",
|
|
493
|
+
"version": "...",
|
|
494
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
495
|
+
"change": "mutated"
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"type": "TableItem_EntityLinker",
|
|
499
|
+
"type_raw": "0x2::dynamic_field::Field<address, 0x2::registrar::Votes>",
|
|
500
|
+
"object": "0x...",
|
|
501
|
+
"version": "...",
|
|
502
|
+
"owner": {"ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000aaa"},
|
|
503
|
+
"change": "created"
|
|
504
|
+
}
|
|
505
|
+
]
|
|
349
506
|
```
|
|
350
507
|
|
|
351
508
|
---
|
|
@@ -389,12 +546,16 @@ Set up fund allocation: 100% to the author upon order completion.
|
|
|
389
546
|
|
|
390
547
|
**Expected Result**:
|
|
391
548
|
```json
|
|
392
|
-
[
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
549
|
+
[
|
|
550
|
+
{
|
|
551
|
+
"type": "Service",
|
|
552
|
+
"type_raw": "0x2::service::Service<0x2::wow::WOW>",
|
|
553
|
+
"object": "0x...",
|
|
554
|
+
"version": "...",
|
|
555
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
556
|
+
"change": "mutated"
|
|
557
|
+
}
|
|
558
|
+
]
|
|
398
559
|
```
|
|
399
560
|
|
|
400
561
|
---
|
|
@@ -426,19 +587,26 @@ Add sales items and publish the service to make it available for orders.
|
|
|
426
587
|
},
|
|
427
588
|
"env": {
|
|
428
589
|
"account": "three_body_author",
|
|
429
|
-
"network": "testnet"
|
|
590
|
+
"network": "testnet",
|
|
591
|
+
"confirmed": true
|
|
430
592
|
}
|
|
431
593
|
}
|
|
432
594
|
```
|
|
433
595
|
|
|
596
|
+
> **Important**: `env.confirmed: true` is **critical** here because `publish: true` is an irreversible operation that locks the `machine` and `order_allocators` fields. The server will block the transaction with a "Publish confirmation (irreversible lock)" prompt until `confirmed: true` is provided.
|
|
597
|
+
|
|
434
598
|
**Expected Result**:
|
|
435
599
|
```json
|
|
436
|
-
[
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
600
|
+
[
|
|
601
|
+
{
|
|
602
|
+
"type": "Service",
|
|
603
|
+
"type_raw": "0x2::service::Service<0x2::wow::WOW>",
|
|
604
|
+
"object": "0x...",
|
|
605
|
+
"version": "...",
|
|
606
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
607
|
+
"change": "mutated"
|
|
608
|
+
}
|
|
609
|
+
]
|
|
442
610
|
```
|
|
443
611
|
|
|
444
612
|
---
|
|
@@ -464,12 +632,16 @@ Unpause the service to allow order creation.
|
|
|
464
632
|
|
|
465
633
|
**Expected Result**:
|
|
466
634
|
```json
|
|
467
|
-
[
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
635
|
+
[
|
|
636
|
+
{
|
|
637
|
+
"type": "Service",
|
|
638
|
+
"type_raw": "0x2::service::Service<0x2::wow::WOW>",
|
|
639
|
+
"object": "0x...",
|
|
640
|
+
"version": "...",
|
|
641
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
642
|
+
"change": "mutated"
|
|
643
|
+
}
|
|
644
|
+
]
|
|
473
645
|
```
|
|
474
646
|
|
|
475
647
|
---
|
|
@@ -491,28 +663,76 @@ Query the service to verify all configurations.
|
|
|
491
663
|
**Expected Result**:
|
|
492
664
|
```json
|
|
493
665
|
{
|
|
494
|
-
"
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
666
|
+
"result": {
|
|
667
|
+
"query_type": "onchain_objects",
|
|
668
|
+
"result": {
|
|
669
|
+
"objects": [
|
|
670
|
+
{
|
|
671
|
+
"object": "0x...",
|
|
672
|
+
"type": "Service",
|
|
673
|
+
"type_raw": "0x2::service::Service<0x2::wow::WOW>",
|
|
674
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
675
|
+
"version": "...",
|
|
676
|
+
"previousTransaction": "...",
|
|
677
|
+
"description": "Three-Body author book signature service. Provide a message up to 10 characters, and the author will sign your book. Process: 1.Book Delivery 2.Signature Completion. Fee: 888.",
|
|
678
|
+
"location": "",
|
|
679
|
+
"sales": [
|
|
680
|
+
{
|
|
681
|
+
"name": "Three-Body Book Signature",
|
|
682
|
+
"stock": "100",
|
|
683
|
+
"suspension": false,
|
|
684
|
+
"price": "888",
|
|
685
|
+
"wip": "",
|
|
686
|
+
"wip_hash": ""
|
|
687
|
+
}
|
|
688
|
+
],
|
|
689
|
+
"repositories": [],
|
|
690
|
+
"buy_guard": "0x...",
|
|
691
|
+
"machine": "0x...",
|
|
692
|
+
"bPublished": true,
|
|
693
|
+
"bPaused": false,
|
|
694
|
+
"customer_required": ["phone", "email", "shipping_address"],
|
|
695
|
+
"arbitrations": [],
|
|
696
|
+
"compensation_fund": "0",
|
|
697
|
+
"paused_time": null,
|
|
698
|
+
"setting_lock_duration": "2592000000",
|
|
699
|
+
"order_allocators": {
|
|
700
|
+
"description": "Three-Body signature service fund allocation - 100% to author",
|
|
701
|
+
"threshold": "0",
|
|
702
|
+
"allocators": [
|
|
703
|
+
{
|
|
704
|
+
"guard": "0x...",
|
|
705
|
+
"sharing": [
|
|
706
|
+
{
|
|
707
|
+
"who": {"Signer": null},
|
|
708
|
+
"sharing": "10000",
|
|
709
|
+
"mode": 1
|
|
710
|
+
}
|
|
711
|
+
],
|
|
712
|
+
"fix": "0",
|
|
713
|
+
"max": null
|
|
714
|
+
}
|
|
715
|
+
]
|
|
716
|
+
},
|
|
717
|
+
"rewards": [],
|
|
718
|
+
"um": null,
|
|
719
|
+
"permission": "0x...",
|
|
720
|
+
"cache_expire": 1234567890,
|
|
721
|
+
"query_name": "three_body_signature_service"
|
|
722
|
+
}
|
|
723
|
+
]
|
|
505
724
|
}
|
|
506
|
-
|
|
507
|
-
"buy_guard": "three_body_buy_guard",
|
|
508
|
-
"machine": "three_body_machine",
|
|
509
|
-
"bPublished": true,
|
|
510
|
-
"bPaused": false,
|
|
511
|
-
"customer_required": ["phone", "email", "shipping_address"],
|
|
512
|
-
"permission": "three_body_permission"
|
|
725
|
+
}
|
|
513
726
|
}
|
|
514
727
|
```
|
|
515
728
|
|
|
729
|
+
> **Field Reference**:
|
|
730
|
+
> - **`buy_guard`**, **`machine`**, **`permission`**: Return **on-chain object IDs** (not names). The on-chain data stores raw object IDs; resolving them back to local mark names requires a separate reverse lookup that is not performed by `onchain_objects` queries.
|
|
731
|
+
> - **`query_name`**: The original name string passed in the query request (here, `"three_body_signature_service"`). This is automatically populated by the SDK from the input `objects` array, so you can identify which queried name corresponds to which returned object.
|
|
732
|
+
> - **`order_allocators.allocators[].sharing[].who`**: `{"Signer": null}` indicates the recipient is the order signer (the buyer). The `null` value is the on-chain representation; the input used `"signer"` as a placeholder.
|
|
733
|
+
> - **`order_allocators.allocators[].sharing[].mode`**: `1` is the numeric enum for `Rate` mode (input accepts the string `"Rate"`, output returns the numeric `1`).
|
|
734
|
+
> - **`order_allocators.allocators[].fix`** and **`max`**: Additional fields returned on-chain (default `"0"` and `null` respectively) that are not part of the input schema but are present in the on-chain data structure.
|
|
735
|
+
|
|
516
736
|
---
|
|
517
737
|
|
|
518
738
|
## Testing the Buy Guard
|
|
@@ -565,26 +785,78 @@ The author (`three_body_author`) should be able to purchase the service.
|
|
|
565
785
|
```json
|
|
566
786
|
[
|
|
567
787
|
{
|
|
568
|
-
"type": "
|
|
569
|
-
"
|
|
788
|
+
"type": "Service",
|
|
789
|
+
"type_raw": "0x2::service::Service<0x2::wow::WOW>",
|
|
790
|
+
"object": "0x...",
|
|
791
|
+
"version": "...",
|
|
792
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
793
|
+
"change": "mutated"
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
"type": "TableItem_EntityLinker",
|
|
797
|
+
"type_raw": "0x2::dynamic_field::Field<address, 0x2::registrar::Votes>",
|
|
798
|
+
"object": "0x...",
|
|
799
|
+
"version": "...",
|
|
800
|
+
"owner": {"ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000aaa"},
|
|
801
|
+
"change": "mutated"
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
"type": "TableItem_EntityLinker",
|
|
805
|
+
"type_raw": "0x2::dynamic_field::Field<address, 0x2::registrar::Votes>",
|
|
806
|
+
"object": "0x...",
|
|
807
|
+
"version": "...",
|
|
808
|
+
"owner": {"ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000aaa"},
|
|
809
|
+
"change": "mutated"
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
"type": "TableItem_EntityLinker",
|
|
813
|
+
"type_raw": "0x2::dynamic_field::Field<address, 0x2::registrar::Votes>",
|
|
814
|
+
"object": "0x...",
|
|
815
|
+
"version": "...",
|
|
816
|
+
"owner": {"ObjectOwner": "0x0000000000000000000000000000000000000000000000000000000000000aaa"},
|
|
817
|
+
"change": "created"
|
|
818
|
+
},
|
|
819
|
+
{
|
|
820
|
+
"type": "Allocation",
|
|
821
|
+
"type_raw": "0x2::allocation::Allocation<0x2::wow::WOW>",
|
|
822
|
+
"object": "0x...",
|
|
570
823
|
"version": "...",
|
|
824
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
571
825
|
"change": "created"
|
|
572
826
|
},
|
|
573
827
|
{
|
|
574
828
|
"type": "Order",
|
|
575
|
-
"
|
|
829
|
+
"type_raw": "0x2::order::Order",
|
|
830
|
+
"object": "0x...",
|
|
576
831
|
"version": "...",
|
|
832
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
577
833
|
"change": "created"
|
|
578
834
|
},
|
|
579
835
|
{
|
|
580
|
-
"type": "
|
|
581
|
-
"
|
|
836
|
+
"type": "Progress",
|
|
837
|
+
"type_raw": "0x2::progress::Progress",
|
|
838
|
+
"object": "0x...",
|
|
582
839
|
"version": "...",
|
|
840
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
583
841
|
"change": "created"
|
|
584
842
|
}
|
|
585
843
|
]
|
|
586
844
|
```
|
|
587
845
|
|
|
846
|
+
> **Why So Many Objects? (Information Injection Design)**
|
|
847
|
+
>
|
|
848
|
+
> The `order_new` operation intentionally returns ALL objects affected by the transaction, not just the primary created objects. This is a deliberate "information injection" design so callers get a complete view of the state changes:
|
|
849
|
+
>
|
|
850
|
+
> 1. **`Service` (mutated)**: The service's inventory/stock is decremented as a side effect of the purchase.
|
|
851
|
+
> 2. **`TableItem_EntityLinker` ×3 (mutated/created)**: The order process links the buyer, the service, and the buy_guard entities via the on-chain `EntityLinker` registrar (owner `0xaaa`). These are the entity-graph edges created/updated by the order.
|
|
852
|
+
> 3. **`Allocation` (created)**: The fund allocation object that will distribute the payment according to `order_allocators`.
|
|
853
|
+
> 4. **`Order` (created)**: The primary order object tracking the purchase.
|
|
854
|
+
> 5. **`Progress` (created)**: The workflow progress object tracking the order through the Machine nodes.
|
|
855
|
+
>
|
|
856
|
+
> The returned objects use **on-chain object IDs** (not the names assigned via `namedNewOrder`/`namedNewProgress`/`namedNewAllocation`). To resolve an object ID back to its local mark name, use the `query_toolkit` with `query_type: "local_names"` and pass the addresses array.
|
|
857
|
+
>
|
|
858
|
+
> The return order reflects the transaction's execution order: Service (inventory update) → EntityLinker (entity graph edges) → Allocation → Order → Progress, following the dependency chain of object creation.
|
|
859
|
+
|
|
588
860
|
---
|
|
589
861
|
|
|
590
862
|
### Test 2: Non-Author Purchase (Should Fail)
|
|
@@ -620,11 +892,23 @@ Any other account attempting to purchase should fail with Buy Guard verification
|
|
|
620
892
|
```
|
|
621
893
|
|
|
622
894
|
**Expected Result** (Error):
|
|
623
|
-
```json
|
|
624
|
-
{
|
|
625
|
-
"error": "Transaction resolution failed: MoveAbort in 8th command, abort code: 7 (Verify failed), in '0x2::passport::result_for_guard'"
|
|
626
|
-
}
|
|
627
895
|
```
|
|
896
|
+
Error: Error Description: Verification failed
|
|
897
|
+
Transaction resolution failed: MoveAbort in 8th command, abort code: 7 (Verify failed), in '0x0000000000000000000000000000000000000000000000000000000000000002::passport::result_for_guard' (instruction 17)
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
> **Error Format Explained (Information Injection Design)**
|
|
901
|
+
>
|
|
902
|
+
> The error response is intentionally enriched with detailed diagnostic information via the `enrichMoveError` function in the SDK. This is a deliberate "information injection" design so callers can precisely diagnose failures without needing to replay the transaction:
|
|
903
|
+
>
|
|
904
|
+
> - **`Error: ` prefix**: Added by the MCP handler (`handler.ts`) to mark the response as an error.
|
|
905
|
+
> - **`Error Description: Verification failed`**: A human-readable classification of the error category, generated by the `classifyError` logic.
|
|
906
|
+
> - **`Transaction resolution failed: MoveAbort in 8th command`**: The raw Sui SDK error indicating which transaction command (8th) aborted.
|
|
907
|
+
> - **`abort code: 7 (Verify failed)`**: The Move abort code (7) translated to its semantic meaning (`Verify failed`).
|
|
908
|
+
> - **`0x0000...0002::passport::result_for_guard`**: The fully-qualified module path using the 64-character hex address form (the Sui SDK returns the full canonical form, not the short `0x2` shorthand).
|
|
909
|
+
> - **`(instruction 17)`**: The specific instruction index within the Move function where the abort occurred.
|
|
910
|
+
>
|
|
911
|
+
> The error is returned as a **plain text string** (not a JSON object), because it combines multiple layers of diagnostic information from different sources (Sui SDK, Move VM, MCP handler). Callers detecting errors should check for the `Error:` prefix or use the `isError` flag on the MCP response.
|
|
628
912
|
|
|
629
913
|
---
|
|
630
914
|
|
|
@@ -651,19 +935,34 @@ The author confirms the book has been delivered.
|
|
|
651
935
|
},
|
|
652
936
|
"env": {
|
|
653
937
|
"account": "three_body_author",
|
|
654
|
-
"network": "testnet"
|
|
938
|
+
"network": "testnet",
|
|
939
|
+
"no_cache": true
|
|
655
940
|
}
|
|
656
941
|
}
|
|
657
942
|
```
|
|
658
943
|
|
|
944
|
+
> **Tip**: Although `no_cache: true` is most critical for the second Progress operation (Node 2), it is recommended to use it for **all** sequential Progress operations on the same object to ensure the SDK always reads the latest on-chain state.
|
|
945
|
+
|
|
659
946
|
**Expected Result**:
|
|
660
947
|
```json
|
|
661
|
-
[
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
948
|
+
[
|
|
949
|
+
{
|
|
950
|
+
"type": "Progress",
|
|
951
|
+
"type_raw": "0x2::progress::Progress",
|
|
952
|
+
"object": "0x...",
|
|
953
|
+
"version": "...",
|
|
954
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
955
|
+
"change": "mutated"
|
|
956
|
+
},
|
|
957
|
+
{
|
|
958
|
+
"type": "TableItem_ProgressHistory",
|
|
959
|
+
"type_raw": "0x2::dynamic_field::Field<u64, 0x2::progress::History>",
|
|
960
|
+
"object": "0x...",
|
|
961
|
+
"version": "...",
|
|
962
|
+
"owner": {"ObjectOwner": "0x..."},
|
|
963
|
+
"change": "created"
|
|
964
|
+
}
|
|
965
|
+
]
|
|
667
966
|
```
|
|
668
967
|
|
|
669
968
|
### Node 2: Signature Completed
|
|
@@ -695,12 +994,24 @@ The author completes the signature.
|
|
|
695
994
|
|
|
696
995
|
**Expected Result**:
|
|
697
996
|
```json
|
|
698
|
-
[
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
997
|
+
[
|
|
998
|
+
{
|
|
999
|
+
"type": "Progress",
|
|
1000
|
+
"type_raw": "0x2::progress::Progress",
|
|
1001
|
+
"object": "0x...",
|
|
1002
|
+
"version": "...",
|
|
1003
|
+
"owner": {"Shared": {"initial_shared_version": "..."}},
|
|
1004
|
+
"change": "mutated"
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
"type": "TableItem_ProgressHistory",
|
|
1008
|
+
"type_raw": "0x2::dynamic_field::Field<u64, 0x2::progress::History>",
|
|
1009
|
+
"object": "0x...",
|
|
1010
|
+
"version": "...",
|
|
1011
|
+
"owner": {"ObjectOwner": "0x..."},
|
|
1012
|
+
"change": "created"
|
|
1013
|
+
}
|
|
1014
|
+
]
|
|
704
1015
|
```
|
|
705
1016
|
|
|
706
1017
|
---
|
|
@@ -764,6 +1075,15 @@ Each node transition requires the author's confirmation, ensuring accountability
|
|
|
764
1075
|
- Configure Service (add machine, buy_guard, order_allocators)
|
|
765
1076
|
- Publish Service (LAST - once published, many changes are blocked)
|
|
766
1077
|
|
|
767
|
-
3. **Use `
|
|
1078
|
+
3. **Use `confirmed: true` for Irreversible/Destructive Operations**: The MCP server enforces a two-phase confirmation for safety. You MUST add `"confirmed": true` to the `env` for:
|
|
1079
|
+
- Any operation using `replaceExistName: true` (unbinds existing names)
|
|
1080
|
+
- Any `publish: true` operation (irreversible lock on Machine/Service)
|
|
1081
|
+
Without `confirmed: true`, the server returns a `Confirmation required` warning and blocks the transaction.
|
|
1082
|
+
|
|
1083
|
+
4. **Use `no_cache: true` for Sequential Operations**: When performing multiple operations on the same object in sequence (especially Progress workflow advancement), always set `no_cache: true` in the `env` to ensure the SDK reads the latest on-chain state.
|
|
1084
|
+
|
|
1085
|
+
5. **Query Toolkit is Your Best Friend**: Use queries constantly to verify objects exist, check configurations, debug issues, and confirm state changes.
|
|
1086
|
+
|
|
1087
|
+
6. **Object IDs vs Names in Responses**: On-chain query responses return **object IDs** (e.g., `0x8202...`) for cross-object references like `buy_guard`, `machine`, `permission`. The `query_name` field in the response echoes back the original query input name. To resolve object IDs back to local mark names, use `query_toolkit` with `query_type: "local_names"`.
|
|
768
1088
|
|
|
769
|
-
|
|
1089
|
+
7. **Information Injection in Transaction Responses**: Mutation/creation operations return ALL objects affected by the transaction (including side effects like `TableItem_EntityLinker` and `TableItem_ProgressHistory`), not just the primary target. This is a deliberate design for transparency. The return order reflects the transaction's execution order.
|