@wowok/agent-mcp 2.6.0 → 2.6.3
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/README.md +5 -3
- package/dist/customer/info-puzzle.d.ts +1 -1
- package/dist/customer/info-puzzle.js +4 -2
- package/dist/customer/risk-assessment.js +26 -4
- package/dist/customer/types.d.ts +2 -0
- package/dist/examples/guard-template-balance-check.json +38 -0
- package/dist/examples/guard-template-time-lock.json +39 -0
- package/dist/examples/machine-template-7node-rental.json +114 -0
- package/dist/examples/rental-ziroom-machine-create.json +137 -0
- package/dist/examples/rental-ziroom-permission-create.json +35 -0
- package/dist/examples/rental-ziroom-service-create.json +80 -0
- package/dist/examples/retail-myshop-service-create.json +88 -0
- package/dist/extensions/capability-manifest.d.ts +125 -0
- package/dist/extensions/capability-manifest.js +594 -0
- package/dist/extensions/constraint-registry.d.ts +24 -0
- package/dist/extensions/constraint-registry.js +196 -0
- package/dist/extensions/index.d.ts +12 -0
- package/dist/extensions/index.js +6 -0
- package/dist/extensions/metric-registry.d.ts +26 -0
- package/dist/extensions/metric-registry.js +257 -0
- package/dist/extensions/mode-evaluator.d.ts +15 -0
- package/dist/extensions/mode-evaluator.js +170 -0
- package/dist/extensions/modes.d.ts +2 -0
- package/dist/extensions/modes.js +493 -0
- package/dist/extensions/registry.d.ts +50 -0
- package/dist/extensions/registry.js +662 -0
- package/dist/extensions/types.d.ts +219 -0
- package/dist/extensions/types.js +1 -0
- package/dist/index.js +50 -0
- package/dist/knowledge/deployment-scanner.d.ts +3 -0
- package/dist/knowledge/deployment-scanner.js +64 -3
- package/dist/knowledge/fund-layer.d.ts +72 -0
- package/dist/knowledge/fund-layer.js +420 -0
- package/dist/knowledge/guard-render.d.ts +57 -0
- package/dist/knowledge/guard-render.js +700 -0
- package/dist/knowledge/guard-risk.d.ts +13 -0
- package/dist/knowledge/guard-risk.js +57 -0
- package/dist/knowledge/guard-submission-prompt.d.ts +31 -0
- package/dist/knowledge/guard-submission-prompt.js +171 -0
- package/dist/knowledge/guard-templates.js +278 -0
- package/dist/knowledge/machine-ledger.js +1 -1
- package/dist/knowledge/machine-render.d.ts +41 -0
- package/dist/knowledge/machine-render.js +565 -0
- package/dist/knowledge/machine-templates.js +24 -5
- package/dist/knowledge/reward-confirm.js +2 -2
- package/dist/knowledge/reward-puzzle.js +1 -1
- package/dist/knowledge/reward-risk.js +9 -9
- package/dist/knowledge/reward-templates.js +2 -2
- package/dist/knowledge/service-confirm.d.ts +15 -6
- package/dist/knowledge/service-confirm.js +119 -11
- package/dist/knowledge/service-context.js +1 -1
- package/dist/knowledge/service-ledger.js +1 -1
- package/dist/knowledge/service-risk.d.ts +1 -1
- package/dist/knowledge/service-risk.js +3 -3
- package/dist/knowledge/service-templates.js +2 -2
- package/dist/knowledge/service-translation.d.ts +1 -1
- package/dist/knowledge/service-translation.js +7 -7
- package/dist/knowledge/tool-constraints.js +6 -2
- package/dist/project/deployment-bridge.d.ts +1 -1
- package/dist/project/deployment-bridge.js +31 -7
- package/dist/project/deployment-doc.d.ts +3 -0
- package/dist/project/deployment-doc.js +76 -14
- package/dist/project/edit-planner.d.ts +123 -0
- package/dist/project/edit-planner.js +1371 -0
- package/dist/project/evaluation.d.ts +2 -0
- package/dist/project/evaluation.js +873 -88
- package/dist/project/graph-builder.d.ts +4 -1
- package/dist/project/graph-builder.js +132 -62
- package/dist/project/graph.d.ts +1 -0
- package/dist/project/handlers.d.ts +317 -6
- package/dist/project/handlers.js +1171 -19
- package/dist/project/stage-gate.d.ts +4 -0
- package/dist/project/stage-gate.js +64 -5
- package/dist/project/task-tracker.d.ts +26 -0
- package/dist/project/task-tracker.js +78 -0
- package/dist/safety/preview.js +16 -0
- package/dist/schema/call/allocation.d.ts +16 -16
- package/dist/schema/call/allocation.js +2 -2
- package/dist/schema/call/arbitration.js +19 -6
- package/dist/schema/call/base.d.ts +21 -13
- package/dist/schema/call/base.js +27 -6
- package/dist/schema/call/bridge.d.ts +5 -5
- package/dist/schema/call/bridge.js +3 -1
- package/dist/schema/call/contact.js +2 -2
- package/dist/schema/call/demand.d.ts +23 -31
- package/dist/schema/call/demand.js +2 -2
- package/dist/schema/call/guard.js +2 -2
- package/dist/schema/call/machine.d.ts +1976 -752
- package/dist/schema/call/machine.js +50 -4
- package/dist/schema/call/order.d.ts +149 -228
- package/dist/schema/call/order.js +4 -3
- package/dist/schema/call/payment.d.ts +183 -3
- package/dist/schema/call/payment.js +21 -3
- package/dist/schema/call/permission.js +2 -2
- package/dist/schema/call/personal.d.ts +241 -52
- package/dist/schema/call/progress.d.ts +53 -61
- package/dist/schema/call/progress.js +18 -4
- package/dist/schema/call/repository.d.ts +23 -31
- package/dist/schema/call/repository.js +2 -2
- package/dist/schema/call/reward.js +3 -3
- package/dist/schema/call/semantic.d.ts +1 -1
- package/dist/schema/call/semantic.js +37 -2
- package/dist/schema/call/service.d.ts +275 -127
- package/dist/schema/call/service.js +89 -13
- package/dist/schema/call/treasury.js +3 -3
- package/dist/schema/common/index.d.ts +13 -2
- package/dist/schema/common/index.js +80 -15
- package/dist/schema/local/index.d.ts +32 -35
- package/dist/schema/local/index.js +28 -6
- package/dist/schema/messenger/index.d.ts +274 -46
- package/dist/schema/operations.d.ts +1264 -544
- package/dist/schema/operations.js +65 -7
- package/dist/schema/project/index.d.ts +2846 -167
- package/dist/schema/project/index.js +570 -16
- package/dist/schema/query/index.d.ts +922 -349
- package/dist/schema/query/index.js +205 -42
- package/dist/schema/schema-query/index.d.ts +65 -3
- package/dist/schema/schema-query/index.js +38 -5
- package/dist/schema/utils/node-parser.js +20 -4
- package/dist/schema/utils/object-type-utils.d.ts +12 -0
- package/dist/schema/utils/object-type-utils.js +35 -0
- package/dist/schema/utils/permission-machine-check.d.ts +49 -0
- package/dist/schema/utils/permission-machine-check.js +121 -0
- package/dist/schema/utils/skills-recommendation.d.ts +2 -0
- package/dist/schema/utils/skills-recommendation.js +76 -0
- package/dist/schema-query/index.d.ts +20 -1
- package/dist/schema-query/index.js +306 -4
- package/dist/schemas/account_operation.output.json +7 -1
- package/dist/schemas/account_operation.schema.json +3 -3
- package/dist/schemas/bridge_operation.output.json +6 -0
- package/dist/schemas/bridge_operation.schema.json +1 -1
- package/dist/schemas/guard-templates.json +379 -0
- package/dist/schemas/guard2file.schema.json +1 -1
- package/dist/schemas/index.json +1 -1
- package/dist/schemas/local_info_operation.output.json +6 -0
- package/dist/schemas/local_mark_operation.output.json +7 -1
- package/dist/schemas/local_mark_operation.schema.json +1 -1
- package/dist/schemas/machineNode2file.schema.json +1 -1
- package/dist/schemas/messenger_operation.schema.json +4 -4
- package/dist/schemas/onchain_events.output.json +1 -1
- package/dist/schemas/onchain_operations.output.json +2820 -0
- package/dist/schemas/onchain_operations.schema.json +444 -330
- package/dist/schemas/onchain_operations_allocation.schema.json +37 -28
- package/dist/schemas/onchain_operations_arbitration.schema.json +16 -14
- package/dist/schemas/onchain_operations_contact.schema.json +10 -10
- package/dist/schemas/onchain_operations_demand.schema.json +10 -10
- package/dist/schemas/onchain_operations_gen_passport.schema.json +16 -16
- package/dist/schemas/onchain_operations_gen_proof.schema.json +2 -2
- package/dist/schemas/onchain_operations_guard.schema.json +2 -2
- package/dist/schemas/onchain_operations_machine.schema.json +43 -35
- package/dist/schemas/onchain_operations_order.schema.json +72 -78
- package/dist/schemas/onchain_operations_payment.schema.json +141 -111
- package/dist/schemas/onchain_operations_permission.schema.json +3 -3
- package/dist/schemas/onchain_operations_personal.schema.json +7 -7
- package/dist/schemas/onchain_operations_progress.schema.json +9 -9
- package/dist/schemas/onchain_operations_proof.schema.json +8 -8
- package/dist/schemas/onchain_operations_repository.schema.json +10 -10
- package/dist/schemas/onchain_operations_reward.schema.json +14 -14
- package/dist/schemas/onchain_operations_service.schema.json +119 -48
- package/dist/schemas/onchain_operations_treasury.schema.json +11 -11
- package/dist/schemas/onchain_table_data.output.json +33 -25
- package/dist/schemas/onchain_table_data.schema.json +22 -21
- package/dist/schemas/project_operation.output.json +2194 -23
- package/dist/schemas/project_operation.schema.json +84 -6
- package/dist/schemas/query_toolkit.output.json +108 -80
- package/dist/schemas/query_toolkit.schema.json +12 -12
- package/dist/schemas/schema_query.output.json +70 -3
- package/dist/schemas/schema_query.schema.json +18 -3
- package/dist/schemas/wowok_buildin_info.output.json +81 -8
- package/dist/schemas/wowok_buildin_info.schema.json +46 -2
- package/dist/tools/handlers/local.js +20 -5
- package/dist/tools/handlers/onchain.js +594 -6
- package/dist/tools/handlers/project.js +76 -4
- package/dist/tools/handlers/query.js +27 -0
- package/dist/tools/handlers/schema-query.js +43 -1
- package/dist/tools/handlers/task-status.d.ts +170 -0
- package/dist/tools/handlers/task-status.js +55 -0
- package/dist/tools/handlers/wip.js +47 -1
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.js +387 -12
- package/dist/tools/retry.d.ts +8 -0
- package/dist/tools/retry.js +85 -0
- package/dist/tools/wip-deploy-assist.d.ts +28 -0
- package/dist/tools/wip-deploy-assist.js +278 -0
- package/package.json +2 -2
- package/dist/schemas/guard-node-examples.md +0 -199
|
@@ -1,10 +1,247 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
2
|
import { resolve } from "path";
|
|
3
|
-
import { CallService, CallMachine, CallProgress, CallGuard, CallArbitration, CallRepository, CallContact, CallTreasury, CallReward, CallAllocation, CallPersonal, CallPayment, CallDemand, CallOrder, gen_passport, CallProof, gen_proof, ResponseData, LocalProject, parseGuardFile, formatGuardParseErrors, transformRewardInput, transformPersonalInput, } from "@wowok/wowok";
|
|
3
|
+
import { CallService, CallMachine, CallProgress, CallGuard, CallArbitration, CallRepository, CallContact, CallTreasury, CallReward, CallAllocation, CallPersonal, CallPayment, CallDemand, CallOrder, gen_passport, CallProof, gen_proof, ResponseData, LocalProject, LocalMark, query_objects, parseGuardFile, formatGuardParseErrors, transformRewardInput, transformPersonalInput, } from "@wowok/wowok";
|
|
4
4
|
import { OnchainOperationsSchema, strictParse, getEnvConfig, handleCallResult, checkClientSchemaCompatibility, parseMachineNodesFromText, formatNodeErrors as formatMachineNodeErrors, transformSubmission, } from "../../schema/index.js";
|
|
5
5
|
import { handlePermissionOperations } from "../../schema/call/permission-handler.js";
|
|
6
|
+
import { collectPermissionIndexes, checkPermissionAuthorization, formatPermissionWarning, } from "../../schema/utils/permission-machine-check.js";
|
|
6
7
|
import { isProjectServiceEnabled, normalize as projectNormalize, } from "../../project/index.js";
|
|
8
|
+
import { getSkillsHintForOnchain } from "../../schema/utils/skills-recommendation.js";
|
|
9
|
+
import { processFundOperation, generateSkillsHint as generateFundSkillsHint } from "../../knowledge/fund-layer.js";
|
|
10
|
+
import { generateGuardSubmissionPrompt, formatSubmissionPrompt } from "../../knowledge/guard-submission-prompt.js";
|
|
7
11
|
import { withHarness } from "../shared.js";
|
|
12
|
+
function formatRecipientLabel(recipient) {
|
|
13
|
+
if (!recipient)
|
|
14
|
+
return "unknown";
|
|
15
|
+
if (typeof recipient === "string")
|
|
16
|
+
return recipient;
|
|
17
|
+
if ("GuardIdentifier" in recipient) {
|
|
18
|
+
return `GuardIdentifier(${recipient.GuardIdentifier})`;
|
|
19
|
+
}
|
|
20
|
+
if ("Entity" in recipient) {
|
|
21
|
+
const addr = recipient.Entity;
|
|
22
|
+
if (typeof addr === "string" && addr.length > 16) {
|
|
23
|
+
return `Entity(${addr.slice(0, 8)}...${addr.slice(-4)})`;
|
|
24
|
+
}
|
|
25
|
+
return `Entity(${addr})`;
|
|
26
|
+
}
|
|
27
|
+
if ("Signer" in recipient) {
|
|
28
|
+
return "Signer (transaction signer)";
|
|
29
|
+
}
|
|
30
|
+
return JSON.stringify(recipient);
|
|
31
|
+
}
|
|
32
|
+
function formatMistAmount(amount) {
|
|
33
|
+
const wowEquivalent = Number(amount) / 1_000_000_000;
|
|
34
|
+
if (wowEquivalent === 0)
|
|
35
|
+
return "0 WOW";
|
|
36
|
+
const wowStr = wowEquivalent.toFixed(9).replace(/\.?0+$/, "");
|
|
37
|
+
return `${wowStr} WOW (${amount.toString()} MIST)`;
|
|
38
|
+
}
|
|
39
|
+
function extractNodesForCheck(data) {
|
|
40
|
+
if (!data?.node)
|
|
41
|
+
return [];
|
|
42
|
+
if (data.node.nodes && Array.isArray(data.node.nodes)) {
|
|
43
|
+
return data.node.nodes;
|
|
44
|
+
}
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
function resolvePermissionRef(data) {
|
|
48
|
+
if (!data?.object)
|
|
49
|
+
return null;
|
|
50
|
+
if (typeof data.object === "string")
|
|
51
|
+
return null;
|
|
52
|
+
const obj = data.object;
|
|
53
|
+
if (obj.permission) {
|
|
54
|
+
if (typeof obj.permission === "string")
|
|
55
|
+
return obj.permission;
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
async function checkMachinePermissionBeforePublish(data, env) {
|
|
61
|
+
if (data?.publish !== true)
|
|
62
|
+
return "";
|
|
63
|
+
const nodes = extractNodesForCheck(data);
|
|
64
|
+
if (nodes.length === 0)
|
|
65
|
+
return "";
|
|
66
|
+
const collection = collectPermissionIndexes(nodes);
|
|
67
|
+
if (collection.requiredIndexes.length === 0) {
|
|
68
|
+
return "";
|
|
69
|
+
}
|
|
70
|
+
let warning = "";
|
|
71
|
+
if (collection.missingRefs.length > 0) {
|
|
72
|
+
warning += "WARNING: Some forwards have NEITHER permissionIndex nor namedOperator. " +
|
|
73
|
+
"These forwards cannot be executed. Add one or the other:\n";
|
|
74
|
+
for (const ref of collection.missingRefs) {
|
|
75
|
+
warning += ` - Node "${ref.nodeName}", pair prev_node="${ref.prevNode}", forward "${ref.forwardName}"\n`;
|
|
76
|
+
}
|
|
77
|
+
warning += "\n";
|
|
78
|
+
}
|
|
79
|
+
const permRef = resolvePermissionRef(data);
|
|
80
|
+
if (!permRef) {
|
|
81
|
+
if (collection.requiredIndexes.length > 0) {
|
|
82
|
+
warning += "PRE-PUBLISH REMINDER: This Machine uses " + collection.requiredIndexes.length +
|
|
83
|
+
" custom permission index(es): [" + collection.requiredIndexes.join(", ") + "]. " +
|
|
84
|
+
"After creating the Permission object, grant entities for each index via " +
|
|
85
|
+
'permission.op="add perm by index" BEFORE publishing the Machine. ' +
|
|
86
|
+
"Otherwise, forwards using these indexes will be un-executable.\n";
|
|
87
|
+
}
|
|
88
|
+
return warning;
|
|
89
|
+
}
|
|
90
|
+
let permAddress = permRef;
|
|
91
|
+
if (!permRef.startsWith("0x")) {
|
|
92
|
+
try {
|
|
93
|
+
const mark = await LocalMark.Instance().get(permRef);
|
|
94
|
+
if (mark?.address) {
|
|
95
|
+
permAddress = mark.address;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return warning + "PRE-PUBLISH: Could not resolve Permission name '" + permRef +
|
|
99
|
+
"' to an address. Ensure the Permission object exists and is marked. " +
|
|
100
|
+
"Required indexes: [" + collection.requiredIndexes.join(", ") + "]\n";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return warning + "PRE-PUBLISH: Failed to lookup Permission name '" + permRef +
|
|
105
|
+
"'. Required indexes: [" + collection.requiredIndexes.join(", ") + "]\n";
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const network = env?.network ?? "testnet";
|
|
109
|
+
let permData = null;
|
|
110
|
+
try {
|
|
111
|
+
const queryResult = await query_objects({
|
|
112
|
+
objects: [permAddress],
|
|
113
|
+
network: network,
|
|
114
|
+
});
|
|
115
|
+
const permObj = queryResult?.objects?.[0];
|
|
116
|
+
if (permObj) {
|
|
117
|
+
permData = {
|
|
118
|
+
address: permAddress,
|
|
119
|
+
entity_count: permObj.entity_count,
|
|
120
|
+
perm_remark: permObj.perm_remark,
|
|
121
|
+
owner: permObj.owner ?? permObj.builder,
|
|
122
|
+
admin: permObj.admin,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return warning + "PRE-PUBLISH: Failed to query Permission object " + permAddress +
|
|
128
|
+
". Required indexes: [" + collection.requiredIndexes.join(", ") + "]\n";
|
|
129
|
+
}
|
|
130
|
+
if (!permData) {
|
|
131
|
+
return warning + "PRE-PUBLISH: Permission object " + permAddress +
|
|
132
|
+
" not found on-chain. Required indexes: [" + collection.requiredIndexes.join(", ") + "]\n";
|
|
133
|
+
}
|
|
134
|
+
const report = checkPermissionAuthorization(collection.requiredIndexes, permData);
|
|
135
|
+
if (!report.allAuthorized) {
|
|
136
|
+
warning += formatPermissionWarning(report) + "\n";
|
|
137
|
+
}
|
|
138
|
+
return warning;
|
|
139
|
+
}
|
|
140
|
+
async function detectServiceGuardCircularDependency(data) {
|
|
141
|
+
if (!data || typeof data.object !== "object" || data.object === null)
|
|
142
|
+
return null;
|
|
143
|
+
const orderAllocators = data.order_allocators;
|
|
144
|
+
if (!orderAllocators || typeof orderAllocators !== "object")
|
|
145
|
+
return null;
|
|
146
|
+
const allocators = Array.isArray(orderAllocators.allocators)
|
|
147
|
+
? orderAllocators.allocators
|
|
148
|
+
: [];
|
|
149
|
+
if (allocators.length === 0)
|
|
150
|
+
return null;
|
|
151
|
+
const unresolvedGuards = [];
|
|
152
|
+
const seen = new Set();
|
|
153
|
+
for (const alloc of allocators) {
|
|
154
|
+
const guardRef = alloc?.guard;
|
|
155
|
+
if (typeof guardRef === "string" && !guardRef.startsWith("0x")) {
|
|
156
|
+
if (seen.has(guardRef))
|
|
157
|
+
continue;
|
|
158
|
+
seen.add(guardRef);
|
|
159
|
+
try {
|
|
160
|
+
const addr = await LocalMark.Instance().get_address(guardRef);
|
|
161
|
+
if (!addr) {
|
|
162
|
+
unresolvedGuards.push(guardRef);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
unresolvedGuards.push(guardRef);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (unresolvedGuards.length === 0)
|
|
171
|
+
return null;
|
|
172
|
+
const guardList = unresolvedGuards.map(g => `'${g}'`).join(", ");
|
|
173
|
+
const serviceObj = data.object;
|
|
174
|
+
const serviceName = serviceObj.name ?? "<service_name>";
|
|
175
|
+
const recommended_steps = [
|
|
176
|
+
{
|
|
177
|
+
step: 1,
|
|
178
|
+
action: "onchain_operations",
|
|
179
|
+
operation_type: "service",
|
|
180
|
+
params: {
|
|
181
|
+
...data,
|
|
182
|
+
order_allocators: null,
|
|
183
|
+
publish: false,
|
|
184
|
+
},
|
|
185
|
+
purpose: `Create Service '${serviceName}' WITHOUT order_allocators to establish ` +
|
|
186
|
+
`its LocalMark entry. After this step, the name '${serviceName}' resolves ` +
|
|
187
|
+
`to an on-chain address.`,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
step: 2,
|
|
191
|
+
action: "onchain_operations",
|
|
192
|
+
operation_type: "guard",
|
|
193
|
+
params: unresolvedGuards.map(name => ({
|
|
194
|
+
object: { name, type: "Guard" },
|
|
195
|
+
_hint: `Table items may reference Service '${serviceName}' by name`,
|
|
196
|
+
})),
|
|
197
|
+
purpose: `Create Guard(s) ${guardList}. Their table items can now resolve ` +
|
|
198
|
+
`Service '${serviceName}' by name (LocalMark entry from step 1).`,
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
step: 3,
|
|
202
|
+
action: "onchain_operations",
|
|
203
|
+
operation_type: "service",
|
|
204
|
+
params: {
|
|
205
|
+
object: serviceName,
|
|
206
|
+
order_allocators: orderAllocators,
|
|
207
|
+
publish: data.publish ?? true,
|
|
208
|
+
},
|
|
209
|
+
purpose: "Update Service with order_allocators (referencing Guards created in " +
|
|
210
|
+
"step 2) and publish. After publish, order_allocators becomes SDK-LOCKED.",
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
const guideText = `⚠️ Circular dependency detected: Service ↔ Guard\n\n` +
|
|
214
|
+
`Cannot create Service with order_allocators referencing non-existent Guard(s): ${guardList}.\n\n` +
|
|
215
|
+
`Explanation: Guard table items may reference the Service by name, creating a circular ` +
|
|
216
|
+
`dependency: the Service must exist as a LocalMark before the Guard can resolve it, ` +
|
|
217
|
+
`but the Guard must exist before the Service can reference it in order_allocators. ` +
|
|
218
|
+
`The SDK would fail with a BCS serialization error: "Expected valid Address for ` +
|
|
219
|
+
`ValueType.Address, but failed to resolve string: <name>".\n\n` +
|
|
220
|
+
`Recommended steps:\n` +
|
|
221
|
+
recommended_steps
|
|
222
|
+
.map(s => ` Step ${s.step} (${s.operation_type}): ${s.purpose}`)
|
|
223
|
+
.join("\n") +
|
|
224
|
+
`\n\nHint: This is the canonical Service↔Guard circular dependency. The 3-step ` +
|
|
225
|
+
`pattern above is the documented solution (see SCHEMA-03 / P0-01 fix in ` +
|
|
226
|
+
`schema/call/service.ts publish field description).`;
|
|
227
|
+
return {
|
|
228
|
+
content: [{
|
|
229
|
+
type: "text",
|
|
230
|
+
text: guideText,
|
|
231
|
+
}],
|
|
232
|
+
structuredContent: {
|
|
233
|
+
result: {
|
|
234
|
+
type: "error",
|
|
235
|
+
error: `Cannot create Service with order_allocators referencing non-existent Guard(s): ${guardList}. Circular dependency: Service↔Guard.`,
|
|
236
|
+
error_code: "circular_dependency_detected",
|
|
237
|
+
retryable: true,
|
|
238
|
+
recovery_hint: JSON.stringify(recommended_steps),
|
|
239
|
+
related_object: serviceName,
|
|
240
|
+
},
|
|
241
|
+
message: "Circular dependency detected: Service ↔ Guard. See recovery_hint for the 3-step creation pattern.",
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
}
|
|
8
245
|
export async function handleOnchainOperations(args) {
|
|
9
246
|
let validated;
|
|
10
247
|
let constraintWarnings = [];
|
|
@@ -34,12 +271,19 @@ export async function handleOnchainOperations(args) {
|
|
|
34
271
|
const rData = ResponseData(result);
|
|
35
272
|
const created = rData.filter((r) => r.change === "created" && r.object);
|
|
36
273
|
for (const obj of created) {
|
|
274
|
+
const isService = !!(obj.type_raw && obj.type_raw.includes("service::Service"));
|
|
37
275
|
await lp.addObject(project.id, {
|
|
38
276
|
objectAddress: obj.object,
|
|
39
277
|
objectType: obj.type_raw,
|
|
40
|
-
role: "reference",
|
|
278
|
+
role: isService ? "core" : "reference",
|
|
41
279
|
});
|
|
42
280
|
}
|
|
281
|
+
if (operationType === "service") {
|
|
282
|
+
const svc = created.find((o) => o.type_raw && o.type_raw.includes("service::Service"));
|
|
283
|
+
if (svc && svc.object) {
|
|
284
|
+
await lp.updateProject(project.id, { serviceAddress: svc.object }).catch(() => { });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
43
287
|
}
|
|
44
288
|
catch {
|
|
45
289
|
}
|
|
@@ -81,19 +325,144 @@ export async function handleOnchainOperations(args) {
|
|
|
81
325
|
validated.data = pn.data;
|
|
82
326
|
projectWarnings = pn.warnings ?? [];
|
|
83
327
|
}
|
|
328
|
+
if (!env.account && projectEnvPrefix) {
|
|
329
|
+
try {
|
|
330
|
+
const project = await LocalProject.Instance().getProjectByPrefix(projectEnvPrefix);
|
|
331
|
+
if (project?.defaultAccount) {
|
|
332
|
+
env.account = project.defaultAccount;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
catch { }
|
|
336
|
+
}
|
|
84
337
|
delete env.confirmed;
|
|
85
338
|
delete env.user_intent_phrases;
|
|
86
339
|
delete env.project;
|
|
87
340
|
delete env.client_schema_version;
|
|
88
341
|
delete env.schema_strict_mode;
|
|
342
|
+
const skillsHint = getSkillsHintForOnchain(validated.operation_type, validated.data);
|
|
343
|
+
if (skillsHint) {
|
|
344
|
+
constraintWarnings.push(skillsHint);
|
|
345
|
+
}
|
|
89
346
|
switch (validated.operation_type) {
|
|
90
347
|
case "service": {
|
|
91
|
-
const callService = new CallService(validated.data);
|
|
92
|
-
const result = validated.submission
|
|
93
|
-
? await callService.call_with_submission(env, await transformSubmission(validated.submission))
|
|
94
|
-
: await callService.call(env);
|
|
95
348
|
const data = validated.data;
|
|
349
|
+
const circularDepGuide = await detectServiceGuardCircularDependency(data);
|
|
350
|
+
if (circularDepGuide) {
|
|
351
|
+
return circularDepGuide;
|
|
352
|
+
}
|
|
353
|
+
const fundResult = await processFundOperation(data, "service", env?.network);
|
|
354
|
+
constraintWarnings.push(...fundResult.warnings);
|
|
355
|
+
if (fundResult.confirmation) {
|
|
356
|
+
constraintWarnings.push(fundResult.confirmation.message);
|
|
357
|
+
const fundHint = generateFundSkillsHint(fundResult.confirmation);
|
|
358
|
+
if (fundHint)
|
|
359
|
+
constraintWarnings.push(fundHint);
|
|
360
|
+
}
|
|
361
|
+
const orderAllocators = data?.order_allocators;
|
|
362
|
+
if (orderAllocators && typeof orderAllocators === "object") {
|
|
363
|
+
const allocators = Array.isArray(orderAllocators.allocators)
|
|
364
|
+
? orderAllocators.allocators : [];
|
|
365
|
+
for (let i = 0; i < allocators.length; i++) {
|
|
366
|
+
const alloc = allocators[i];
|
|
367
|
+
const sharing = Array.isArray(alloc?.sharing) ? alloc.sharing : [];
|
|
368
|
+
if (sharing.length <= 1)
|
|
369
|
+
continue;
|
|
370
|
+
const seen = new Map();
|
|
371
|
+
for (let j = 0; j < sharing.length; j++) {
|
|
372
|
+
const who = sharing[j]?.who;
|
|
373
|
+
let key = "";
|
|
374
|
+
if (typeof who === "string") {
|
|
375
|
+
key = who;
|
|
376
|
+
}
|
|
377
|
+
else if (who && typeof who === "object") {
|
|
378
|
+
if (who.Entity && typeof who.Entity === "object") {
|
|
379
|
+
key = "Entity:" + (who.Entity.name_or_address ?? "");
|
|
380
|
+
}
|
|
381
|
+
else if (who.Signer) {
|
|
382
|
+
key = "Signer";
|
|
383
|
+
}
|
|
384
|
+
else if (who.GuardIdentifier !== undefined) {
|
|
385
|
+
key = "Guard:" + who.GuardIdentifier;
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
key = JSON.stringify(who);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (!key)
|
|
392
|
+
continue;
|
|
393
|
+
const count = seen.get(key) ?? 0;
|
|
394
|
+
seen.set(key, count + 1);
|
|
395
|
+
}
|
|
396
|
+
for (const [key, count] of seen) {
|
|
397
|
+
if (count > 1) {
|
|
398
|
+
constraintWarnings.push(`DUPLICATE SHARING ENTITY: Allocator[${i}] has entity "${key}" appearing ${count} times ` +
|
|
399
|
+
`in the sharing array. This is likely a configuration error — each entity should appear ` +
|
|
400
|
+
`at most once per allocator. Total sharing: ${count * 100}% to the same entity. ` +
|
|
401
|
+
`If intentional, ignore this warning.`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
const callService = new CallService(validated.data);
|
|
407
|
+
let result;
|
|
408
|
+
try {
|
|
409
|
+
result = validated.submission
|
|
410
|
+
? await callService.call_with_submission(env, await transformSubmission(validated.submission))
|
|
411
|
+
: await callService.call(env);
|
|
412
|
+
}
|
|
413
|
+
catch (e) {
|
|
414
|
+
const errMsg = e.message ?? String(e);
|
|
415
|
+
if (/not found|GetObjectExisted|resolve|LocalMark|failed to resolve/i.test(errMsg)) {
|
|
416
|
+
const permRef = (typeof data?.object === 'object' && data?.object !== null)
|
|
417
|
+
? data.object.permission
|
|
418
|
+
: undefined;
|
|
419
|
+
throw new Error(`DEPENDENCY RESOLUTION FAILED: One or more referenced objects could not be resolved.\n` +
|
|
420
|
+
`Original error: ${errMsg}\n` +
|
|
421
|
+
`Common causes:\n` +
|
|
422
|
+
` 1. Object name typo — check spelling in LocalMark\n` +
|
|
423
|
+
` 2. Object not yet created — create dependencies first\n` +
|
|
424
|
+
` 3. Object on different network — check env.network\n` +
|
|
425
|
+
`Referenced objects in this call: machine=${data?.machine ?? "<none>"}, ` +
|
|
426
|
+
`buy_guard=${data?.buy_guard ?? "<none>"}, ` +
|
|
427
|
+
`permission=${permRef ?? "<inline/new>"}, ` +
|
|
428
|
+
`arbitrations=${Array.isArray(data?.arbitrations) ? data.arbitrations.length + " item(s)" : "<none>"}`);
|
|
429
|
+
}
|
|
430
|
+
throw e;
|
|
431
|
+
}
|
|
96
432
|
const intent = data?.publish ? "publish service" : data?.bPublished !== undefined ? "modify service" : "create service";
|
|
433
|
+
const isCreatingService = data?.object != null && typeof data.object === "object";
|
|
434
|
+
const hasCompensationFundAdd = data?.compensation_fund_add != null;
|
|
435
|
+
if (isCreatingService && !hasCompensationFundAdd) {
|
|
436
|
+
constraintWarnings.push("Service.compensation_fund balance is 0 — no refund protection for buyers. " +
|
|
437
|
+
"If buyers need refund protection, call service.compensation_fund_add with a Coin<T> " +
|
|
438
|
+
"to deposit. Without deposit, arbitration rulings cannot withdraw compensation " +
|
|
439
|
+
"from this Service. (Note: some industries like freelance do not require this; " +
|
|
440
|
+
"the evaluation engine will check industry-specific requirements.)");
|
|
441
|
+
}
|
|
442
|
+
const isPublishing = data?.publish === true;
|
|
443
|
+
const hasArbitrations = Array.isArray(data?.arbitrations) && data.arbitrations.length > 0;
|
|
444
|
+
if (isPublishing && !hasArbitrations && hasCompensationFundAdd) {
|
|
445
|
+
throw new Error("HARD PRE-CHECK FAILED: Cannot publish Service with compensation_fund > 0 but no Arbitration bound.\n" +
|
|
446
|
+
"Contract (service.move:494): assert!(arbitration_count > 0, E_ARBITRATION_NOT_SET_WITH_COMPENSATION_FUND).\n" +
|
|
447
|
+
"The transaction WILL ABORT on-chain if you proceed.\n" +
|
|
448
|
+
"Fix: (1) Create an Arbitration object first (onchain_operations with operation_type='arbitration').\n" +
|
|
449
|
+
" (2) Set data.arbitrations=[<arbitration_name_or_id>] in this same call.\n" +
|
|
450
|
+
" (3) Then call publish=true.\n" +
|
|
451
|
+
"Note: After publish, arbitrations can still be ADDED (but not removed without pause+lock).");
|
|
452
|
+
}
|
|
453
|
+
if (isPublishing && !hasArbitrations && !hasCompensationFundAdd) {
|
|
454
|
+
constraintWarnings.push("WARNING: Publishing Service without arbitrations. If compensation_fund was " +
|
|
455
|
+
"deposited in a prior call, the contract (service.move:494) WILL ABORT with " +
|
|
456
|
+
"E_ARBITRATION_NOT_SET_WITH_COMPENSATION_FUND. If compensation_fund is 0, " +
|
|
457
|
+
"publish will succeed but buyers have no dispute resolution path. " +
|
|
458
|
+
"Recommended: bind arbitrations before publish.");
|
|
459
|
+
}
|
|
460
|
+
if (!isPublishing && hasCompensationFundAdd && !hasArbitrations) {
|
|
461
|
+
constraintWarnings.push("WARNING: Depositing compensation_fund on a Service without arbitrations bound. " +
|
|
462
|
+
"If this Service is already published, arbitration rulings will not be able to " +
|
|
463
|
+
"withdraw compensation from this Service. Recommended: bind arbitrations first, " +
|
|
464
|
+
"then deposit compensation_fund.");
|
|
465
|
+
}
|
|
97
466
|
const output = handleCallResult(result, withHarness(buildCtx(data), "service", intent));
|
|
98
467
|
autoAddToProject(projectEnvPrefix, validated.operation_type, result).catch(() => { });
|
|
99
468
|
return output;
|
|
@@ -123,6 +492,33 @@ export async function handleOnchainOperations(args) {
|
|
|
123
492
|
bReplace: true
|
|
124
493
|
};
|
|
125
494
|
}
|
|
495
|
+
const machineData = validated.data;
|
|
496
|
+
const permWarning = await checkMachinePermissionBeforePublish(machineData, env);
|
|
497
|
+
if (permWarning) {
|
|
498
|
+
constraintWarnings.push(permWarning);
|
|
499
|
+
}
|
|
500
|
+
const machineNodes = machineData?.node?.nodes ?? machineData?.node;
|
|
501
|
+
if (Array.isArray(machineNodes)) {
|
|
502
|
+
const arbPatterns = /^(dispute|disputed|arbitration|arb_|arb\.|resolve_dispute|start_arbitration|open_dispute)/i;
|
|
503
|
+
for (const node of machineNodes) {
|
|
504
|
+
const nodeName = typeof node === 'object' ? node?.name : '';
|
|
505
|
+
if (typeof nodeName === 'string' && arbPatterns.test(nodeName)) {
|
|
506
|
+
constraintWarnings.push(`ARBITRATION SEPARATION WARNING: Machine node "${nodeName}" appears to be an arbitration-related node.\n` +
|
|
507
|
+
`CORE PRINCIPLE: A merchant cannot handle disputes fairly — they would be 'both player and referee'.\n` +
|
|
508
|
+
`The WoWok smart contract design makes Arbitration a completely separate object:\n` +
|
|
509
|
+
` - Arbitration has its own Permission, voting_guard, usage_guard, and balance\n` +
|
|
510
|
+
` - Arb instances have a 7-state lifecycle managed by Arbitration, not by Machine\n` +
|
|
511
|
+
` - The Order object is the bridge: it has 'progress' (Machine) and 'dispute' (Arb list) fields\n` +
|
|
512
|
+
` - When Arb rules, it calls order::compensate() — the ONLY connection to the business flow\n\n` +
|
|
513
|
+
`CORRECT PATTERN:\n` +
|
|
514
|
+
` - Machine: business states only (ordered -> shipped -> delivered -> refunded -> completed)\n` +
|
|
515
|
+
` - Arbitration: separate object with its own Permission (NOT shared with Service)\n` +
|
|
516
|
+
` - Arb: 7-state lifecycle (Principal_confirming -> Arbitrator_confirming -> Voting -> Arbitrated -> Objectionable -> Finished -> Withdrawn)\n` +
|
|
517
|
+
` - Use 'refunded'/'cancelled' nodes for the BUSINESS OUTCOME of a dispute, not 'disputed'\n\n` +
|
|
518
|
+
`If this node name is a legitimate business state (not arbitration), ignore this warning.`);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
126
522
|
const callMachine = new CallMachine(validated.data);
|
|
127
523
|
const result = validated.submission
|
|
128
524
|
? await callMachine.call_with_submission(env, await transformSubmission(validated.submission))
|
|
@@ -143,6 +539,65 @@ export async function handleOnchainOperations(args) {
|
|
|
143
539
|
const result = validated.submission
|
|
144
540
|
? await callProgress.call_with_submission(env, await transformSubmission(validated.submission))
|
|
145
541
|
: await callProgress.call(env);
|
|
542
|
+
if (!validated.submission && result && typeof result === 'object' && 'submission' in result) {
|
|
543
|
+
const submissionData = result.submission;
|
|
544
|
+
if (Array.isArray(submissionData) && submissionData.length > 0) {
|
|
545
|
+
const guardRef = submissionData[0]?.guard;
|
|
546
|
+
if (guardRef) {
|
|
547
|
+
const submissionCtx = {
|
|
548
|
+
operation: "progress_next",
|
|
549
|
+
network: env?.network,
|
|
550
|
+
signer: env?.account,
|
|
551
|
+
progress_address: validated.data?.object,
|
|
552
|
+
order_address: validated.data?.task,
|
|
553
|
+
};
|
|
554
|
+
const prompt = await generateGuardSubmissionPrompt(guardRef, submissionCtx);
|
|
555
|
+
if (prompt) {
|
|
556
|
+
constraintWarnings.push(formatSubmissionPrompt(prompt));
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (result && typeof result === 'object' && 'digest' in result
|
|
562
|
+
&& result?.effects?.status?.status === 'success') {
|
|
563
|
+
try {
|
|
564
|
+
const progressRef = validated.data?.object;
|
|
565
|
+
let progressAddr;
|
|
566
|
+
if (typeof progressRef === 'string') {
|
|
567
|
+
if (progressRef.startsWith('0x')) {
|
|
568
|
+
progressAddr = progressRef;
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
progressAddr = await LocalMark.Instance().get_address(progressRef) ?? undefined;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
if (progressAddr) {
|
|
575
|
+
const progressQuery = await query_objects({
|
|
576
|
+
objects: [progressAddr],
|
|
577
|
+
no_cache: true,
|
|
578
|
+
network: env?.network,
|
|
579
|
+
});
|
|
580
|
+
const progressObj = progressQuery?.objects?.[0];
|
|
581
|
+
if (progressObj?.type === "Progress") {
|
|
582
|
+
const currentNode = progressObj.current ?? "<unknown>";
|
|
583
|
+
const historyCount = Array.isArray(progressObj.session)
|
|
584
|
+
? progressObj.session.length : 0;
|
|
585
|
+
const machineAddr = progressObj.machine;
|
|
586
|
+
const previewLines = [
|
|
587
|
+
`PROGRESS PREVIEW:`,
|
|
588
|
+
` • current node: "${currentNode}"`,
|
|
589
|
+
` • history count: ${historyCount} session(s)`,
|
|
590
|
+
];
|
|
591
|
+
if (machineAddr) {
|
|
592
|
+
previewLines.push(` • machine: ${machineAddr}`);
|
|
593
|
+
}
|
|
594
|
+
constraintWarnings.push(previewLines.join("\n"));
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
}
|
|
600
|
+
}
|
|
146
601
|
const output = handleCallResult(result, buildCtx(validated.data));
|
|
147
602
|
autoAddToProject(projectEnvPrefix, validated.operation_type, result).catch(() => { });
|
|
148
603
|
return output;
|
|
@@ -157,6 +612,14 @@ export async function handleOnchainOperations(args) {
|
|
|
157
612
|
return output;
|
|
158
613
|
}
|
|
159
614
|
case "arbitration": {
|
|
615
|
+
const arbFundResult = await processFundOperation(validated.data, "arbitration", env?.network);
|
|
616
|
+
constraintWarnings.push(...arbFundResult.warnings);
|
|
617
|
+
if (arbFundResult.confirmation) {
|
|
618
|
+
constraintWarnings.push(arbFundResult.confirmation.message);
|
|
619
|
+
const fundHint = generateFundSkillsHint(arbFundResult.confirmation);
|
|
620
|
+
if (fundHint)
|
|
621
|
+
constraintWarnings.push(fundHint);
|
|
622
|
+
}
|
|
160
623
|
const callArbitration = new CallArbitration(validated.data);
|
|
161
624
|
const result = validated.submission
|
|
162
625
|
? await callArbitration.call_with_submission(env, await transformSubmission(validated.submission))
|
|
@@ -175,6 +638,14 @@ export async function handleOnchainOperations(args) {
|
|
|
175
638
|
return output;
|
|
176
639
|
}
|
|
177
640
|
case "treasury": {
|
|
641
|
+
const trFundResult = await processFundOperation(validated.data, "treasury", env?.network);
|
|
642
|
+
constraintWarnings.push(...trFundResult.warnings);
|
|
643
|
+
if (trFundResult.confirmation) {
|
|
644
|
+
constraintWarnings.push(trFundResult.confirmation.message);
|
|
645
|
+
const fundHint = generateFundSkillsHint(trFundResult.confirmation);
|
|
646
|
+
if (fundHint)
|
|
647
|
+
constraintWarnings.push(fundHint);
|
|
648
|
+
}
|
|
178
649
|
const callTreasury = new CallTreasury(validated.data);
|
|
179
650
|
const result = validated.submission
|
|
180
651
|
? await callTreasury.call_with_submission(env, await transformSubmission(validated.submission))
|
|
@@ -184,6 +655,14 @@ export async function handleOnchainOperations(args) {
|
|
|
184
655
|
return output;
|
|
185
656
|
}
|
|
186
657
|
case "reward": {
|
|
658
|
+
const rwFundResult = await processFundOperation(validated.data, "reward", env?.network);
|
|
659
|
+
constraintWarnings.push(...rwFundResult.warnings);
|
|
660
|
+
if (rwFundResult.confirmation) {
|
|
661
|
+
constraintWarnings.push(rwFundResult.confirmation.message);
|
|
662
|
+
const fundHint = generateFundSkillsHint(rwFundResult.confirmation);
|
|
663
|
+
if (fundHint)
|
|
664
|
+
constraintWarnings.push(fundHint);
|
|
665
|
+
}
|
|
187
666
|
const transformedData = transformRewardInput(validated.data);
|
|
188
667
|
const callReward = new CallReward(transformedData);
|
|
189
668
|
const result = validated.submission
|
|
@@ -198,6 +677,51 @@ export async function handleOnchainOperations(args) {
|
|
|
198
677
|
const result = validated.submission
|
|
199
678
|
? await callAllocation.call_with_submission(env, await transformSubmission(validated.submission))
|
|
200
679
|
: await callAllocation.call(env);
|
|
680
|
+
const allocData = validated.data;
|
|
681
|
+
if (allocData?.alloc_by_guard) {
|
|
682
|
+
const createdPayments = ResponseData(result).filter((e) => e?.type === "Payment" && e?.change === "created");
|
|
683
|
+
if (createdPayments.length > 0) {
|
|
684
|
+
const paymentAddrs = createdPayments.map((p) => p.object).join(", ");
|
|
685
|
+
try {
|
|
686
|
+
const paymentResult = await query_objects({
|
|
687
|
+
objects: createdPayments.map((p) => p.object),
|
|
688
|
+
no_cache: true,
|
|
689
|
+
network: env?.network,
|
|
690
|
+
});
|
|
691
|
+
const distributionLines = [];
|
|
692
|
+
let totalDistributed = BigInt(0);
|
|
693
|
+
for (const obj of paymentResult?.objects || []) {
|
|
694
|
+
if (obj?.type === "Payment" && obj?.record) {
|
|
695
|
+
for (const entry of obj.record) {
|
|
696
|
+
const recipientLabel = formatRecipientLabel(entry.recipient);
|
|
697
|
+
const amountStr = typeof entry.amount === 'string' ? entry.amount : String(entry.amount);
|
|
698
|
+
const amountBig = BigInt(amountStr);
|
|
699
|
+
totalDistributed += amountBig;
|
|
700
|
+
distributionLines.push(` • ${recipientLabel}: ${formatMistAmount(amountBig)}`);
|
|
701
|
+
}
|
|
702
|
+
if (obj.object) {
|
|
703
|
+
distributionLines.push(` • Payment object: ${obj.object}`);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
if (distributionLines.length > 0) {
|
|
708
|
+
distributionLines.unshift(`DISTRIBUTION DETAILS (alloc_by_guard):`);
|
|
709
|
+
distributionLines.push(` ─────────────────`, ` • Total distributed: ${formatMistAmount(totalDistributed)}`);
|
|
710
|
+
const isEscrow = paymentResult?.objects?.some((obj) => obj?.type === "Payment" && obj?.info?.for_object);
|
|
711
|
+
if (isEscrow) {
|
|
712
|
+
distributionLines.push(``, `ESCROW MODE: Funds are held in escrow (Payment.for_object points to an Order). ` +
|
|
713
|
+
`The Order owner must call order_claim (or service_order_claim) to actually receive the funds. ` +
|
|
714
|
+
`This happens when the Guard's sharing config uses { GuardIdentifier: 0 }.`);
|
|
715
|
+
}
|
|
716
|
+
constraintWarnings.push(distributionLines.join("\n"));
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
catch {
|
|
720
|
+
constraintWarnings.push(`alloc_by_guard created Payment object(s) [${paymentAddrs}]. ` +
|
|
721
|
+
`Query these Payment objects to see the distribution details and check for_object for escrow mode.`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
201
725
|
const output = handleCallResult(result, buildCtx(validated.data));
|
|
202
726
|
autoAddToProject(projectEnvPrefix, validated.operation_type, result).catch(() => { });
|
|
203
727
|
return output;
|
|
@@ -259,6 +783,34 @@ export async function handleOnchainOperations(args) {
|
|
|
259
783
|
}
|
|
260
784
|
const callGuard = new CallGuard(validatedData);
|
|
261
785
|
const result = await callGuard.call(env);
|
|
786
|
+
if (result && typeof result === 'object' && 'digest' in result
|
|
787
|
+
&& result?.effects?.status?.status === 'success') {
|
|
788
|
+
try {
|
|
789
|
+
const rootType = validatedData.root?.type ?? "<unknown>";
|
|
790
|
+
const tableArr = Array.isArray(validatedData.table) ? validatedData.table : [];
|
|
791
|
+
const tableEntries = tableArr.length;
|
|
792
|
+
const submissionEntries = tableArr.filter((t) => t?.b_submission === true).length;
|
|
793
|
+
const relyGuards = validatedData.rely?.guards;
|
|
794
|
+
const relyCount = Array.isArray(relyGuards) ? relyGuards.length : 0;
|
|
795
|
+
const relyMode = validatedData.rely?.logic_or === true
|
|
796
|
+
? "OR" : validatedData.rely?.logic_or === false ? "AND" : "none";
|
|
797
|
+
const summaryLines = [
|
|
798
|
+
`GUARD LOGIC SUMMARY:`,
|
|
799
|
+
` • root type: ${rootType}`,
|
|
800
|
+
` • table entries: ${tableEntries} (${submissionEntries} require runtime submission)`,
|
|
801
|
+
];
|
|
802
|
+
if (relyCount > 0) {
|
|
803
|
+
summaryLines.push(` • relies on: ${relyCount} guard(s) (mode: ${relyMode})`);
|
|
804
|
+
}
|
|
805
|
+
if (validatedData.description) {
|
|
806
|
+
const desc = String(validatedData.description).slice(0, 80);
|
|
807
|
+
summaryLines.push(` • description: "${desc}"`);
|
|
808
|
+
}
|
|
809
|
+
constraintWarnings.push(summaryLines.join("\n"));
|
|
810
|
+
}
|
|
811
|
+
catch {
|
|
812
|
+
}
|
|
813
|
+
}
|
|
262
814
|
const output = handleCallResult(result, buildCtx(validated.data));
|
|
263
815
|
autoAddToProject(projectEnvPrefix, validated.operation_type, result).catch(() => { });
|
|
264
816
|
return output;
|
|
@@ -272,6 +824,37 @@ export async function handleOnchainOperations(args) {
|
|
|
272
824
|
return output;
|
|
273
825
|
}
|
|
274
826
|
case "payment": {
|
|
827
|
+
const payFundResult = await processFundOperation(validated.data, "payment", env?.network);
|
|
828
|
+
constraintWarnings.push(...payFundResult.warnings);
|
|
829
|
+
if (payFundResult.confirmation) {
|
|
830
|
+
constraintWarnings.push(payFundResult.confirmation.message);
|
|
831
|
+
const fundHint = generateFundSkillsHint(payFundResult.confirmation);
|
|
832
|
+
if (fundHint)
|
|
833
|
+
constraintWarnings.push(fundHint);
|
|
834
|
+
}
|
|
835
|
+
const paymentData = validated.data;
|
|
836
|
+
if (paymentData?.receive === true) {
|
|
837
|
+
constraintWarnings.push("RECEIVE MODE (order_claim): You are unwrapping a CoinWrapper to your wallet. " +
|
|
838
|
+
"This is the final step of the escrow flow:\n" +
|
|
839
|
+
" 1. Allocation's alloc_by_guard created a Payment object with your address as a recipient\n" +
|
|
840
|
+
" 2. The Payment's revenue split transferred a CoinWrapper to your wallet (owned but not spendable)\n" +
|
|
841
|
+
" 3. THIS operation unwraps the CoinWrapper via payment::unwrap_to_myself, deleting the " +
|
|
842
|
+
"CoinWrapper and transferring the underlying coin to tx_context::sender\n" +
|
|
843
|
+
"Requirements:\n" +
|
|
844
|
+
" - You MUST be the CoinWrapper's owner (the recipient specified in the Allocation)\n" +
|
|
845
|
+
" - type_parameter MUST match the coin type used when the Allocation created the Payment " +
|
|
846
|
+
"(e.g. '0x2::wow::WOW')\n" +
|
|
847
|
+
" - object can be a CoinWrapper ID (0x...) or a local mark name\n" +
|
|
848
|
+
"To find received CoinWrappers: call query_toolkit with query_type='onchain_received' " +
|
|
849
|
+
"and type='CoinWrapper' for your account.\n" +
|
|
850
|
+
"After success: verify your balance increased via account_balance query.");
|
|
851
|
+
}
|
|
852
|
+
else if (paymentData?.revenue) {
|
|
853
|
+
constraintWarnings.push("CREATE MODE: Creating a new Payment object. Payments are IMMUTABLE after creation — " +
|
|
854
|
+
"they cannot be modified. The 'revenue' array defines the recipients and amounts; " +
|
|
855
|
+
"ensure correctness before submitting. The 'info.for_guard' field (if set) binds this " +
|
|
856
|
+
"Payment to a Guard for conditional release (common in escrow workflows).");
|
|
857
|
+
}
|
|
275
858
|
const callPayment = new CallPayment(validated.data);
|
|
276
859
|
const result = await callPayment.call(env);
|
|
277
860
|
const output = handleCallResult(result, buildCtx(validated.data));
|
|
@@ -293,6 +876,11 @@ export async function handleOnchainOperations(args) {
|
|
|
293
876
|
"namedOperator=\"\" (OrderHolder). If the target Forward uses a " +
|
|
294
877
|
"non-empty namedOperator or permissionIndex-only, use progress.operate " +
|
|
295
878
|
"on the Progress object directly instead.");
|
|
879
|
+
constraintWarnings.push("Allocation hint: if this operation advances the order to a terminal " +
|
|
880
|
+
"state (e.g. Completed/Cancelled/Refunded), funds remain in the " +
|
|
881
|
+
"Allocation object and are NOT auto-distributed. To release funds, " +
|
|
882
|
+
"trigger the corresponding Guard verification via allocation operation " +
|
|
883
|
+
"(the Guard bound to the matching Allocator will verify and distribute).");
|
|
296
884
|
}
|
|
297
885
|
const callOrder = new CallOrder(validated.data);
|
|
298
886
|
const result = validated.submission
|