@wowok/skills 1.1.9 → 1.1.10
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/LICENSE +36 -0
- package/README.md +1 -8
- package/examples/Insurance/Insurance.md +218 -90
- package/examples/MyShop/MyShop.md +513 -245
- package/examples/MyShop/myshop_machine_nodes.json +87 -0
- package/examples/MyShop_Advanced/MyShop_Advanced.md +633 -228
- package/examples/ThreeBody_Signature/ThreeBody_Signature.md +195 -202
- package/examples/Travel/Travel.md +775 -487
- package/examples/Travel/calc-weather-timestamps.js +9 -5
- package/package.json +4 -3
- package/wowok-tools/SKILL.md +21 -0
- package/examples/Insurance/Insurance_TestResults.md +0 -481
- package/examples/Insurance/insurance_complete_guard_v1.json +0 -50
- package/examples/MyShop/MyShop_TestResults.md +0 -1003
- package/examples/MyShop_Advanced/MyShop_Advanced_MerchantSystem_TestResults.md +0 -1297
- package/examples/MyShop_Advanced/MyShop_Advanced_OrderFlow_TestResults.md +0 -743
- package/examples/MyShop_Advanced/machine_nodes.json +0 -222
- package/examples/MyShop_Advanced/three_body_signature.wip +0 -30
- package/examples/ThreeBody_Signature/ThreeBody_Signature_TestResults.md +0 -599
- package/examples/Travel/Travel_TestResults.md +0 -743
- package/examples/Travel/travel_machine_v2_export.json +0 -104
- package/examples/Travel/weather_check_guard_v1.json +0 -51
|
@@ -2,108 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
A complete example demonstrating how to create an Iceland travel service using WoWok protocol. This service integrates weather-dependent activities, insurance sub-orders, and multi-node workflow management.
|
|
4
4
|
|
|
5
|
-
> **View Actual Execution Results**: See [Travel_TestResults.md](Travel_TestResults.md) for real testnet execution results with actual object addresses and transaction outputs.
|
|
6
|
-
|
|
7
5
|
---
|
|
8
6
|
|
|
9
7
|
## Core Requirements & Features
|
|
10
8
|
|
|
11
9
|
| Requirement | Description | Implementation |
|
|
12
10
|
|-------------|-------------|----------------|
|
|
13
|
-
| **Weather
|
|
14
|
-
| **Insurance Sub-Order** | Travel provider purchases insurance as supply chain sub-order | Machine forward creates insurance Order via Progress |
|
|
11
|
+
| **Weather Data Query** | Demonstrate querying weather Repository | Guard checks if weather data exists for a given date |
|
|
15
12
|
| **Multi-Node Workflow** | Start -> Buy Insurance -> SPA -> Ice Scooting -> Complete | Machine with 5 nodes and conditional paths |
|
|
16
13
|
| **Time-Lock Completion** | Prevent premature order completion | Guard using Order + convert_witness(TypeOrderProgress) |
|
|
17
|
-
| **Cancellation Support** | Allow order cancellation
|
|
14
|
+
| **Cancellation Support** | Allow order cancellation from Ice Scooting node | Cancel forward with permission-based access |
|
|
18
15
|
| **Arbitration** | Dispute resolution for order conflicts | Arbitration object bound to Service |
|
|
19
|
-
|
|
20
|
-
### Key Design Decisions
|
|
21
|
-
|
|
22
|
-
1. **Weather Data via Repository**: Weather conditions are stored in a Repository object with policy "Condition". Each day's weather is keyed by UTC timestamp (converted to Address via `convert_number_address`).
|
|
23
|
-
2. **Insurance as Sub-Order**: The "Buy Insurance" node creates a sub-order on the insurance service, linking the two workflows.
|
|
24
|
-
3. **Time-Lock via Witness Conversion**: The Complete node Guard uses `convert_witness: 100` (TypeOrderProgress) to access Progress data from the submitted Order.
|
|
25
|
-
4. **Dynamic Time Calculation**: Weather data timestamps are calculated dynamically based on current time, ensuring the example is always testable.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Overview
|
|
30
|
-
|
|
31
|
-
This example demonstrates:
|
|
32
|
-
|
|
33
|
-
- **Travel Service Setup**: Permission, Arbitration, Guards, Machine, and Service creation
|
|
34
|
-
- **Weather Data Management**: Repository with weather conditions for activity validation
|
|
35
|
-
- **Insurance Integration**: Supply chain sub-order for insurance purchase
|
|
36
|
-
- **Multi-Node Workflow**: Complex workflow with weather-dependent paths
|
|
37
|
-
- **Time-Lock Guards**: Using Order + convert_witness for Progress data access
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Architecture
|
|
42
|
-
|
|
43
|
-
### System Components
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
+--------------------------------------------------------------------------+
|
|
47
|
-
| Iceland Travel Service System |
|
|
48
|
-
+--------------------------------------------------------------------------+
|
|
49
|
-
| |
|
|
50
|
-
| +--------------+ +--------------+ +--------------+ +--------------+ |
|
|
51
|
-
| | Permission | | Arbitration | | Guards | | Repository | |
|
|
52
|
-
| | (travel_ | | (travel_ | | (weather_ | | (weather_ | |
|
|
53
|
-
| | permission)| | arbitration)| | check, | | repo) | |
|
|
54
|
-
| +------+-------+ +------+-------+ | complete, | +------+-------+ |
|
|
55
|
-
| | | | cancel) | | |
|
|
56
|
-
| v v +------+-------+ | |
|
|
57
|
-
| +--------------+ +--------------+ | | |
|
|
58
|
-
| | Machine | | Service | | | |
|
|
59
|
-
| | (travel_ |<-| (travel_ | | | |
|
|
60
|
-
| | machine) | | service) | | | |
|
|
61
|
-
| +--------------+ +--------------+ | | |
|
|
62
|
-
| | | |
|
|
63
|
-
| Workflow: | | |
|
|
64
|
-
| Start -> Buy Insurance -> SPA -> Ice Scooting | | |
|
|
65
|
-
| | | | |
|
|
66
|
-
| +----------+---------+----------+ | |
|
|
67
|
-
| | | | |
|
|
68
|
-
| v v | |
|
|
69
|
-
| +----------+ +----------+ | |
|
|
70
|
-
| | Complete | | Cancel | | |
|
|
71
|
-
| |(TimeLock)| | | | |
|
|
72
|
-
| +----------+ +----------+ | |
|
|
73
|
-
| |
|
|
74
|
-
+--------------------------------------------------------------------------+
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Workflow Diagram
|
|
78
|
-
|
|
79
|
-
```
|
|
80
|
-
+-------------+
|
|
81
|
-
| Start |
|
|
82
|
-
+------+------+
|
|
83
|
-
|
|
|
84
|
-
v
|
|
85
|
-
+-------------+
|
|
86
|
-
|Buy Insurance| ---> Creates sub-order on insurance service
|
|
87
|
-
+------+------+
|
|
88
|
-
|
|
|
89
|
-
v
|
|
90
|
-
+-------------+
|
|
91
|
-
| SPA |
|
|
92
|
-
+------+------+
|
|
93
|
-
|
|
|
94
|
-
v
|
|
95
|
-
+-------------+
|
|
96
|
-
|Ice Scooting | ---> Weather check Guard (sunny required)
|
|
97
|
-
+------+------+
|
|
98
|
-
|
|
|
99
|
-
+-----------+-----------+
|
|
100
|
-
| |
|
|
101
|
-
v v
|
|
102
|
-
+-------------+ +-------------+
|
|
103
|
-
| Complete | | Cancel |
|
|
104
|
-
| (Time-Lock) | | |
|
|
105
|
-
+-------------+ +-------------+
|
|
106
|
-
```
|
|
16
|
+
| **Revenue Allocation** | Distribute funds based on progress state | Three allocation Guards for different refund scenarios |
|
|
107
17
|
|
|
108
18
|
---
|
|
109
19
|
|
|
@@ -111,11 +21,10 @@ This example demonstrates:
|
|
|
111
21
|
|
|
112
22
|
Before running this example, ensure you have:
|
|
113
23
|
|
|
114
|
-
1. An account named `
|
|
115
|
-
2. An account named `
|
|
116
|
-
3. An account named `
|
|
117
|
-
4.
|
|
118
|
-
5. Access to the WoWok MCP server
|
|
24
|
+
1. An account named `travel_provider` with sufficient WOW tokens
|
|
25
|
+
2. An account named `weather_provider` with sufficient WOW tokens
|
|
26
|
+
3. An account named `alice` (test customer) with sufficient WOW tokens
|
|
27
|
+
4. Access to the WoWok MCP server
|
|
119
28
|
|
|
120
29
|
### Create Accounts
|
|
121
30
|
|
|
@@ -124,7 +33,7 @@ Before running this example, ensure you have:
|
|
|
124
33
|
```json
|
|
125
34
|
{
|
|
126
35
|
"gen": {
|
|
127
|
-
"name": "
|
|
36
|
+
"name": "travel_provider",
|
|
128
37
|
"replaceExistName": true
|
|
129
38
|
}
|
|
130
39
|
}
|
|
@@ -133,7 +42,7 @@ Before running this example, ensure you have:
|
|
|
133
42
|
```json
|
|
134
43
|
{
|
|
135
44
|
"gen": {
|
|
136
|
-
"name": "
|
|
45
|
+
"name": "weather_provider",
|
|
137
46
|
"replaceExistName": true
|
|
138
47
|
}
|
|
139
48
|
}
|
|
@@ -142,7 +51,7 @@ Before running this example, ensure you have:
|
|
|
142
51
|
```json
|
|
143
52
|
{
|
|
144
53
|
"gen": {
|
|
145
|
-
"name": "
|
|
54
|
+
"name": "alice",
|
|
146
55
|
"replaceExistName": true
|
|
147
56
|
}
|
|
148
57
|
}
|
|
@@ -156,7 +65,7 @@ Before running this example, ensure you have:
|
|
|
156
65
|
{
|
|
157
66
|
"faucet": {
|
|
158
67
|
"network": "testnet",
|
|
159
|
-
"name_or_address": "
|
|
68
|
+
"name_or_address": "travel_provider"
|
|
160
69
|
}
|
|
161
70
|
}
|
|
162
71
|
```
|
|
@@ -165,7 +74,7 @@ Before running this example, ensure you have:
|
|
|
165
74
|
{
|
|
166
75
|
"faucet": {
|
|
167
76
|
"network": "testnet",
|
|
168
|
-
"name_or_address": "
|
|
77
|
+
"name_or_address": "weather_provider"
|
|
169
78
|
}
|
|
170
79
|
}
|
|
171
80
|
```
|
|
@@ -174,7 +83,7 @@ Before running this example, ensure you have:
|
|
|
174
83
|
{
|
|
175
84
|
"faucet": {
|
|
176
85
|
"network": "testnet",
|
|
177
|
-
"name_or_address": "
|
|
86
|
+
"name_or_address": "alice"
|
|
178
87
|
}
|
|
179
88
|
}
|
|
180
89
|
```
|
|
@@ -183,41 +92,53 @@ Before running this example, ensure you have:
|
|
|
183
92
|
|
|
184
93
|
## Step 0: Setup Weather Data
|
|
185
94
|
|
|
186
|
-
Before creating the travel service, set up the weather Repository with
|
|
95
|
+
Before creating the travel service, set up the weather Repository with the next 5 days of weather data. The weather data is keyed by timestamp, and the `weather_check_guard` (Step 3.1) will query this repository at runtime when the customer enters the "Ice Scooting" node (Step 7.3). You must use timestamps that are valid at the time you run this example.
|
|
187
96
|
|
|
188
97
|
### 0.1 Calculate Weather Timestamps
|
|
189
98
|
|
|
190
|
-
|
|
99
|
+
The weather record `id` is a UTC timestamp (in milliseconds). It must be **aligned to UTC 00:00:00** so that the same timestamp can be reproduced and submitted later in Step 7.3. Run the following JavaScript (also available as `calc-weather-timestamps.js` in this example folder):
|
|
191
100
|
|
|
192
101
|
```js
|
|
193
|
-
const DAY_MS = 86400000;
|
|
102
|
+
const DAY_MS = 86400000; // 24 * 60 * 60 * 1000
|
|
194
103
|
const now = Date.now();
|
|
104
|
+
// Align to UTC 00:00:00 of today, then compute the next 5 days.
|
|
105
|
+
// Repository data is keyed by timestamp, so the submitted activity date
|
|
106
|
+
// must match exactly the id used when adding weather data.
|
|
195
107
|
const todayStart = Math.floor(now / DAY_MS) * DAY_MS;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
console.log(`Day ${5-i}: ${ts} (${new Date(ts).toISOString()})`);
|
|
108
|
+
for (let i = 1; i <= 5; i++) {
|
|
109
|
+
const ts = todayStart + i * DAY_MS;
|
|
110
|
+
console.log(`Day ${i}: ${ts} (${new Date(ts).toISOString()})`);
|
|
200
111
|
}
|
|
201
112
|
```
|
|
202
113
|
|
|
203
|
-
|
|
114
|
+
**Sample output** (run on 2026-07-08 — re-run the script to get current values for your test time):
|
|
204
115
|
|
|
205
|
-
|
|
116
|
+
```
|
|
117
|
+
Day 1: 1783555200000 (2026-07-09T00:00:00.000Z)
|
|
118
|
+
Day 2: 1783641600000 (2026-07-10T00:00:00.000Z)
|
|
119
|
+
Day 3: 1783728000000 (2026-07-11T00:00:00.000Z)
|
|
120
|
+
Day 4: 1783814400000 (2026-07-12T00:00:00.000Z)
|
|
121
|
+
Day 5: 1783900800000 (2026-07-13T00:00:00.000Z)
|
|
122
|
+
```
|
|
206
123
|
|
|
207
|
-
**
|
|
124
|
+
> **Important**: Always re-run the script at test time — the timestamps depend on the current date. Copy the 5 printed values; they will be used in Step 0.4 (as repository data `id`) and Step 7.3 (as the submitted `activity_date`). The two must match exactly, otherwise the Guard query `repository.data has("Condition", convert_number_address(activity_date))` will return false.
|
|
125
|
+
|
|
126
|
+
### 0.2 Create Weather Permission
|
|
127
|
+
|
|
128
|
+
**Prompt**: Create a Permission object named "weather_permission".
|
|
208
129
|
|
|
209
130
|
```json
|
|
210
131
|
{
|
|
211
132
|
"operation_type": "permission",
|
|
212
133
|
"data": {
|
|
213
134
|
"object": {
|
|
214
|
-
"name": "
|
|
135
|
+
"name": "weather_permission",
|
|
215
136
|
"replaceExistName": true
|
|
216
137
|
},
|
|
217
138
|
"description": "Weather repository permission"
|
|
218
139
|
},
|
|
219
140
|
"env": {
|
|
220
|
-
"account": "
|
|
141
|
+
"account": "weather_provider",
|
|
221
142
|
"network": "testnet"
|
|
222
143
|
}
|
|
223
144
|
}
|
|
@@ -225,17 +146,15 @@ First, create a Permission object for the weather repository.
|
|
|
225
146
|
|
|
226
147
|
### 0.3 Create Weather Repository with Policies
|
|
227
148
|
|
|
228
|
-
Create
|
|
229
|
-
|
|
230
|
-
**Prompt**: Create a Repository named "weather_repo_v2" with "Condition" policy.
|
|
149
|
+
**Prompt**: Create a Repository named "weather_repo" with "Condition" policy.
|
|
231
150
|
|
|
232
151
|
```json
|
|
233
152
|
{
|
|
234
153
|
"operation_type": "repository",
|
|
235
154
|
"data": {
|
|
236
155
|
"object": {
|
|
237
|
-
"name": "
|
|
238
|
-
"permission": "
|
|
156
|
+
"name": "weather_repo",
|
|
157
|
+
"permission": "weather_permission",
|
|
239
158
|
"replaceExistName": true
|
|
240
159
|
},
|
|
241
160
|
"description": "Weather data repository for Iceland travel activities",
|
|
@@ -253,7 +172,7 @@ Create the weather repository with the "Condition" policy included during creati
|
|
|
253
172
|
}
|
|
254
173
|
},
|
|
255
174
|
"env": {
|
|
256
|
-
"account": "
|
|
175
|
+
"account": "weather_provider",
|
|
257
176
|
"network": "testnet"
|
|
258
177
|
}
|
|
259
178
|
}
|
|
@@ -261,90 +180,93 @@ Create the weather repository with the "Condition" policy included during creati
|
|
|
261
180
|
|
|
262
181
|
### 0.4 Add Weather Data
|
|
263
182
|
|
|
264
|
-
Add 5 days of weather data. The
|
|
183
|
+
Add 5 days of weather data. The `weather_check_guard` (Step 3.1) only verifies that a record **exists** for the activity date (via `repository.data has`), so all 5 days will pass the Guard regardless of the condition value. The "rainy" value on Day 5 is informational only — to actually reject based on weather condition, you would need a Guard that queries `repository.data` and compares the value, which is more complex and not used in this example.
|
|
265
184
|
|
|
266
|
-
**Prompt**: Add weather data to "
|
|
185
|
+
**Prompt**: Add weather data to "weather_repo" with policy "Condition".
|
|
267
186
|
|
|
268
187
|
```json
|
|
269
188
|
{
|
|
270
189
|
"operation_type": "repository",
|
|
271
190
|
"data": {
|
|
272
|
-
"object": "
|
|
191
|
+
"object": "weather_repo",
|
|
273
192
|
"data_add": {
|
|
274
193
|
"name": "Condition",
|
|
275
194
|
"items": [
|
|
276
195
|
{
|
|
277
196
|
"data": [
|
|
278
|
-
{"id":
|
|
279
|
-
{"id":
|
|
280
|
-
{"id":
|
|
281
|
-
{"id":
|
|
282
|
-
{"id":
|
|
197
|
+
{"id": <DAY1_TIMESTAMP>, "data": "sunny"},
|
|
198
|
+
{"id": <DAY2_TIMESTAMP>, "data": "sunny"},
|
|
199
|
+
{"id": <DAY3_TIMESTAMP>, "data": "sunny"},
|
|
200
|
+
{"id": <DAY4_TIMESTAMP>, "data": "sunny"},
|
|
201
|
+
{"id": <DAY5_TIMESTAMP>, "data": "rainy"}
|
|
283
202
|
]
|
|
284
203
|
}
|
|
285
204
|
]
|
|
286
205
|
}
|
|
287
206
|
},
|
|
288
207
|
"env": {
|
|
289
|
-
"account": "
|
|
208
|
+
"account": "weather_provider",
|
|
290
209
|
"network": "testnet"
|
|
291
210
|
}
|
|
292
211
|
}
|
|
293
212
|
```
|
|
294
213
|
|
|
295
|
-
> **Note**: Replace
|
|
214
|
+
> **Note**: Replace `<DAY1_TIMESTAMP>` through `<DAY5_TIMESTAMP>` with the actual values from Step 0.1. The `id` field is the timestamp (as a number) that keys the weather record — it must **exactly match** the timestamp you submit later in Step 7.3.
|
|
296
215
|
|
|
297
216
|
---
|
|
298
217
|
|
|
299
218
|
## Step 1: Create Permission Object
|
|
300
219
|
|
|
301
|
-
Create a Permission object to manage access control for the travel service.
|
|
220
|
+
Create a Permission object to manage access control for the travel service. Add permission indices for the travel_provider account.
|
|
302
221
|
|
|
303
|
-
**Prompt**: Create a Permission object named "
|
|
222
|
+
**Prompt**: Create a Permission object named "travel_permission" for the travel service.
|
|
304
223
|
|
|
305
224
|
```json
|
|
306
225
|
{
|
|
307
226
|
"operation_type": "permission",
|
|
308
227
|
"data": {
|
|
309
228
|
"object": {
|
|
310
|
-
"name": "
|
|
229
|
+
"name": "travel_permission",
|
|
230
|
+
"tags": ["travel", "iceland", "tourism"],
|
|
311
231
|
"replaceExistName": true
|
|
312
232
|
},
|
|
313
233
|
"description": "Permission for Iceland travel service",
|
|
314
234
|
"table": {
|
|
315
235
|
"op": "add perm by entity",
|
|
316
|
-
"entity": {"name_or_address": "
|
|
317
|
-
"index": [1000, 1001, 1002, 1003, 1004, 1005]
|
|
236
|
+
"entity": {"name_or_address": "travel_provider"},
|
|
237
|
+
"index": [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 306]
|
|
318
238
|
}
|
|
319
239
|
},
|
|
320
240
|
"env": {
|
|
321
|
-
"account": "
|
|
241
|
+
"account": "travel_provider",
|
|
322
242
|
"network": "testnet"
|
|
323
243
|
}
|
|
324
244
|
}
|
|
325
245
|
```
|
|
326
246
|
|
|
247
|
+
> **Note**: Permission indices 1000-1009 are used for different workflow forwards. Index 306 is a reserved administrative permission. The travel_provider account is granted all these permissions.
|
|
248
|
+
|
|
327
249
|
---
|
|
328
250
|
|
|
329
251
|
## Step 2: Create Arbitration Object
|
|
330
252
|
|
|
331
253
|
Create an Arbitration object for dispute resolution.
|
|
332
254
|
|
|
333
|
-
**Prompt**: Create an Arbitration named "
|
|
255
|
+
**Prompt**: Create an Arbitration named "travel_arbitration".
|
|
334
256
|
|
|
335
257
|
```json
|
|
336
258
|
{
|
|
337
259
|
"operation_type": "arbitration",
|
|
338
260
|
"data": {
|
|
339
261
|
"object": {
|
|
340
|
-
"name": "
|
|
341
|
-
"permission": "
|
|
262
|
+
"name": "travel_arbitration",
|
|
263
|
+
"permission": "travel_permission",
|
|
342
264
|
"replaceExistName": true
|
|
343
265
|
},
|
|
344
266
|
"description": "Arbitration for Iceland travel service disputes"
|
|
345
267
|
},
|
|
346
268
|
"env": {
|
|
347
|
-
"account": "
|
|
269
|
+
"account": "travel_provider",
|
|
348
270
|
"network": "testnet"
|
|
349
271
|
}
|
|
350
272
|
}
|
|
@@ -354,17 +276,15 @@ Create an Arbitration object for dispute resolution.
|
|
|
354
276
|
|
|
355
277
|
## Step 3: Create Guards
|
|
356
278
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
> **Note**: This guard demonstrates how to query repository data using a timestamp converted to an address. You can test it using `gen_passport` with a timestamp from step 0.1.
|
|
279
|
+
Create all Guards needed for the workflow and fund allocation. Guards are immutable once created, so create them before the Machine and Service.
|
|
360
280
|
|
|
361
|
-
|
|
281
|
+
### 3.1 Weather Check Guard
|
|
362
282
|
|
|
363
|
-
Creates a Guard that verifies weather
|
|
283
|
+
Creates a Guard that verifies weather data exists for a given date. This Guard is bound to the "go_ice_scooting" forward (entering the weather-dependent Ice Scooting activity) to ensure the activity date has a weather record in the repository.
|
|
364
284
|
|
|
365
285
|
**Guard Logic**:
|
|
366
286
|
```
|
|
367
|
-
repository.data("Condition", convert_number_address(activity_date))
|
|
287
|
+
repository.data has("Condition", convert_number_address(activity_date))
|
|
368
288
|
```
|
|
369
289
|
|
|
370
290
|
**Prompt**: Create a Guard named "weather_check_guard" for weather condition verification.
|
|
@@ -374,17 +294,17 @@ repository.data("Condition", convert_number_address(activity_date)) == expected_
|
|
|
374
294
|
"operation_type": "guard",
|
|
375
295
|
"data": {
|
|
376
296
|
"namedNew": {
|
|
377
|
-
"name": "
|
|
297
|
+
"name": "weather_check_guard",
|
|
378
298
|
"tags": ["weather", "check", "travel"],
|
|
379
299
|
"replaceExistName": true
|
|
380
300
|
},
|
|
381
|
-
"description": "Weather check guard for ice scooting activity.
|
|
301
|
+
"description": "Weather check guard for ice scooting activity. Checks if weather data exists for the activity date.",
|
|
382
302
|
"table": [
|
|
383
303
|
{
|
|
384
304
|
"identifier": 0,
|
|
385
305
|
"b_submission": false,
|
|
386
306
|
"value_type": "Address",
|
|
387
|
-
"value": "
|
|
307
|
+
"value": "weather_repo",
|
|
388
308
|
"name": "Weather Repository address"
|
|
389
309
|
},
|
|
390
310
|
{
|
|
@@ -400,65 +320,35 @@ repository.data("Condition", convert_number_address(activity_date)) == expected_
|
|
|
400
320
|
"value_type": "U64",
|
|
401
321
|
"value": 0,
|
|
402
322
|
"name": "Activity date timestamp (submitted at runtime)"
|
|
403
|
-
},
|
|
404
|
-
{
|
|
405
|
-
"identifier": 3,
|
|
406
|
-
"b_submission": false,
|
|
407
|
-
"value_type": "String",
|
|
408
|
-
"value": "sunny",
|
|
409
|
-
"name": "Expected weather condition"
|
|
410
323
|
}
|
|
411
324
|
],
|
|
412
325
|
"root": {
|
|
413
|
-
"type": "
|
|
414
|
-
"
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
"parameters": [
|
|
424
|
-
{
|
|
425
|
-
"type": "identifier",
|
|
426
|
-
"identifier": 1
|
|
427
|
-
},
|
|
428
|
-
{
|
|
429
|
-
"type": "convert_number_address",
|
|
430
|
-
"node": {
|
|
431
|
-
"type": "identifier",
|
|
432
|
-
"identifier": 2
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
]
|
|
436
|
-
},
|
|
437
|
-
{
|
|
438
|
-
"type": "identifier",
|
|
439
|
-
"identifier": 3
|
|
440
|
-
}
|
|
441
|
-
]
|
|
442
|
-
}
|
|
326
|
+
"type": "query",
|
|
327
|
+
"query": "repository.data has",
|
|
328
|
+
"object": {"identifier": 0},
|
|
329
|
+
"parameters": [
|
|
330
|
+
{"type": "identifier", "identifier": 1},
|
|
331
|
+
{
|
|
332
|
+
"type": "convert_number_address",
|
|
333
|
+
"node": {"type": "identifier", "identifier": 2}
|
|
334
|
+
}
|
|
335
|
+
]
|
|
443
336
|
}
|
|
444
337
|
},
|
|
445
338
|
"env": {
|
|
446
|
-
"account": "
|
|
339
|
+
"account": "travel_provider",
|
|
447
340
|
"network": "testnet"
|
|
448
341
|
}
|
|
449
342
|
}
|
|
450
343
|
```
|
|
451
344
|
|
|
452
|
-
> **Note**: We use the repository name "weather_repo_v2" directly (the MCP server resolves names to addresses automatically). If you need the raw address, you can query it using `onchain_objects` with the name.
|
|
453
|
-
|
|
454
345
|
**Guard Table**:
|
|
455
346
|
|
|
456
347
|
| identifier | b_submission | value_type | value | Purpose |
|
|
457
348
|
|------------|-------------|-----------|-------|---------|
|
|
458
|
-
| 0 | false | Address | weather_repo
|
|
349
|
+
| 0 | false | Address | weather_repo | Weather Repository to query |
|
|
459
350
|
| 1 | false | String | "Condition" | Repository policy name |
|
|
460
351
|
| 2 | **true** | U64 | 0 (placeholder) | Activity date timestamp, submitted at runtime |
|
|
461
|
-
| 3 | false | String | "sunny" | Expected weather condition |
|
|
462
352
|
|
|
463
353
|
### 3.2 Travel Complete Guard (Time-Lock)
|
|
464
354
|
|
|
@@ -477,11 +367,11 @@ clock > progress.current_time + 1000
|
|
|
477
367
|
"operation_type": "guard",
|
|
478
368
|
"data": {
|
|
479
369
|
"namedNew": {
|
|
480
|
-
"name": "
|
|
370
|
+
"name": "travel_complete_guard",
|
|
481
371
|
"tags": ["travel", "time-lock", "complete"],
|
|
482
372
|
"replaceExistName": true
|
|
483
373
|
},
|
|
484
|
-
"description": "Time-lock guard for travel order completion. Requires current clock > progress.current_time + 1000ms
|
|
374
|
+
"description": "Time-lock guard for travel order completion. Requires current clock > progress.current_time + 1000ms.",
|
|
485
375
|
"table": [
|
|
486
376
|
{
|
|
487
377
|
"identifier": 0,
|
|
@@ -494,42 +384,31 @@ clock > progress.current_time + 1000
|
|
|
494
384
|
"identifier": 1,
|
|
495
385
|
"b_submission": false,
|
|
496
386
|
"value_type": "U64",
|
|
497
|
-
"value": 1000
|
|
387
|
+
"value": 1000,
|
|
388
|
+
"name": "Time-lock duration in ms"
|
|
498
389
|
}
|
|
499
390
|
],
|
|
500
391
|
"root": {
|
|
501
|
-
"type": "
|
|
502
|
-
"
|
|
503
|
-
"type": "
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
"convert_witness": 100
|
|
518
|
-
},
|
|
519
|
-
"parameters": []
|
|
520
|
-
},
|
|
521
|
-
{
|
|
522
|
-
"type": "identifier",
|
|
523
|
-
"identifier": 1
|
|
524
|
-
}
|
|
525
|
-
]
|
|
526
|
-
}
|
|
527
|
-
]
|
|
528
|
-
}
|
|
392
|
+
"type": "logic_as_u256_greater",
|
|
393
|
+
"nodes": [
|
|
394
|
+
{"type": "context", "context": "Clock"},
|
|
395
|
+
{
|
|
396
|
+
"type": "calc_number_add",
|
|
397
|
+
"nodes": [
|
|
398
|
+
{
|
|
399
|
+
"type": "query",
|
|
400
|
+
"query": "progress.current_time",
|
|
401
|
+
"object": {"identifier": 0, "convert_witness": 100},
|
|
402
|
+
"parameters": []
|
|
403
|
+
},
|
|
404
|
+
{"type": "identifier", "identifier": 1}
|
|
405
|
+
]
|
|
406
|
+
}
|
|
407
|
+
]
|
|
529
408
|
}
|
|
530
409
|
},
|
|
531
410
|
"env": {
|
|
532
|
-
"account": "
|
|
411
|
+
"account": "travel_provider",
|
|
533
412
|
"network": "testnet"
|
|
534
413
|
}
|
|
535
414
|
}
|
|
@@ -555,11 +434,11 @@ Creates a Guard that allows order cancellation. This Guard always passes (return
|
|
|
555
434
|
"operation_type": "guard",
|
|
556
435
|
"data": {
|
|
557
436
|
"namedNew": {
|
|
558
|
-
"name": "
|
|
437
|
+
"name": "travel_cancel_guard",
|
|
559
438
|
"tags": ["travel", "cancel"],
|
|
560
439
|
"replaceExistName": true
|
|
561
440
|
},
|
|
562
|
-
"description": "Cancel guard for travel orders. Always passes - cancellation is controlled by permission-based access
|
|
441
|
+
"description": "Cancel guard for travel orders. Always passes - cancellation is controlled by permission-based access.",
|
|
563
442
|
"table": [
|
|
564
443
|
{
|
|
565
444
|
"identifier": 0,
|
|
@@ -570,15 +449,192 @@ Creates a Guard that allows order cancellation. This Guard always passes (return
|
|
|
570
449
|
}
|
|
571
450
|
],
|
|
572
451
|
"root": {
|
|
573
|
-
"type": "
|
|
574
|
-
"
|
|
575
|
-
|
|
576
|
-
|
|
452
|
+
"type": "identifier",
|
|
453
|
+
"identifier": 0
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
"env": {
|
|
457
|
+
"account": "travel_provider",
|
|
458
|
+
"network": "testnet"
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
### 3.4 Merchant Victory Guard (100% to Service)
|
|
464
|
+
|
|
465
|
+
Checks if order progress current node is "Complete". If passed, merchant receives 100% of funds.
|
|
466
|
+
|
|
467
|
+
**Prompt**: Create Guard "merchant_victory_guard".
|
|
468
|
+
|
|
469
|
+
```json
|
|
470
|
+
{
|
|
471
|
+
"operation_type": "guard",
|
|
472
|
+
"data": {
|
|
473
|
+
"namedNew": {
|
|
474
|
+
"name": "merchant_victory_guard",
|
|
475
|
+
"tags": ["merchant", "victory", "complete"],
|
|
476
|
+
"replaceExistName": true
|
|
477
|
+
},
|
|
478
|
+
"description": "Guard for merchant victory: checks if order progress current node is Complete. If passed, merchant receives 100% of funds.",
|
|
479
|
+
"table": [
|
|
480
|
+
{
|
|
481
|
+
"identifier": 0,
|
|
482
|
+
"b_submission": true,
|
|
483
|
+
"value_type": "Address",
|
|
484
|
+
"value": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
485
|
+
"name": "Order ID (submitted at runtime)"
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
"identifier": 1,
|
|
489
|
+
"b_submission": false,
|
|
490
|
+
"value_type": "String",
|
|
491
|
+
"value": "Complete",
|
|
492
|
+
"name": "Complete node name"
|
|
493
|
+
}
|
|
494
|
+
],
|
|
495
|
+
"root": {
|
|
496
|
+
"type": "logic_equal",
|
|
497
|
+
"nodes": [
|
|
498
|
+
{
|
|
499
|
+
"type": "query",
|
|
500
|
+
"query": "progress.current",
|
|
501
|
+
"object": {"identifier": 0, "convert_witness": 100},
|
|
502
|
+
"parameters": []
|
|
503
|
+
},
|
|
504
|
+
{"type": "identifier", "identifier": 1}
|
|
505
|
+
]
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
"env": {
|
|
509
|
+
"account": "travel_provider",
|
|
510
|
+
"network": "testnet"
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### 3.5 No Ice Scooting Guard (80% Service, 20% Refund)
|
|
516
|
+
|
|
517
|
+
Checks if progress current is "Cancel" or "Ice Scooting". If passed, merchant gets 80%, user gets 20% refund.
|
|
518
|
+
|
|
519
|
+
**Prompt**: Create Guard "no_ice_scooting_guard".
|
|
520
|
+
|
|
521
|
+
```json
|
|
522
|
+
{
|
|
523
|
+
"operation_type": "guard",
|
|
524
|
+
"data": {
|
|
525
|
+
"namedNew": {
|
|
526
|
+
"name": "no_ice_scooting_guard",
|
|
527
|
+
"tags": ["user", "cancel", "ice_scooting"],
|
|
528
|
+
"replaceExistName": true
|
|
529
|
+
},
|
|
530
|
+
"description": "Guard for user not participating in ice scooting: checks if progress current is Cancel or Ice Scooting. If passed, merchant gets 80%, user gets 20% refund.",
|
|
531
|
+
"table": [
|
|
532
|
+
{
|
|
533
|
+
"identifier": 0,
|
|
534
|
+
"b_submission": true,
|
|
535
|
+
"value_type": "Address",
|
|
536
|
+
"value": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
537
|
+
"name": "Order ID (submitted at runtime)"
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
"identifier": 1,
|
|
541
|
+
"b_submission": false,
|
|
542
|
+
"value_type": "String",
|
|
543
|
+
"value": "Cancel",
|
|
544
|
+
"name": "Cancel node name"
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
"identifier": 2,
|
|
548
|
+
"b_submission": false,
|
|
549
|
+
"value_type": "String",
|
|
550
|
+
"value": "Ice Scooting",
|
|
551
|
+
"name": "Ice Scooting node name"
|
|
552
|
+
}
|
|
553
|
+
],
|
|
554
|
+
"root": {
|
|
555
|
+
"type": "logic_or",
|
|
556
|
+
"nodes": [
|
|
557
|
+
{
|
|
558
|
+
"type": "logic_equal",
|
|
559
|
+
"nodes": [
|
|
560
|
+
{
|
|
561
|
+
"type": "query",
|
|
562
|
+
"query": "progress.current",
|
|
563
|
+
"object": {"identifier": 0, "convert_witness": 100},
|
|
564
|
+
"parameters": []
|
|
565
|
+
},
|
|
566
|
+
{"type": "identifier", "identifier": 1}
|
|
567
|
+
]
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
"type": "logic_equal",
|
|
571
|
+
"nodes": [
|
|
572
|
+
{
|
|
573
|
+
"type": "query",
|
|
574
|
+
"query": "progress.current",
|
|
575
|
+
"object": {"identifier": 0, "convert_witness": 100},
|
|
576
|
+
"parameters": []
|
|
577
|
+
},
|
|
578
|
+
{"type": "identifier", "identifier": 2}
|
|
579
|
+
]
|
|
580
|
+
}
|
|
581
|
+
]
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
"env": {
|
|
585
|
+
"account": "travel_provider",
|
|
586
|
+
"network": "testnet"
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### 3.6 No SPA Guard (5% Service, 95% Refund)
|
|
592
|
+
|
|
593
|
+
Checks if progress current is "SPA". If passed, merchant gets 5%, user gets 95% refund.
|
|
594
|
+
|
|
595
|
+
**Prompt**: Create Guard "no_spa_guard".
|
|
596
|
+
|
|
597
|
+
```json
|
|
598
|
+
{
|
|
599
|
+
"operation_type": "guard",
|
|
600
|
+
"data": {
|
|
601
|
+
"namedNew": {
|
|
602
|
+
"name": "no_spa_guard",
|
|
603
|
+
"tags": ["user", "cancel", "spa"],
|
|
604
|
+
"replaceExistName": true
|
|
605
|
+
},
|
|
606
|
+
"description": "Guard for user not participating in SPA: checks if progress current is SPA. If passed, merchant gets 5%, user gets 95% refund.",
|
|
607
|
+
"table": [
|
|
608
|
+
{
|
|
609
|
+
"identifier": 0,
|
|
610
|
+
"b_submission": true,
|
|
611
|
+
"value_type": "Address",
|
|
612
|
+
"value": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
613
|
+
"name": "Order ID (submitted at runtime)"
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
"identifier": 1,
|
|
617
|
+
"b_submission": false,
|
|
618
|
+
"value_type": "String",
|
|
619
|
+
"value": "SPA",
|
|
620
|
+
"name": "SPA node name"
|
|
577
621
|
}
|
|
622
|
+
],
|
|
623
|
+
"root": {
|
|
624
|
+
"type": "logic_equal",
|
|
625
|
+
"nodes": [
|
|
626
|
+
{
|
|
627
|
+
"type": "query",
|
|
628
|
+
"query": "progress.current",
|
|
629
|
+
"object": {"identifier": 0, "convert_witness": 100},
|
|
630
|
+
"parameters": []
|
|
631
|
+
},
|
|
632
|
+
{"type": "identifier", "identifier": 1}
|
|
633
|
+
]
|
|
578
634
|
}
|
|
579
635
|
},
|
|
580
636
|
"env": {
|
|
581
|
-
"account": "
|
|
637
|
+
"account": "travel_provider",
|
|
582
638
|
"network": "testnet"
|
|
583
639
|
}
|
|
584
640
|
}
|
|
@@ -586,52 +642,39 @@ Creates a Guard that allows order cancellation. This Guard always passes (return
|
|
|
586
642
|
|
|
587
643
|
---
|
|
588
644
|
|
|
589
|
-
## Step 4: Create
|
|
645
|
+
## Step 4: Create and Publish Machine
|
|
646
|
+
|
|
647
|
+
Create a Machine to define the travel service workflow with all nodes and forwards. The Machine must be published before it can be bound to a Service.
|
|
590
648
|
|
|
591
|
-
|
|
649
|
+
> **Important**: In the Machine node structure, each node's `pairs` define how to ENTER that node. The `prev_node` specifies the source node, and `forwards` are the operation names used to advance from `prev_node` to this node.
|
|
592
650
|
|
|
593
|
-
**Prompt**: Create a Machine named "travel_machine" with the complete travel workflow.
|
|
651
|
+
**Prompt**: Create and publish a Machine named "travel_machine" with the complete travel workflow.
|
|
594
652
|
|
|
595
653
|
```json
|
|
596
654
|
{
|
|
597
655
|
"operation_type": "machine",
|
|
598
656
|
"data": {
|
|
599
657
|
"object": {
|
|
600
|
-
"name": "
|
|
601
|
-
"permission": "
|
|
658
|
+
"name": "travel_machine",
|
|
659
|
+
"permission": "travel_permission",
|
|
602
660
|
"replaceExistName": true
|
|
603
661
|
},
|
|
604
|
-
"description": "Iceland travel service workflow:
|
|
662
|
+
"description": "Iceland travel service workflow: (init) -> Buy Insurance -> SPA -> Ice Scooting -> Complete/Cancel",
|
|
605
663
|
"node": {
|
|
606
664
|
"op": "add",
|
|
665
|
+
"bReplace": true,
|
|
607
666
|
"nodes": [
|
|
608
|
-
{
|
|
609
|
-
"name": "Start",
|
|
610
|
-
"pairs": [
|
|
611
|
-
{
|
|
612
|
-
"prev_node": "",
|
|
613
|
-
"threshold": 0,
|
|
614
|
-
"forwards": [
|
|
615
|
-
{
|
|
616
|
-
"name": "buy_insurance",
|
|
617
|
-
"permissionIndex": 1000,
|
|
618
|
-
"weight": 1
|
|
619
|
-
}
|
|
620
|
-
]
|
|
621
|
-
}
|
|
622
|
-
]
|
|
623
|
-
},
|
|
624
667
|
{
|
|
625
668
|
"name": "Buy Insurance",
|
|
626
669
|
"pairs": [
|
|
627
670
|
{
|
|
628
|
-
"prev_node": "
|
|
671
|
+
"prev_node": "",
|
|
629
672
|
"threshold": 1,
|
|
630
673
|
"forwards": [
|
|
631
674
|
{
|
|
632
|
-
"name": "
|
|
633
|
-
"
|
|
634
|
-
"
|
|
675
|
+
"name": "buy_insurance",
|
|
676
|
+
"weight": 1,
|
|
677
|
+
"permissionIndex": 1000
|
|
635
678
|
}
|
|
636
679
|
]
|
|
637
680
|
}
|
|
@@ -645,9 +688,9 @@ Create a Machine to define the travel service workflow with all nodes and forwar
|
|
|
645
688
|
"threshold": 1,
|
|
646
689
|
"forwards": [
|
|
647
690
|
{
|
|
648
|
-
"name": "
|
|
649
|
-
"
|
|
650
|
-
"
|
|
691
|
+
"name": "go_spa",
|
|
692
|
+
"weight": 1,
|
|
693
|
+
"permissionIndex": 1001
|
|
651
694
|
}
|
|
652
695
|
]
|
|
653
696
|
}
|
|
@@ -661,19 +704,12 @@ Create a Machine to define the travel service workflow with all nodes and forwar
|
|
|
661
704
|
"threshold": 1,
|
|
662
705
|
"forwards": [
|
|
663
706
|
{
|
|
664
|
-
"name": "
|
|
665
|
-
"permissionIndex": 1003,
|
|
707
|
+
"name": "go_ice_scooting",
|
|
666
708
|
"weight": 1,
|
|
667
|
-
"guard": {
|
|
668
|
-
"guard": "travel_complete_guard_v1"
|
|
669
|
-
}
|
|
670
|
-
},
|
|
671
|
-
{
|
|
672
|
-
"name": "cancel_trip",
|
|
673
709
|
"permissionIndex": 1004,
|
|
674
|
-
"weight": 1,
|
|
675
710
|
"guard": {
|
|
676
|
-
"guard": "
|
|
711
|
+
"guard": "weather_check_guard",
|
|
712
|
+
"retained_submission": []
|
|
677
713
|
}
|
|
678
714
|
}
|
|
679
715
|
]
|
|
@@ -686,7 +722,17 @@ Create a Machine to define the travel service workflow with all nodes and forwar
|
|
|
686
722
|
{
|
|
687
723
|
"prev_node": "Ice Scooting",
|
|
688
724
|
"threshold": 1,
|
|
689
|
-
"forwards": [
|
|
725
|
+
"forwards": [
|
|
726
|
+
{
|
|
727
|
+
"name": "complete_trip",
|
|
728
|
+
"weight": 1,
|
|
729
|
+
"permissionIndex": 1002,
|
|
730
|
+
"guard": {
|
|
731
|
+
"guard": "travel_complete_guard",
|
|
732
|
+
"retained_submission": []
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
]
|
|
690
736
|
}
|
|
691
737
|
]
|
|
692
738
|
},
|
|
@@ -696,7 +742,17 @@ Create a Machine to define the travel service workflow with all nodes and forwar
|
|
|
696
742
|
{
|
|
697
743
|
"prev_node": "Ice Scooting",
|
|
698
744
|
"threshold": 1,
|
|
699
|
-
"forwards": [
|
|
745
|
+
"forwards": [
|
|
746
|
+
{
|
|
747
|
+
"name": "cancel_trip",
|
|
748
|
+
"weight": 1,
|
|
749
|
+
"permissionIndex": 1003,
|
|
750
|
+
"guard": {
|
|
751
|
+
"guard": "travel_cancel_guard",
|
|
752
|
+
"retained_submission": []
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
]
|
|
700
756
|
}
|
|
701
757
|
]
|
|
702
758
|
}
|
|
@@ -705,136 +761,172 @@ Create a Machine to define the travel service workflow with all nodes and forwar
|
|
|
705
761
|
"publish": true
|
|
706
762
|
},
|
|
707
763
|
"env": {
|
|
708
|
-
"account": "
|
|
764
|
+
"account": "travel_provider",
|
|
709
765
|
"network": "testnet"
|
|
710
766
|
}
|
|
711
767
|
}
|
|
712
768
|
```
|
|
713
769
|
|
|
714
|
-
**Workflow
|
|
770
|
+
**Machine Workflow Diagram**:
|
|
771
|
+
|
|
772
|
+
```
|
|
773
|
+
("") --buy_insurance--> [Buy Insurance] --go_spa--> [SPA] --go_ice_scooting--[weather_check]--> [Ice Scooting]
|
|
774
|
+
|
|
|
775
|
+
+--complete_trip--[time-lock]--> [Complete]
|
|
776
|
+
+--cancel_trip-------------> [Cancel]
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
**Node Pair Structure**:
|
|
780
|
+
|
|
781
|
+
| Node | prev_node | Forward | Permission Index | Guard |
|
|
782
|
+
|------|-----------|---------|-----------------|-------|
|
|
783
|
+
| Buy Insurance | "" (entry) | buy_insurance | 1000 | - |
|
|
784
|
+
| SPA | Buy Insurance | go_spa | 1001 | - |
|
|
785
|
+
| Ice Scooting | SPA | go_ice_scooting | 1004 | weather_check_guard |
|
|
786
|
+
| Complete | Ice Scooting | complete_trip | 1002 | travel_complete_guard |
|
|
787
|
+
| Cancel | Ice Scooting | cancel_trip | 1003 | travel_cancel_guard |
|
|
715
788
|
|
|
716
|
-
|
|
717
|
-
|------|---------|-------|-----------------|-------------|
|
|
718
|
-
| Start | buy_insurance -> Buy Insurance | - | 1000 | Enter Start node from initial state |
|
|
719
|
-
| Buy Insurance | go_ice_scooting -> SPA | - | 1001 | Proceed to SPA |
|
|
720
|
-
| SPA | go_ice_scooting -> Ice Scooting | - | 1002 | Proceed to ice scooting |
|
|
721
|
-
| Ice Scooting | complete_trip -> Complete | travel_complete_guard_v1 | 1003 | Complete trip (time-lock) |
|
|
722
|
-
| Ice Scooting | cancel_trip -> Cancel | travel_cancel_guard_v1 | 1004 | Cancel trip |
|
|
723
|
-
| Complete | (endpoint) | - | - | Final state |
|
|
724
|
-
| Cancel | (endpoint) | - | - | Cancelled state |
|
|
789
|
+
> **Note**: The `prev_node` field uses an empty string `""` to denote the entry point. Each node defines how to enter it from a previous node. The `threshold` field (required) specifies the minimum forward weight needed to trigger node advancement.
|
|
725
790
|
|
|
726
791
|
---
|
|
727
792
|
|
|
728
793
|
## Step 5: Create and Publish Service
|
|
729
794
|
|
|
730
|
-
Create the travel service with
|
|
795
|
+
Create the travel service with all configurations and publish it in one operation. The Service requires:
|
|
796
|
+
- Machine binding (must be published first)
|
|
797
|
+
- Sales items (product listing)
|
|
798
|
+
- Arbitration binding
|
|
799
|
+
- Order allocators (fund distribution rules)
|
|
800
|
+
- `pause: false` and `publish: true` to make it active
|
|
731
801
|
|
|
732
|
-
**Prompt**: Create and publish "travel_service"
|
|
802
|
+
**Prompt**: Create and publish a Service named "travel_service".
|
|
733
803
|
|
|
734
804
|
```json
|
|
735
805
|
{
|
|
736
806
|
"operation_type": "service",
|
|
737
807
|
"data": {
|
|
738
808
|
"object": {
|
|
739
|
-
"name": "
|
|
740
|
-
"
|
|
741
|
-
"permission": "travel_permission_v1",
|
|
809
|
+
"name": "travel_service",
|
|
810
|
+
"permission": "travel_permission",
|
|
742
811
|
"replaceExistName": true
|
|
743
812
|
},
|
|
744
|
-
"description": "Iceland travel service: Blue Lagoon SPA + Glacier Ice Scooting.
|
|
745
|
-
"machine": "
|
|
746
|
-
"arbitrations": {
|
|
747
|
-
"op": "add",
|
|
748
|
-
"objects": ["travel_arbitration_v1"]
|
|
749
|
-
},
|
|
813
|
+
"description": "Iceland travel service: Blue Lagoon SPA + Glacier Ice Scooting.",
|
|
814
|
+
"machine": "travel_machine",
|
|
750
815
|
"sales": {
|
|
751
816
|
"op": "add",
|
|
752
817
|
"sales": [
|
|
753
818
|
{
|
|
754
819
|
"name": "Iceland Travel Package",
|
|
755
|
-
"price": 500000000,
|
|
756
|
-
"stock":
|
|
820
|
+
"price": "500000000",
|
|
821
|
+
"stock": "99",
|
|
757
822
|
"suspension": false,
|
|
758
823
|
"wip": "",
|
|
759
824
|
"wip_hash": ""
|
|
760
825
|
}
|
|
761
826
|
]
|
|
762
827
|
},
|
|
828
|
+
"arbitrations": {
|
|
829
|
+
"op": "add",
|
|
830
|
+
"objects": ["travel_arbitration"]
|
|
831
|
+
},
|
|
763
832
|
"order_allocators": {
|
|
764
|
-
"description": "Travel order revenue allocation",
|
|
833
|
+
"description": "Travel order revenue allocation based on progress state",
|
|
765
834
|
"threshold": 0,
|
|
766
835
|
"allocators": [
|
|
767
836
|
{
|
|
768
|
-
"guard": "
|
|
837
|
+
"guard": "merchant_victory_guard",
|
|
838
|
+
"fix": "0",
|
|
769
839
|
"sharing": [
|
|
770
840
|
{
|
|
771
|
-
"who": {"
|
|
841
|
+
"who": {"Entity": {"name_or_address": "travel_service"}},
|
|
772
842
|
"sharing": 10000,
|
|
773
843
|
"mode": "Rate"
|
|
774
844
|
}
|
|
775
845
|
]
|
|
846
|
+
},
|
|
847
|
+
{
|
|
848
|
+
"guard": "no_ice_scooting_guard",
|
|
849
|
+
"fix": "0",
|
|
850
|
+
"sharing": [
|
|
851
|
+
{
|
|
852
|
+
"who": {"Entity": {"name_or_address": "travel_service"}},
|
|
853
|
+
"sharing": 8000,
|
|
854
|
+
"mode": "Rate"
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
"who": {"GuardIdentifier": 0},
|
|
858
|
+
"sharing": 2000,
|
|
859
|
+
"mode": "Rate"
|
|
860
|
+
}
|
|
861
|
+
]
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
"guard": "no_spa_guard",
|
|
865
|
+
"fix": "0",
|
|
866
|
+
"sharing": [
|
|
867
|
+
{
|
|
868
|
+
"who": {"Entity": {"name_or_address": "travel_service"}},
|
|
869
|
+
"sharing": 500,
|
|
870
|
+
"mode": "Rate"
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
"who": {"GuardIdentifier": 0},
|
|
874
|
+
"sharing": 9500,
|
|
875
|
+
"mode": "Rate"
|
|
876
|
+
}
|
|
877
|
+
]
|
|
776
878
|
}
|
|
777
879
|
]
|
|
778
880
|
},
|
|
881
|
+
"pause": false,
|
|
779
882
|
"publish": true
|
|
780
883
|
},
|
|
781
884
|
"env": {
|
|
782
|
-
"account": "
|
|
885
|
+
"account": "travel_provider",
|
|
783
886
|
"network": "testnet"
|
|
784
887
|
}
|
|
785
888
|
}
|
|
786
889
|
```
|
|
787
890
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
---
|
|
791
|
-
|
|
792
|
-
## Step 6: Unpause Service
|
|
793
|
-
|
|
794
|
-
Unpause the service to allow order creation.
|
|
891
|
+
**order_allocators Field Reference**:
|
|
795
892
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
"network": "testnet"
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
```
|
|
893
|
+
| Field | Type | Description |
|
|
894
|
+
|-------|------|-------------|
|
|
895
|
+
| `description` | string | Description of the allocation strategy |
|
|
896
|
+
| `threshold` | number | Minimum threshold for allocation (0 = no minimum) |
|
|
897
|
+
| `allocators` | array | Array of allocator configurations |
|
|
898
|
+
| `allocators[].guard` | string | Guard name/ID that must pass for this allocation |
|
|
899
|
+
| `allocators[].fix` | string | Fixed amount per recipient ("0" = none) |
|
|
900
|
+
| `allocators[].sharing` | array | Array of sharing rules |
|
|
901
|
+
| `allocators[].sharing[].who` | object | Recipient: `{"Entity": {...}}` for fixed address, `{"GuardIdentifier": N}` for Guard table index, `{"Signer": "signer"}` for caller |
|
|
902
|
+
| `allocators[].sharing[].sharing` | number | Amount in basis points (10000 = 100%) |
|
|
903
|
+
| `allocators[].sharing[].mode` | string | Allocation mode ("Rate" or "Amount") |
|
|
811
904
|
|
|
812
|
-
|
|
905
|
+
**Recipient Types**:
|
|
813
906
|
|
|
814
|
-
|
|
907
|
+
| Type | Syntax | Resolves To | Use Case |
|
|
908
|
+
|------|--------|-------------|----------|
|
|
909
|
+
| `Entity` | `{"Entity": {"name_or_address": "travel_service"}}` | The named address | Merchant receipts (Service object) |
|
|
910
|
+
| `GuardIdentifier` | `{"GuardIdentifier": 0}` | Address from Guard table index 0 (submitted at runtime) | Customer refunds (Order ID submitted to Guard) |
|
|
911
|
+
| `Signer` | `{"Signer": "signer"}` | The caller of `alloc_by_guard` | When caller should receive all funds |
|
|
815
912
|
|
|
816
|
-
|
|
913
|
+
> **Important**: All allocation Guards (merchant_victory_guard, no_ice_scooting_guard, no_spa_guard) have `identifier: 0` as a submission field accepting the Order ID at runtime. `{"GuardIdentifier": 0}` resolves to this submitted Order ID, so funds are sent to the Order object — the customer (Order builder) can then withdraw.
|
|
817
914
|
|
|
818
|
-
**
|
|
915
|
+
**Allocation Logic**:
|
|
819
916
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
"network": "testnet"
|
|
826
|
-
}
|
|
827
|
-
```
|
|
917
|
+
| Guard | Condition | Service Receives | Customer Receives |
|
|
918
|
+
|-------|-----------|-----------------|-------------------|
|
|
919
|
+
| merchant_victory_guard | Progress is "Complete" | 100% (Entity → travel_service) | 0% |
|
|
920
|
+
| no_ice_scooting_guard | Progress is "Cancel" or "Ice Scooting" | 80% (Entity → travel_service) | 20% (GuardIdentifier: 0 → Order) |
|
|
921
|
+
| no_spa_guard | Progress is "SPA" | 5% (Entity → travel_service) | 95% (GuardIdentifier: 0 → Order) |
|
|
828
922
|
|
|
829
923
|
---
|
|
830
924
|
|
|
831
|
-
## Step
|
|
925
|
+
## Step 6: Place Order (Customer Purchase)
|
|
832
926
|
|
|
833
|
-
|
|
927
|
+
The customer (Alice) purchases the travel package. This creates an Order, a Progress (initialized at the entry node ""), and an Allocation object automatically.
|
|
834
928
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
**Prompt**: Create an order on "travel_service" using account "alice".
|
|
929
|
+
**Prompt**: Alice purchases the Iceland Travel Package from the service.
|
|
838
930
|
|
|
839
931
|
```json
|
|
840
932
|
{
|
|
@@ -846,312 +938,508 @@ Create an order on the travel service as the test customer.
|
|
|
846
938
|
"items": [
|
|
847
939
|
{
|
|
848
940
|
"name": "Iceland Travel Package",
|
|
849
|
-
"stock": 1,
|
|
941
|
+
"stock": "1",
|
|
850
942
|
"wip_hash": ""
|
|
851
943
|
}
|
|
852
944
|
],
|
|
853
|
-
"total_pay": {
|
|
854
|
-
"balance": 500000000
|
|
855
|
-
}
|
|
945
|
+
"total_pay": {"balance": "500000000"}
|
|
856
946
|
},
|
|
857
947
|
"namedNewOrder": {
|
|
858
|
-
"name": "
|
|
948
|
+
"name": "alice_travel_order",
|
|
949
|
+
"replaceExistName": true
|
|
950
|
+
},
|
|
951
|
+
"namedNewProgress": {
|
|
952
|
+
"name": "alice_travel_progress",
|
|
953
|
+
"replaceExistName": true
|
|
954
|
+
},
|
|
955
|
+
"namedNewAllocation": {
|
|
956
|
+
"name": "alice_travel_allocation",
|
|
859
957
|
"replaceExistName": true
|
|
860
958
|
}
|
|
861
959
|
}
|
|
862
960
|
},
|
|
863
961
|
"env": {
|
|
864
|
-
"account": "
|
|
962
|
+
"account": "alice",
|
|
865
963
|
"network": "testnet"
|
|
866
964
|
}
|
|
867
965
|
}
|
|
868
966
|
```
|
|
869
967
|
|
|
870
|
-
> **Note**:
|
|
968
|
+
> **Note**: The `total_pay.balance` of `500000000` equals 0.5 WOW (1 WOW = 10^9 base units). The customer account must have sufficient balance. The operation creates three named objects: Order, Progress, and Allocation.
|
|
969
|
+
|
|
970
|
+
---
|
|
971
|
+
|
|
972
|
+
## Step 7: Progress Operations (Order Workflow)
|
|
871
973
|
|
|
872
|
-
|
|
974
|
+
The service provider advances the Progress through the workflow nodes. Each operation moves the Progress from the current node to the next node via a specified forward.
|
|
873
975
|
|
|
874
|
-
|
|
976
|
+
> **Important**: Add `"no_cache": true` to the `env` field for all Progress operations to avoid stale cache issues.
|
|
875
977
|
|
|
876
|
-
|
|
978
|
+
### 7.1 Progress to Buy Insurance
|
|
979
|
+
|
|
980
|
+
Move from initial node ("") to "Buy Insurance" node.
|
|
981
|
+
|
|
982
|
+
**Prompt**: Operate progress to move to Buy Insurance node.
|
|
877
983
|
|
|
878
984
|
```json
|
|
879
985
|
{
|
|
880
986
|
"operation_type": "progress",
|
|
881
987
|
"data": {
|
|
882
|
-
"object": "
|
|
988
|
+
"object": "alice_travel_progress",
|
|
883
989
|
"operate": {
|
|
884
990
|
"operation": {
|
|
885
|
-
"next_node_name": "
|
|
991
|
+
"next_node_name": "Buy Insurance",
|
|
886
992
|
"forward": "buy_insurance"
|
|
887
993
|
}
|
|
888
994
|
}
|
|
889
995
|
},
|
|
890
996
|
"env": {
|
|
891
|
-
"account": "
|
|
892
|
-
"network": "testnet"
|
|
997
|
+
"account": "travel_provider",
|
|
998
|
+
"network": "testnet",
|
|
999
|
+
"no_cache": true
|
|
893
1000
|
}
|
|
894
1001
|
}
|
|
895
1002
|
```
|
|
896
1003
|
|
|
897
|
-
###
|
|
1004
|
+
### 7.2 Progress to SPA
|
|
1005
|
+
|
|
1006
|
+
Move from "Buy Insurance" to "SPA" node.
|
|
898
1007
|
|
|
899
|
-
**Prompt**:
|
|
1008
|
+
**Prompt**: Operate progress to move to SPA node.
|
|
900
1009
|
|
|
901
1010
|
```json
|
|
902
1011
|
{
|
|
903
1012
|
"operation_type": "progress",
|
|
904
1013
|
"data": {
|
|
905
|
-
"object": "
|
|
1014
|
+
"object": "alice_travel_progress",
|
|
906
1015
|
"operate": {
|
|
907
1016
|
"operation": {
|
|
908
|
-
"next_node_name": "
|
|
909
|
-
"forward": "
|
|
1017
|
+
"next_node_name": "SPA",
|
|
1018
|
+
"forward": "go_spa"
|
|
910
1019
|
}
|
|
911
1020
|
}
|
|
912
1021
|
},
|
|
913
1022
|
"env": {
|
|
914
|
-
"account": "
|
|
915
|
-
"network": "testnet"
|
|
1023
|
+
"account": "travel_provider",
|
|
1024
|
+
"network": "testnet",
|
|
1025
|
+
"no_cache": true
|
|
916
1026
|
}
|
|
917
1027
|
}
|
|
918
1028
|
```
|
|
919
1029
|
|
|
920
|
-
###
|
|
1030
|
+
### 7.3 Progress to Ice Scooting (with Weather Guard Submission)
|
|
921
1031
|
|
|
922
|
-
|
|
1032
|
+
Move from "SPA" to "Ice Scooting" node. This forward has a Guard (`weather_check_guard`) that verifies weather data exists in the repository for the activity date. You must submit the activity date timestamp (one of the sunny-day timestamps from Step 0.1) at runtime.
|
|
1033
|
+
|
|
1034
|
+
**Prompt**: Operate progress to move to Ice Scooting node with weather Guard submission.
|
|
923
1035
|
|
|
924
1036
|
```json
|
|
925
1037
|
{
|
|
926
1038
|
"operation_type": "progress",
|
|
927
1039
|
"data": {
|
|
928
|
-
"object": "
|
|
1040
|
+
"object": "alice_travel_progress",
|
|
929
1041
|
"operate": {
|
|
930
1042
|
"operation": {
|
|
931
|
-
"next_node_name": "
|
|
1043
|
+
"next_node_name": "Ice Scooting",
|
|
932
1044
|
"forward": "go_ice_scooting"
|
|
933
1045
|
}
|
|
934
1046
|
}
|
|
935
1047
|
},
|
|
1048
|
+
"submission": {
|
|
1049
|
+
"type": "submission",
|
|
1050
|
+
"guard": [
|
|
1051
|
+
{
|
|
1052
|
+
"object": "weather_check_guard",
|
|
1053
|
+
"impack": true
|
|
1054
|
+
}
|
|
1055
|
+
],
|
|
1056
|
+
"submission": [
|
|
1057
|
+
{
|
|
1058
|
+
"guard": "weather_check_guard",
|
|
1059
|
+
"submission": [
|
|
1060
|
+
{
|
|
1061
|
+
"identifier": 2,
|
|
1062
|
+
"b_submission": true,
|
|
1063
|
+
"value_type": "U64",
|
|
1064
|
+
"value": <ACTIVITY_DATE_TIMESTAMP>,
|
|
1065
|
+
"name": "Activity date timestamp"
|
|
1066
|
+
}
|
|
1067
|
+
]
|
|
1068
|
+
}
|
|
1069
|
+
]
|
|
1070
|
+
},
|
|
936
1071
|
"env": {
|
|
937
|
-
"account": "
|
|
938
|
-
"network": "testnet"
|
|
1072
|
+
"account": "travel_provider",
|
|
1073
|
+
"network": "testnet",
|
|
1074
|
+
"no_cache": true
|
|
939
1075
|
}
|
|
940
1076
|
}
|
|
941
1077
|
```
|
|
942
1078
|
|
|
943
|
-
|
|
1079
|
+
> **Note**: Replace `<ACTIVITY_DATE_TIMESTAMP>` with one of the sunny-day timestamps from Step 0.1 (e.g., `1783555200000` for Day 1). The timestamp **must exactly match** the `id` used when adding weather data to the repository in Step 0.4 — Repository data is keyed by timestamp, and the Guard queries `repository.data has("Condition", convert_number_address(activity_date))`.
|
|
1080
|
+
>
|
|
1081
|
+
> **Key**: Only `identifier: 2` (the activity date) is submitted at runtime. Identifiers 0 (weather_repo) and 1 ("Condition") are fixed in the Guard table and do not need to be submitted. The `submission` field is at the **top level** of the input (alongside `data` and `env`), NOT inside `data`.
|
|
1082
|
+
|
|
1083
|
+
### 7.4 Complete Trip (with Guard Submission)
|
|
944
1084
|
|
|
945
|
-
|
|
1085
|
+
Move from "Ice Scooting" to "Complete" node. This forward has a Guard (`travel_complete_guard`) that requires the Order ID to be submitted for time-lock verification.
|
|
1086
|
+
|
|
1087
|
+
**Prompt**: Operate progress to complete the trip with Guard submission.
|
|
946
1088
|
|
|
947
1089
|
```json
|
|
948
1090
|
{
|
|
949
1091
|
"operation_type": "progress",
|
|
950
1092
|
"data": {
|
|
951
|
-
"object": "
|
|
1093
|
+
"object": "alice_travel_progress",
|
|
952
1094
|
"operate": {
|
|
953
1095
|
"operation": {
|
|
954
|
-
"next_node_name": "
|
|
955
|
-
"forward": "
|
|
1096
|
+
"next_node_name": "Complete",
|
|
1097
|
+
"forward": "complete_trip"
|
|
956
1098
|
}
|
|
957
1099
|
}
|
|
958
1100
|
},
|
|
1101
|
+
"submission": {
|
|
1102
|
+
"type": "submission",
|
|
1103
|
+
"guard": [
|
|
1104
|
+
{
|
|
1105
|
+
"object": "travel_complete_guard",
|
|
1106
|
+
"impack": true
|
|
1107
|
+
}
|
|
1108
|
+
],
|
|
1109
|
+
"submission": [
|
|
1110
|
+
{
|
|
1111
|
+
"guard": "travel_complete_guard",
|
|
1112
|
+
"submission": [
|
|
1113
|
+
{
|
|
1114
|
+
"identifier": 0,
|
|
1115
|
+
"b_submission": true,
|
|
1116
|
+
"value_type": "Address",
|
|
1117
|
+
"value": "<ORDER_OBJECT_ID>",
|
|
1118
|
+
"name": "Order ID"
|
|
1119
|
+
}
|
|
1120
|
+
]
|
|
1121
|
+
}
|
|
1122
|
+
]
|
|
1123
|
+
},
|
|
959
1124
|
"env": {
|
|
960
|
-
"account": "
|
|
961
|
-
"network": "testnet"
|
|
1125
|
+
"account": "travel_provider",
|
|
1126
|
+
"network": "testnet",
|
|
1127
|
+
"no_cache": true
|
|
962
1128
|
}
|
|
963
1129
|
}
|
|
964
1130
|
```
|
|
965
1131
|
|
|
966
|
-
|
|
1132
|
+
> **Note**: Replace `<ORDER_OBJECT_ID>` with the actual Order object ID (e.g., `0x7cc9f2228e130c2f6b585bb9c4666fd4d03252d38048355678f46e31b682eb38`). You can query the Progress object to find the `task` field which contains the Order ID.
|
|
1133
|
+
>
|
|
1134
|
+
> **Key**: The `submission` field is at the **top level** of the input (alongside `data` and `env`), NOT inside `data`. The `impack: true` means the Guard verification result affects the final outcome.
|
|
967
1135
|
|
|
968
|
-
|
|
1136
|
+
### 7.5 Cancel Trip (Alternative Path)
|
|
969
1137
|
|
|
970
|
-
**
|
|
1138
|
+
> **Note**: This is an **alternative** to Step 7.4. Once the trip is Completed, it cannot be Cancelled (and vice versa). Use this path only if cancelling from the "Ice Scooting" node.
|
|
1139
|
+
|
|
1140
|
+
**Prompt**: Operate progress to cancel the trip with Guard submission.
|
|
971
1141
|
|
|
972
1142
|
```json
|
|
973
1143
|
{
|
|
974
1144
|
"operation_type": "progress",
|
|
975
1145
|
"data": {
|
|
976
|
-
"object": "
|
|
1146
|
+
"object": "alice_travel_progress",
|
|
977
1147
|
"operate": {
|
|
978
1148
|
"operation": {
|
|
979
|
-
"next_node_name": "
|
|
980
|
-
"forward": "
|
|
1149
|
+
"next_node_name": "Cancel",
|
|
1150
|
+
"forward": "cancel_trip"
|
|
981
1151
|
}
|
|
982
1152
|
}
|
|
983
1153
|
},
|
|
1154
|
+
"submission": {
|
|
1155
|
+
"type": "submission",
|
|
1156
|
+
"guard": [
|
|
1157
|
+
{
|
|
1158
|
+
"object": "travel_cancel_guard",
|
|
1159
|
+
"impack": true
|
|
1160
|
+
}
|
|
1161
|
+
],
|
|
1162
|
+
"submission": [
|
|
1163
|
+
{
|
|
1164
|
+
"guard": "travel_cancel_guard",
|
|
1165
|
+
"submission": []
|
|
1166
|
+
}
|
|
1167
|
+
]
|
|
1168
|
+
},
|
|
984
1169
|
"env": {
|
|
985
|
-
"account": "
|
|
986
|
-
"network": "testnet"
|
|
1170
|
+
"account": "travel_provider",
|
|
1171
|
+
"network": "testnet",
|
|
1172
|
+
"no_cache": true
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
```
|
|
1176
|
+
|
|
1177
|
+
> **Note**: The `travel_cancel_guard` always passes (returns true), so no submission data is needed. The empty `submission: []` is sufficient.
|
|
1178
|
+
|
|
1179
|
+
**Progress Operation Structure**:
|
|
1180
|
+
|
|
1181
|
+
| Field | Type | Description |
|
|
1182
|
+
|-------|------|-------------|
|
|
1183
|
+
| `data.object` | string | Progress object name/ID |
|
|
1184
|
+
| `data.operate.operation.next_node_name` | string | Target node name to move to |
|
|
1185
|
+
| `data.operate.operation.forward` | string | Forward name defined in Machine |
|
|
1186
|
+
| `submission` | object | Guard verification data (top-level, required when forward has Guard) |
|
|
1187
|
+
| `env.no_cache` | boolean | Set to `true` to avoid stale cache issues |
|
|
1188
|
+
|
|
1189
|
+
---
|
|
1190
|
+
|
|
1191
|
+
## Step 8: Execute Fund Allocation
|
|
1192
|
+
|
|
1193
|
+
After the Progress reaches a terminal state (Complete or Cancel), execute the fund allocation. This operation verifies the allocation Guard and distributes funds according to the `order_allocators` configured in Step 5.
|
|
1194
|
+
|
|
1195
|
+
**Anyone can call this operation** — the caller does not need to be the merchant or the customer. The Guard verification determines which allocator's sharing rules apply, and funds are distributed to the recipients defined in those rules.
|
|
1196
|
+
|
|
1197
|
+
### 8.1 Query the Order ID
|
|
1198
|
+
|
|
1199
|
+
The allocation Guard requires the Order ID as a submission (identifier: 0). Query the Progress object to find the `task` field, which contains the Order ID.
|
|
1200
|
+
|
|
1201
|
+
**Prompt**: Query the Progress object to get the Order ID.
|
|
1202
|
+
|
|
1203
|
+
```json
|
|
1204
|
+
{
|
|
1205
|
+
"operation_type": "progress",
|
|
1206
|
+
"data": {
|
|
1207
|
+
"object": "alice_travel_progress"
|
|
1208
|
+
},
|
|
1209
|
+
"env": {
|
|
1210
|
+
"network": "testnet",
|
|
1211
|
+
"no_cache": true
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
> **Note**: The response includes a `task` field containing the Order object ID. Copy this value for the allocation submission.
|
|
1217
|
+
|
|
1218
|
+
### 8.2 Execute Allocation (Merchant Victory Path)
|
|
1219
|
+
|
|
1220
|
+
When the Progress is "Complete", the `merchant_victory_guard` passes, and 100% of funds go to the Service object.
|
|
1221
|
+
|
|
1222
|
+
**Prompt**: Execute fund allocation with the merchant_victory_guard, submitting the Order ID.
|
|
1223
|
+
|
|
1224
|
+
```json
|
|
1225
|
+
{
|
|
1226
|
+
"operation_type": "allocation",
|
|
1227
|
+
"data": {
|
|
1228
|
+
"object": "alice_travel_allocation",
|
|
1229
|
+
"alloc_by_guard": "merchant_victory_guard"
|
|
987
1230
|
},
|
|
988
1231
|
"submission": {
|
|
989
1232
|
"type": "submission",
|
|
990
1233
|
"guard": [
|
|
991
1234
|
{
|
|
992
|
-
"object": "
|
|
1235
|
+
"object": "merchant_victory_guard",
|
|
993
1236
|
"impack": true
|
|
994
1237
|
}
|
|
995
1238
|
],
|
|
996
1239
|
"submission": [
|
|
997
1240
|
{
|
|
998
|
-
"guard": "
|
|
1241
|
+
"guard": "merchant_victory_guard",
|
|
999
1242
|
"submission": [
|
|
1000
1243
|
{
|
|
1001
1244
|
"identifier": 0,
|
|
1002
1245
|
"b_submission": true,
|
|
1003
1246
|
"value_type": "Address",
|
|
1004
|
-
"value": "<
|
|
1247
|
+
"value": "<ORDER_OBJECT_ID>",
|
|
1248
|
+
"name": "Order ID"
|
|
1005
1249
|
}
|
|
1006
1250
|
]
|
|
1007
1251
|
}
|
|
1008
1252
|
]
|
|
1253
|
+
},
|
|
1254
|
+
"env": {
|
|
1255
|
+
"account": "travel_provider",
|
|
1256
|
+
"network": "testnet",
|
|
1257
|
+
"no_cache": true
|
|
1009
1258
|
}
|
|
1010
1259
|
}
|
|
1011
1260
|
```
|
|
1012
1261
|
|
|
1013
|
-
> **Note**: Replace `<
|
|
1262
|
+
> **Note**: Replace `<ORDER_OBJECT_ID>` with the actual Order object ID from Step 8.1.
|
|
1263
|
+
>
|
|
1264
|
+
> **Two-phase submission**: If the first call returns a submission prompt (without executing), re-call with the `submission` field populated as shown above.
|
|
1014
1265
|
|
|
1015
|
-
|
|
1266
|
+
### 8.3 Execute Allocation (Refund Paths)
|
|
1016
1267
|
|
|
1017
|
-
|
|
1268
|
+
For refund scenarios (Cancel or SPA), use the corresponding Guard. The same submission structure applies — the Order ID is submitted to identifier 0.
|
|
1018
1269
|
|
|
1019
|
-
|
|
1270
|
+
**Cancel/Ice Scooting path** (80% Service, 20% Order):
|
|
1020
1271
|
|
|
1021
|
-
|
|
1272
|
+
```json
|
|
1273
|
+
{
|
|
1274
|
+
"operation_type": "allocation",
|
|
1275
|
+
"data": {
|
|
1276
|
+
"object": "alice_travel_allocation",
|
|
1277
|
+
"alloc_by_guard": "no_ice_scooting_guard"
|
|
1278
|
+
},
|
|
1279
|
+
"submission": {
|
|
1280
|
+
"type": "submission",
|
|
1281
|
+
"guard": [{"object": "no_ice_scooting_guard", "impack": true}],
|
|
1282
|
+
"submission": [{
|
|
1283
|
+
"guard": "no_ice_scooting_guard",
|
|
1284
|
+
"submission": [{"identifier": 0, "b_submission": true, "value_type": "Address", "value": "<ORDER_OBJECT_ID>", "name": "Order ID"}]
|
|
1285
|
+
}]
|
|
1286
|
+
},
|
|
1287
|
+
"env": {"account": "travel_provider", "network": "testnet", "no_cache": true}
|
|
1288
|
+
}
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
**SPA path** (5% Service, 95% Order):
|
|
1022
1292
|
|
|
1023
1293
|
```json
|
|
1024
1294
|
{
|
|
1025
|
-
"operation_type": "
|
|
1295
|
+
"operation_type": "allocation",
|
|
1026
1296
|
"data": {
|
|
1027
|
-
"
|
|
1028
|
-
"
|
|
1029
|
-
"type": "submission",
|
|
1030
|
-
"guard": [
|
|
1031
|
-
{
|
|
1032
|
-
"object": "weather_check_guard",
|
|
1033
|
-
"impack": true
|
|
1034
|
-
}
|
|
1035
|
-
],
|
|
1036
|
-
"submission": [
|
|
1037
|
-
{
|
|
1038
|
-
"guard": "weather_check_guard",
|
|
1039
|
-
"submission": [
|
|
1040
|
-
{
|
|
1041
|
-
"identifier": 2,
|
|
1042
|
-
"b_submission": true,
|
|
1043
|
-
"value_type": "U64",
|
|
1044
|
-
"value": 1745884800000
|
|
1045
|
-
}
|
|
1046
|
-
]
|
|
1047
|
-
}
|
|
1048
|
-
]
|
|
1049
|
-
}
|
|
1297
|
+
"object": "alice_travel_allocation",
|
|
1298
|
+
"alloc_by_guard": "no_spa_guard"
|
|
1050
1299
|
},
|
|
1051
|
-
"
|
|
1052
|
-
"
|
|
1053
|
-
"
|
|
1054
|
-
|
|
1300
|
+
"submission": {
|
|
1301
|
+
"type": "submission",
|
|
1302
|
+
"guard": [{"object": "no_spa_guard", "impack": true}],
|
|
1303
|
+
"submission": [{
|
|
1304
|
+
"guard": "no_spa_guard",
|
|
1305
|
+
"submission": [{"identifier": 0, "b_submission": true, "value_type": "Address", "value": "<ORDER_OBJECT_ID>", "name": "Order ID"}]
|
|
1306
|
+
}]
|
|
1307
|
+
},
|
|
1308
|
+
"env": {"account": "travel_provider", "network": "testnet", "no_cache": true}
|
|
1055
1309
|
}
|
|
1056
1310
|
```
|
|
1057
1311
|
|
|
1058
|
-
|
|
1312
|
+
### 8.4 Verify Allocation Result
|
|
1059
1313
|
|
|
1060
|
-
|
|
1314
|
+
After allocation, query the Allocation and Payment objects to verify the fund distribution.
|
|
1061
1315
|
|
|
1062
|
-
**Prompt**:
|
|
1316
|
+
**Prompt**: Query the Allocation object to verify the balance and payment records.
|
|
1063
1317
|
|
|
1064
1318
|
```json
|
|
1065
1319
|
{
|
|
1066
|
-
"operation_type": "
|
|
1320
|
+
"operation_type": "allocation",
|
|
1067
1321
|
"data": {
|
|
1068
|
-
"
|
|
1069
|
-
"info": {
|
|
1070
|
-
"type": "submission",
|
|
1071
|
-
"guard": [
|
|
1072
|
-
{
|
|
1073
|
-
"object": "weather_check_guard",
|
|
1074
|
-
"impack": true
|
|
1075
|
-
}
|
|
1076
|
-
],
|
|
1077
|
-
"submission": [
|
|
1078
|
-
{
|
|
1079
|
-
"guard": "weather_check_guard",
|
|
1080
|
-
"submission": [
|
|
1081
|
-
{
|
|
1082
|
-
"identifier": 2,
|
|
1083
|
-
"b_submission": true,
|
|
1084
|
-
"value_type": "U64",
|
|
1085
|
-
"value": 1746230400000
|
|
1086
|
-
}
|
|
1087
|
-
]
|
|
1088
|
-
}
|
|
1089
|
-
]
|
|
1090
|
-
}
|
|
1322
|
+
"object": "alice_travel_allocation"
|
|
1091
1323
|
},
|
|
1092
1324
|
"env": {
|
|
1093
|
-
"
|
|
1094
|
-
"
|
|
1325
|
+
"network": "testnet",
|
|
1326
|
+
"no_cache": true
|
|
1095
1327
|
}
|
|
1096
1328
|
}
|
|
1097
1329
|
```
|
|
1098
1330
|
|
|
1099
|
-
> **
|
|
1331
|
+
> **Expected result**: The Allocation `balance` should be 0 (all funds distributed), and the `payment` array should contain the Payment object ID(s) created by the allocation.
|
|
1100
1332
|
|
|
1101
|
-
|
|
1333
|
+
**Allocation Operation Structure**:
|
|
1334
|
+
|
|
1335
|
+
| Field | Type | Description |
|
|
1336
|
+
|-------|------|-------------|
|
|
1337
|
+
| `data.object` | string | Allocation object name/ID |
|
|
1338
|
+
| `data.alloc_by_guard` | string | Guard name/ID to verify (determines which allocator's rules apply) |
|
|
1339
|
+
| `submission` | object | Guard submission data (Order ID at identifier 0) |
|
|
1340
|
+
| `env.no_cache` | boolean | Set to `true` to avoid stale cache issues |
|
|
1102
1341
|
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
- [ ] Create `travel_provider`, `weather_provider`, `alice` accounts
|
|
1106
|
-
- [ ] Get test tokens for all accounts
|
|
1107
|
-
- [ ] Step 0.1: Calculate weather timestamps
|
|
1108
|
-
- [ ] Step 0.2: Create `weather_repo` Repository
|
|
1109
|
-
- [ ] Step 0.3: Add "Condition" policy to repository
|
|
1110
|
-
- [ ] Step 0.4: Add weather data (5 days)
|
|
1111
|
-
- [ ] Step 1: Create `travel_permission`
|
|
1112
|
-
- [ ] Step 2: Create `travel_arbitration`
|
|
1113
|
-
- [ ] Step 3.1: Create `weather_check_guard` (update weather_repo address)
|
|
1114
|
-
- [ ] Step 3.2: Create `travel_complete_guard`
|
|
1115
|
-
- [ ] Step 3.3: Create `travel_cancel_guard`
|
|
1116
|
-
- [ ] Step 4: Create `travel_machine` (with nodes including Complete/Cancel, published)
|
|
1117
|
-
- [ ] Step 5: Create and publish `travel_service` (with machine, arbitration, sales, order_allocators)
|
|
1118
|
-
- [ ] Step 6: Unpause Service
|
|
1119
|
-
- [ ] Step 7: Verify Service configuration
|
|
1120
|
-
- [ ] Step 8.1: Create test travel order (as alice)
|
|
1121
|
-
- [ ] Step 8.2: Advance Initial -> Start
|
|
1122
|
-
- [ ] Step 8.3: Advance Start -> Buy Insurance
|
|
1123
|
-
- [ ] Step 8.4: Advance Buy Insurance -> SPA
|
|
1124
|
-
- [ ] Step 8.5: Advance SPA -> Ice Scooting
|
|
1125
|
-
- [ ] Step 8.6: Wait 1s, advance Ice Scooting -> Complete
|
|
1126
|
-
- [ ] Step 9: Test weather check Guard (sunny passes, rainy fails)
|
|
1342
|
+
> **Important**: `alloc_by_guard` accepts Guard names (e.g., `"merchant_victory_guard"`) or Guard object IDs. The Guard must exist in the Allocation's `allocators` list. Only the first Guard that passes (first-Guard-wins) triggers fund distribution.
|
|
1127
1343
|
|
|
1128
1344
|
---
|
|
1129
1345
|
|
|
1130
1346
|
## Best Practices
|
|
1131
1347
|
|
|
1132
|
-
### 1.
|
|
1348
|
+
### 1. Guard Root Format
|
|
1133
1349
|
|
|
1134
|
-
|
|
1350
|
+
Guards use a direct GuardNode as the `root` field — no wrapper needed:
|
|
1351
|
+
```json
|
|
1352
|
+
"root": {
|
|
1353
|
+
"type": "logic_equal",
|
|
1354
|
+
"nodes": [...]
|
|
1355
|
+
}
|
|
1356
|
+
```
|
|
1135
1357
|
|
|
1136
|
-
### 2.
|
|
1358
|
+
### 2. Machine Node Structure
|
|
1137
1359
|
|
|
1138
|
-
|
|
1360
|
+
Each node's `pairs` define how to **enter** that node. The `prev_node` specifies the source, and `forwards` are the operations to advance:
|
|
1361
|
+
```json
|
|
1362
|
+
{
|
|
1363
|
+
"name": "TargetNode",
|
|
1364
|
+
"pairs": [{
|
|
1365
|
+
"prev_node": "SourceNode",
|
|
1366
|
+
"threshold": 1,
|
|
1367
|
+
"forwards": [{"name": "forward_name", "weight": 1, "permissionIndex": 1000}]
|
|
1368
|
+
}]
|
|
1369
|
+
}
|
|
1370
|
+
```
|
|
1139
1371
|
|
|
1140
|
-
|
|
1372
|
+
- Use `prev_node` (not `prior_node`)
|
|
1373
|
+
- `threshold` is required for every pair
|
|
1374
|
+
- Consolidate forwards with the same `prev_node` into one pair
|
|
1141
1375
|
|
|
1142
|
-
|
|
1376
|
+
### 3. Machine Publish Before Service
|
|
1143
1377
|
|
|
1144
|
-
|
|
1378
|
+
The Machine must be published (`"publish": true`) before binding it to a Service. Unpublished Machines cannot be referenced by Services.
|
|
1145
1379
|
|
|
1146
|
-
|
|
1380
|
+
### 4. Service Creation in One Step
|
|
1147
1381
|
|
|
1148
|
-
|
|
1382
|
+
Create the Service with all configurations — including `pause: false` and `publish: true` — in a single operation:
|
|
1383
|
+
```json
|
|
1384
|
+
{
|
|
1385
|
+
"pause": false,
|
|
1386
|
+
"publish": true,
|
|
1387
|
+
"order_allocators": {...},
|
|
1388
|
+
"sales": {...},
|
|
1389
|
+
"machine": "...",
|
|
1390
|
+
"arbitrations": {...}
|
|
1391
|
+
}
|
|
1392
|
+
```
|
|
1149
1393
|
|
|
1150
|
-
|
|
1394
|
+
### 5. Guard Submission for Progress
|
|
1151
1395
|
|
|
1152
|
-
|
|
1396
|
+
When a forward has a Guard, the Progress operation requires a `submission` field at the **top level** (not inside `data`):
|
|
1397
|
+
```json
|
|
1398
|
+
{
|
|
1399
|
+
"operation_type": "progress",
|
|
1400
|
+
"data": {...},
|
|
1401
|
+
"submission": {
|
|
1402
|
+
"type": "submission",
|
|
1403
|
+
"guard": [{"object": "guard_name", "impack": true}],
|
|
1404
|
+
"submission": [{
|
|
1405
|
+
"guard": "guard_name",
|
|
1406
|
+
"submission": [{"identifier": 0, "b_submission": true, "value_type": "Address", "value": "0x...", "name": "Order ID"}]
|
|
1407
|
+
}]
|
|
1408
|
+
},
|
|
1409
|
+
"env": {"no_cache": true, ...}
|
|
1410
|
+
}
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
### 6. Cache Management
|
|
1414
|
+
|
|
1415
|
+
Add `"no_cache": true` to the `env` field for all Progress operations and queries after mutations to avoid stale cache issues.
|
|
1153
1416
|
|
|
1154
|
-
|
|
1417
|
+
### 7. Weather Data Timestamps
|
|
1155
1418
|
|
|
1156
|
-
|
|
1157
|
-
|
|
1419
|
+
Weather data in the Repository is keyed by timestamp (`id`). The `weather_check_guard` queries `repository.data has("Condition", convert_number_address(activity_date))` — the `activity_date` submitted at runtime in Step 7.3 **must exactly match** the `id` used when adding data in Step 0.4.
|
|
1420
|
+
|
|
1421
|
+
- Always align timestamps to UTC 00:00:00 (`Math.floor(now / DAY_MS) * DAY_MS`) so they are reproducible.
|
|
1422
|
+
- Re-run `calc-weather-timestamps.js` at test time — the values depend on the current date.
|
|
1423
|
+
- The Guard only checks data **existence**, not the condition value. A "rainy" day will still pass. To reject by condition value, use a Guard querying `repository.data` with a value comparison.
|
|
1424
|
+
|
|
1425
|
+
### 8. Order Placement
|
|
1426
|
+
|
|
1427
|
+
Customers place orders using the `order_new` field in the Service operation. This automatically creates Order, Progress, and Allocation objects:
|
|
1428
|
+
```json
|
|
1429
|
+
{
|
|
1430
|
+
"operation_type": "service",
|
|
1431
|
+
"data": {
|
|
1432
|
+
"object": "service_name",
|
|
1433
|
+
"order_new": {
|
|
1434
|
+
"buy": {
|
|
1435
|
+
"items": [{"name": "Product", "stock": "1", "wip_hash": ""}],
|
|
1436
|
+
"total_pay": {"balance": "price_amount"}
|
|
1437
|
+
},
|
|
1438
|
+
"namedNewOrder": {"name": "order_name", "replaceExistName": true},
|
|
1439
|
+
"namedNewProgress": {"name": "progress_name", "replaceExistName": true},
|
|
1440
|
+
"namedNewAllocation": {"name": "allocation_name", "replaceExistName": true}
|
|
1441
|
+
}
|
|
1442
|
+
},
|
|
1443
|
+
"env": {"account": "customer", "network": "testnet"}
|
|
1444
|
+
}
|
|
1445
|
+
```
|