@mysten/pas 0.0.1
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 +1 -0
- package/dist/client.d.mts +117 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +89 -0
- package/dist/client.mjs.map +1 -0
- package/dist/constants.mjs +9 -0
- package/dist/constants.mjs.map +1 -0
- package/dist/contracts/pas/deps/std/type_name.mjs +17 -0
- package/dist/contracts/pas/deps/std/type_name.mjs.map +1 -0
- package/dist/contracts/pas/deps/sui/vec_map.mjs +37 -0
- package/dist/contracts/pas/deps/sui/vec_map.mjs.map +1 -0
- package/dist/contracts/pas/deps/sui/vec_set.mjs +26 -0
- package/dist/contracts/pas/deps/sui/vec_set.mjs.map +1 -0
- package/dist/contracts/pas/policy.mjs +33 -0
- package/dist/contracts/pas/policy.mjs.map +1 -0
- package/dist/contracts/pas/versioning.mjs +25 -0
- package/dist/contracts/pas/versioning.mjs.map +1 -0
- package/dist/contracts/ptb/ptb.mjs +162 -0
- package/dist/contracts/ptb/ptb.mjs.map +1 -0
- package/dist/contracts/sui/dynamic_field.mjs +22 -0
- package/dist/contracts/sui/dynamic_field.mjs.map +1 -0
- package/dist/contracts/utils/index.mjs +37 -0
- package/dist/contracts/utils/index.mjs.map +1 -0
- package/dist/derivation.mjs +70 -0
- package/dist/derivation.mjs.map +1 -0
- package/dist/error.d.mts +16 -0
- package/dist/error.d.mts.map +1 -0
- package/dist/error.mjs +26 -0
- package/dist/error.mjs.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +4 -0
- package/dist/intents.mjs +494 -0
- package/dist/intents.mjs.map +1 -0
- package/dist/resolution.mjs +185 -0
- package/dist/resolution.mjs.map +1 -0
- package/dist/types.d.mts +34 -0
- package/dist/types.d.mts.map +1 -0
- package/package.json +59 -0
- package/src/client.ts +173 -0
- package/src/constants.ts +15 -0
- package/src/contracts/pas/account.ts +343 -0
- package/src/contracts/pas/clawback_funds.ts +114 -0
- package/src/contracts/pas/deps/std/type_name.ts +24 -0
- package/src/contracts/pas/deps/sui/vec_map.ts +33 -0
- package/src/contracts/pas/deps/sui/vec_set.ts +22 -0
- package/src/contracts/pas/keys.ts +90 -0
- package/src/contracts/pas/namespace.ts +207 -0
- package/src/contracts/pas/policy.ts +212 -0
- package/src/contracts/pas/request.ts +87 -0
- package/src/contracts/pas/send_funds.ts +174 -0
- package/src/contracts/pas/templates.ts +101 -0
- package/src/contracts/pas/unlock_funds.ts +155 -0
- package/src/contracts/pas/versioning.ts +69 -0
- package/src/contracts/ptb/ptb.ts +821 -0
- package/src/contracts/sui/dynamic_field.ts +171 -0
- package/src/contracts/utils/index.ts +235 -0
- package/src/derivation.ts +107 -0
- package/src/error.ts +29 -0
- package/src/index.ts +6 -0
- package/src/intents.ts +852 -0
- package/src/resolution.ts +294 -0
- package/src/types.ts +34 -0
package/dist/intents.mjs
ADDED
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
import { deriveAccountAddress, derivePolicyAddress, deriveTemplateAddress, deriveTemplateRegistryAddress } from "./derivation.mjs";
|
|
2
|
+
import { InvalidObjectOwnershipError, PASClientError, PolicyNotFoundError } from "./error.mjs";
|
|
3
|
+
import { buildMoveCallCommandFromTemplate, collectTemplateObjectIds, getCommandFromTemplate, getRequiredApprovals } from "./resolution.mjs";
|
|
4
|
+
import { bcs } from "@mysten/sui/bcs";
|
|
5
|
+
import { normalizeStructTag } from "@mysten/sui/utils";
|
|
6
|
+
import { Inputs, TransactionCommands } from "@mysten/sui/transactions";
|
|
7
|
+
|
|
8
|
+
//#region src/intents.ts
|
|
9
|
+
const PAS_INTENT_NAME = "PAS";
|
|
10
|
+
/**
|
|
11
|
+
* Creates a memoized PAS intent closure. On first call it registers the
|
|
12
|
+
* shared resolver and adds the $Intent command; subsequent calls return
|
|
13
|
+
* the cached TransactionResult.
|
|
14
|
+
*/
|
|
15
|
+
function createPASIntent(data) {
|
|
16
|
+
let result = null;
|
|
17
|
+
return (tx) => {
|
|
18
|
+
if (result) return result;
|
|
19
|
+
tx.addIntentResolver(PAS_INTENT_NAME, resolvePASIntents);
|
|
20
|
+
result = tx.add(TransactionCommands.Intent({
|
|
21
|
+
name: PAS_INTENT_NAME,
|
|
22
|
+
inputs: {},
|
|
23
|
+
data
|
|
24
|
+
}));
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function sendBalanceIntent(packageConfig) {
|
|
29
|
+
return ({ from, to, amount, assetType }) => createPASIntent({
|
|
30
|
+
action: "sendBalance",
|
|
31
|
+
from,
|
|
32
|
+
to,
|
|
33
|
+
amount: String(amount),
|
|
34
|
+
assetType,
|
|
35
|
+
cfg: packageConfig
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function unlockBalanceIntent(packageConfig) {
|
|
39
|
+
return ({ from, amount, assetType }) => createPASIntent({
|
|
40
|
+
action: "unlockBalance",
|
|
41
|
+
from,
|
|
42
|
+
amount: String(amount),
|
|
43
|
+
assetType,
|
|
44
|
+
cfg: packageConfig
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function unlockUnrestrictedBalanceIntent(packageConfig) {
|
|
48
|
+
return ({ from, amount, assetType }) => createPASIntent({
|
|
49
|
+
action: "unlockUnrestrictedBalance",
|
|
50
|
+
from,
|
|
51
|
+
amount: String(amount),
|
|
52
|
+
assetType,
|
|
53
|
+
cfg: packageConfig
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function accountForAddressIntent(packageConfig) {
|
|
57
|
+
return (owner) => createPASIntent({
|
|
58
|
+
action: "accountForAddress",
|
|
59
|
+
owner,
|
|
60
|
+
cfg: packageConfig
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
var Resolver = class {
|
|
64
|
+
#tx;
|
|
65
|
+
#inputCache = /* @__PURE__ */ new Map();
|
|
66
|
+
#templateCommandsCache = /* @__PURE__ */ new Map();
|
|
67
|
+
#config;
|
|
68
|
+
constructor({ transactionData, objects, templates, templateApprovals, accounts, config }) {
|
|
69
|
+
this.#tx = transactionData;
|
|
70
|
+
this.objects = objects;
|
|
71
|
+
this.templates = templates;
|
|
72
|
+
this.templateApprovals = templateApprovals;
|
|
73
|
+
this.accounts = accounts;
|
|
74
|
+
this.#config = config;
|
|
75
|
+
}
|
|
76
|
+
addObjectInput(objectId) {
|
|
77
|
+
let arg = this.#inputCache.get(objectId);
|
|
78
|
+
if (!arg) {
|
|
79
|
+
arg = this.#tx.addInput("object", {
|
|
80
|
+
$kind: "UnresolvedObject",
|
|
81
|
+
UnresolvedObject: { objectId }
|
|
82
|
+
});
|
|
83
|
+
this.#inputCache.set(objectId, arg);
|
|
84
|
+
}
|
|
85
|
+
return arg;
|
|
86
|
+
}
|
|
87
|
+
addPureInput(key, value) {
|
|
88
|
+
let arg = this.#inputCache.get(key);
|
|
89
|
+
if (!arg) {
|
|
90
|
+
arg = this.#tx.addInput("pure", value);
|
|
91
|
+
this.#inputCache.set(key, arg);
|
|
92
|
+
}
|
|
93
|
+
return arg;
|
|
94
|
+
}
|
|
95
|
+
addTemplateInput(type, arg) {
|
|
96
|
+
if (type === "object" && arg.$kind === "UnresolvedObject") return this.addObjectInput(arg.UnresolvedObject.objectId);
|
|
97
|
+
return this.#tx.addInput(type, arg);
|
|
98
|
+
}
|
|
99
|
+
getObjectOrThrow(objectId, errorFactory) {
|
|
100
|
+
const obj = this.objects.get(objectId);
|
|
101
|
+
if (!obj) throw errorFactory();
|
|
102
|
+
return obj;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Returns an Argument referencing the account for `accountId`.
|
|
106
|
+
*
|
|
107
|
+
* - Existing on-chain account: returns an object Input.
|
|
108
|
+
* - Already created earlier in this PTB: returns the stored Result ref.
|
|
109
|
+
* - Does not exist yet: **pushes** a `account::create` MoveCall into the
|
|
110
|
+
* caller's `commands` array (mutating it) and records the creation so
|
|
111
|
+
* subsequent calls for the same account reuse the same Result. The account
|
|
112
|
+
* will be shared at the end of the PTB via `shareNewAccounts()`.
|
|
113
|
+
*
|
|
114
|
+
* @param commands - The caller's local command array (may be mutated).
|
|
115
|
+
* @param baseIdx - Absolute PTB index where `commands[0]` will land.
|
|
116
|
+
*/
|
|
117
|
+
resolveAccountArg(accountId, owner, baseIdx) {
|
|
118
|
+
const state = this.accounts.get(accountId);
|
|
119
|
+
const commands = [];
|
|
120
|
+
if (state?.kind === "existing") return [this.addObjectInput(accountId), commands];
|
|
121
|
+
if (state?.kind === "created") return [{
|
|
122
|
+
$kind: "Result",
|
|
123
|
+
Result: state.resultIndex
|
|
124
|
+
}, commands];
|
|
125
|
+
const absoluteIndex = baseIdx + commands.length;
|
|
126
|
+
commands.push(TransactionCommands.MoveCall({
|
|
127
|
+
package: this.#config.packageId,
|
|
128
|
+
module: "account",
|
|
129
|
+
function: "create",
|
|
130
|
+
arguments: [this.addObjectInput(this.#config.namespaceId), this.addPureInput(`address:${owner}`, Inputs.Pure(bcs.Address.serialize(owner)))]
|
|
131
|
+
}));
|
|
132
|
+
this.accounts.set(accountId, {
|
|
133
|
+
kind: "created",
|
|
134
|
+
resultIndex: absoluteIndex
|
|
135
|
+
});
|
|
136
|
+
return [{
|
|
137
|
+
$kind: "Result",
|
|
138
|
+
Result: absoluteIndex
|
|
139
|
+
}, commands];
|
|
140
|
+
}
|
|
141
|
+
resolveTemplateCommands(policyObjectId, actionType) {
|
|
142
|
+
const cacheKey = `${policyObjectId}:${actionType}`;
|
|
143
|
+
const cached = this.#templateCommandsCache.get(cacheKey);
|
|
144
|
+
if (cached) return cached;
|
|
145
|
+
const approvalTypeNames = this.templateApprovals.get(cacheKey);
|
|
146
|
+
if (!approvalTypeNames) throw new PASClientError(`No required approvals found for action "${actionType}". The issuer has not configured this action.`);
|
|
147
|
+
const templatesId = deriveTemplateRegistryAddress(this.#config);
|
|
148
|
+
const commands = approvalTypeNames.map((tn) => {
|
|
149
|
+
const templateId = deriveTemplateAddress(templatesId, tn);
|
|
150
|
+
const template = this.templates.get(templateId);
|
|
151
|
+
if (!template) throw new PASClientError(`Template not found for approval type "${tn}". The issuer has not set up the template command.`);
|
|
152
|
+
return getCommandFromTemplate(template);
|
|
153
|
+
});
|
|
154
|
+
this.#templateCommandsCache.set(cacheKey, commands);
|
|
155
|
+
return commands;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Replaces a standard action intent (transfer/unlock) with its built
|
|
159
|
+
* commands. The resolve call at `actualIdx + resultOffset` produces the
|
|
160
|
+
* intent's output value.
|
|
161
|
+
*/
|
|
162
|
+
replaceIntent(actualIdx, commands, resultOffset) {
|
|
163
|
+
this.#tx.replaceCommand(actualIdx, commands, { Result: actualIdx + resultOffset });
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Replaces a accountForAddress intent when the account already exists.
|
|
167
|
+
* The intent is removed (0 replacement commands) and external references
|
|
168
|
+
* are remapped to the existing account's Input argument.
|
|
169
|
+
*
|
|
170
|
+
* Note: SDK's replaceCommand signature doesn't accept Input args as
|
|
171
|
+
* resultIndex, but the runtime handles it correctly via ArgumentSchema.parse().
|
|
172
|
+
*/
|
|
173
|
+
replaceIntentWithExistingAccount(actualIdx, accountArg) {
|
|
174
|
+
this.#tx.replaceCommand(actualIdx, [], accountArg);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Replaces a accountForAddress intent when the account needs to be created.
|
|
178
|
+
* The intent is replaced with the account::create command(s), and external
|
|
179
|
+
* references are remapped to the first command's Result (the new account).
|
|
180
|
+
*/
|
|
181
|
+
replaceIntentWithCreatedAccount(actualIdx, commands) {
|
|
182
|
+
this.#tx.replaceCommand(actualIdx, commands, { Result: actualIdx });
|
|
183
|
+
}
|
|
184
|
+
buildSendBalance(data, baseIdx) {
|
|
185
|
+
const { from, to, assetType, amount } = data;
|
|
186
|
+
const fromAccountId = deriveAccountAddress(from, this.#config);
|
|
187
|
+
const toAccountId = deriveAccountAddress(to, this.#config);
|
|
188
|
+
const policyId = derivePolicyAddress(assetType, this.#config);
|
|
189
|
+
this.getObjectOrThrow(policyId, () => new PolicyNotFoundError(assetType));
|
|
190
|
+
const templateCmds = this.resolveTemplateCommands(policyId, "send_funds");
|
|
191
|
+
const [toAccountArg, commands] = this.resolveAccountArg(toAccountId, to, baseIdx);
|
|
192
|
+
const [fromAccountArg, fromAccountCommands] = this.resolveAccountArg(fromAccountId, from, baseIdx + commands.length);
|
|
193
|
+
commands.push(...fromAccountCommands);
|
|
194
|
+
const policyArg = this.addObjectInput(policyId);
|
|
195
|
+
const authIdx = baseIdx + commands.length;
|
|
196
|
+
commands.push(TransactionCommands.MoveCall({
|
|
197
|
+
package: this.#config.packageId,
|
|
198
|
+
module: "account",
|
|
199
|
+
function: "new_auth"
|
|
200
|
+
}));
|
|
201
|
+
const requestIdx = baseIdx + commands.length;
|
|
202
|
+
commands.push(TransactionCommands.MoveCall({
|
|
203
|
+
package: this.#config.packageId,
|
|
204
|
+
module: "account",
|
|
205
|
+
function: "send_balance",
|
|
206
|
+
arguments: [
|
|
207
|
+
fromAccountArg,
|
|
208
|
+
{
|
|
209
|
+
$kind: "Result",
|
|
210
|
+
Result: authIdx
|
|
211
|
+
},
|
|
212
|
+
toAccountArg,
|
|
213
|
+
this.addTemplateInput("pure", Inputs.Pure(bcs.u64().serialize(BigInt(amount))))
|
|
214
|
+
],
|
|
215
|
+
typeArguments: [normalizeStructTag(assetType)]
|
|
216
|
+
}));
|
|
217
|
+
const requestArg = {
|
|
218
|
+
$kind: "Result",
|
|
219
|
+
Result: requestIdx
|
|
220
|
+
};
|
|
221
|
+
const templateStartIdx = baseIdx + commands.length;
|
|
222
|
+
for (const templateCmd of templateCmds) commands.push(buildMoveCallCommandFromTemplate(templateCmd, {
|
|
223
|
+
addInput: (type, arg) => this.addTemplateInput(type, arg),
|
|
224
|
+
senderAccount: fromAccountArg,
|
|
225
|
+
receiverAccount: toAccountArg,
|
|
226
|
+
policy: policyArg,
|
|
227
|
+
request: requestArg
|
|
228
|
+
}, templateStartIdx));
|
|
229
|
+
const resultOffset = commands.length;
|
|
230
|
+
commands.push(TransactionCommands.MoveCall({
|
|
231
|
+
package: this.#config.packageId,
|
|
232
|
+
module: "send_funds",
|
|
233
|
+
function: "resolve_balance",
|
|
234
|
+
arguments: [requestArg, policyArg],
|
|
235
|
+
typeArguments: [normalizeStructTag(assetType)]
|
|
236
|
+
}));
|
|
237
|
+
return {
|
|
238
|
+
commands,
|
|
239
|
+
resultOffset
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Builds commands for both restricted and unrestricted unlock flows.
|
|
244
|
+
* Restricted: requires a Policy, runs issuer approval templates, then resolve.
|
|
245
|
+
* Unrestricted: no Policy needed, calls resolve_unrestricted_balance directly.
|
|
246
|
+
*/
|
|
247
|
+
buildUnlockBalance(data, baseIdx) {
|
|
248
|
+
const { from, assetType, amount } = data;
|
|
249
|
+
const fromAccountId = deriveAccountAddress(from, this.#config);
|
|
250
|
+
const policyId = derivePolicyAddress(assetType, this.#config);
|
|
251
|
+
const isRestricted = data.action === "unlockBalance";
|
|
252
|
+
if (isRestricted) this.getObjectOrThrow(policyId, () => new PASClientError(`Policy does not exist for asset type ${assetType}. That means that the issuer has not yet enabled funds management for this asset. If this is a non-managed asset, you can use the unrestricted unlock flow by calling unlockUnrestrictedBalance() instead.`));
|
|
253
|
+
else if (this.objects.get(policyId) !== null) throw new PASClientError(`A policy exists for asset type ${assetType}. That means that the issuer has enabled funds management for this asset and you can no longer use the unrestricted unlock flow.`);
|
|
254
|
+
const [fromAccountArg, commands] = this.resolveAccountArg(fromAccountId, from, baseIdx);
|
|
255
|
+
const policyArg = isRestricted ? this.addObjectInput(policyId) : void 0;
|
|
256
|
+
const authIdx = baseIdx + commands.length;
|
|
257
|
+
commands.push(TransactionCommands.MoveCall({
|
|
258
|
+
package: this.#config.packageId,
|
|
259
|
+
module: "account",
|
|
260
|
+
function: "new_auth"
|
|
261
|
+
}));
|
|
262
|
+
const requestIdx = baseIdx + commands.length;
|
|
263
|
+
commands.push(TransactionCommands.MoveCall({
|
|
264
|
+
package: this.#config.packageId,
|
|
265
|
+
module: "account",
|
|
266
|
+
function: "unlock_balance",
|
|
267
|
+
arguments: [
|
|
268
|
+
fromAccountArg,
|
|
269
|
+
{
|
|
270
|
+
$kind: "Result",
|
|
271
|
+
Result: authIdx
|
|
272
|
+
},
|
|
273
|
+
this.addTemplateInput("pure", Inputs.Pure(bcs.u64().serialize(BigInt(amount))))
|
|
274
|
+
],
|
|
275
|
+
typeArguments: [normalizeStructTag(assetType)]
|
|
276
|
+
}));
|
|
277
|
+
const requestArg = {
|
|
278
|
+
$kind: "Result",
|
|
279
|
+
Result: requestIdx
|
|
280
|
+
};
|
|
281
|
+
if (isRestricted) {
|
|
282
|
+
const templateCmds = this.resolveTemplateCommands(policyId, "unlock_funds");
|
|
283
|
+
const templateStartIdx = baseIdx + commands.length;
|
|
284
|
+
for (const templateCmd of templateCmds) commands.push(buildMoveCallCommandFromTemplate(templateCmd, {
|
|
285
|
+
addInput: (type, arg) => this.addTemplateInput(type, arg),
|
|
286
|
+
senderAccount: fromAccountArg,
|
|
287
|
+
policy: policyArg,
|
|
288
|
+
request: requestArg
|
|
289
|
+
}, templateStartIdx));
|
|
290
|
+
const resultOffset$1 = commands.length;
|
|
291
|
+
commands.push(TransactionCommands.MoveCall({
|
|
292
|
+
package: this.#config.packageId,
|
|
293
|
+
module: "unlock_funds",
|
|
294
|
+
function: "resolve",
|
|
295
|
+
arguments: [requestArg, policyArg],
|
|
296
|
+
typeArguments: [normalizeStructTag(assetType)]
|
|
297
|
+
}));
|
|
298
|
+
return {
|
|
299
|
+
commands,
|
|
300
|
+
resultOffset: resultOffset$1
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const resultOffset = commands.length;
|
|
304
|
+
commands.push(TransactionCommands.MoveCall({
|
|
305
|
+
package: this.#config.packageId,
|
|
306
|
+
module: "unlock_funds",
|
|
307
|
+
function: "resolve_unrestricted_balance",
|
|
308
|
+
arguments: [requestArg, this.addObjectInput(this.#config.namespaceId)],
|
|
309
|
+
typeArguments: [normalizeStructTag(assetType)]
|
|
310
|
+
}));
|
|
311
|
+
return {
|
|
312
|
+
commands,
|
|
313
|
+
resultOffset
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Appends `account::share` commands for every account that was created during
|
|
318
|
+
* resolution. Called once at the end, after all intents have been resolved,
|
|
319
|
+
* so that each account is shared exactly once regardless of how many intents
|
|
320
|
+
* referenced it.
|
|
321
|
+
*/
|
|
322
|
+
shareNewAccounts() {
|
|
323
|
+
for (const state of this.accounts.values()) {
|
|
324
|
+
if (state.kind !== "created") continue;
|
|
325
|
+
this.#tx.commands.push(TransactionCommands.MoveCall({
|
|
326
|
+
package: this.#config.packageId,
|
|
327
|
+
module: "account",
|
|
328
|
+
function: "share",
|
|
329
|
+
arguments: [{
|
|
330
|
+
$kind: "Result",
|
|
331
|
+
Result: state.resultIndex
|
|
332
|
+
}]
|
|
333
|
+
}));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
/** Scans commands for PAS intents and collects the object IDs we need to fetch. */
|
|
338
|
+
function collectIntentData(commands) {
|
|
339
|
+
const objectIds = /* @__PURE__ */ new Set();
|
|
340
|
+
const accountRequests = /* @__PURE__ */ new Map();
|
|
341
|
+
const intentDataList = [];
|
|
342
|
+
let cfg = null;
|
|
343
|
+
for (const command of commands) {
|
|
344
|
+
if (command.$kind !== "$Intent" || command.$Intent.name !== PAS_INTENT_NAME) continue;
|
|
345
|
+
const data = command.$Intent.data;
|
|
346
|
+
if (!cfg) cfg = data.cfg;
|
|
347
|
+
intentDataList.push(data);
|
|
348
|
+
switch (data.action) {
|
|
349
|
+
case "sendBalance": {
|
|
350
|
+
const fromId = deriveAccountAddress(data.from, cfg);
|
|
351
|
+
const toId = deriveAccountAddress(data.to, cfg);
|
|
352
|
+
objectIds.add(fromId);
|
|
353
|
+
objectIds.add(toId);
|
|
354
|
+
objectIds.add(derivePolicyAddress(data.assetType, cfg));
|
|
355
|
+
accountRequests.set(fromId, { owner: data.from });
|
|
356
|
+
accountRequests.set(toId, { owner: data.to });
|
|
357
|
+
break;
|
|
358
|
+
}
|
|
359
|
+
case "unlockBalance":
|
|
360
|
+
case "unlockUnrestrictedBalance": {
|
|
361
|
+
const fromId = deriveAccountAddress(data.from, cfg);
|
|
362
|
+
objectIds.add(fromId);
|
|
363
|
+
objectIds.add(derivePolicyAddress(data.assetType, cfg));
|
|
364
|
+
accountRequests.set(fromId, { owner: data.from });
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
case "accountForAddress": {
|
|
368
|
+
const id = deriveAccountAddress(data.owner, cfg);
|
|
369
|
+
objectIds.add(id);
|
|
370
|
+
accountRequests.set(id, { owner: data.owner });
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (intentDataList.length === 0) return null;
|
|
376
|
+
if (!cfg) throw new PASClientError("No package configuration found in intents. This is an internal bug.");
|
|
377
|
+
return {
|
|
378
|
+
objectIds,
|
|
379
|
+
accountRequests,
|
|
380
|
+
intentDataList,
|
|
381
|
+
cfg
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
async function initializeContext(transactionData, client, objectIds, accountRequests, intentDataList, config) {
|
|
385
|
+
const allIds = [...objectIds];
|
|
386
|
+
const { objects: fetched } = await client.core.getObjects({
|
|
387
|
+
objectIds: allIds,
|
|
388
|
+
include: { content: true }
|
|
389
|
+
});
|
|
390
|
+
const objects = /* @__PURE__ */ new Map();
|
|
391
|
+
for (const id of allIds) {
|
|
392
|
+
const obj = fetched.filter((o) => "content" in o).find((o) => o.objectId === id);
|
|
393
|
+
objects.set(id, obj ?? null);
|
|
394
|
+
}
|
|
395
|
+
const accounts = /* @__PURE__ */ new Map();
|
|
396
|
+
for (const [accountId] of accountRequests) if (objects.get(accountId) !== null) accounts.set(accountId, { kind: "existing" });
|
|
397
|
+
const templateApprovals = /* @__PURE__ */ new Map();
|
|
398
|
+
const templateIds = [];
|
|
399
|
+
const seen = /* @__PURE__ */ new Set();
|
|
400
|
+
for (const data of intentDataList) {
|
|
401
|
+
let actionType = null;
|
|
402
|
+
let assetType = null;
|
|
403
|
+
if (data.action === "sendBalance") {
|
|
404
|
+
actionType = "send_funds";
|
|
405
|
+
assetType = data.assetType;
|
|
406
|
+
} else if (data.action === "unlockBalance") {
|
|
407
|
+
actionType = "unlock_funds";
|
|
408
|
+
assetType = data.assetType;
|
|
409
|
+
}
|
|
410
|
+
if (!actionType || !assetType) continue;
|
|
411
|
+
const policyId = derivePolicyAddress(assetType, config);
|
|
412
|
+
const key = `${policyId}:${actionType}`;
|
|
413
|
+
if (seen.has(key)) continue;
|
|
414
|
+
seen.add(key);
|
|
415
|
+
const policyObject = objects.get(policyId);
|
|
416
|
+
if (!policyObject) continue;
|
|
417
|
+
const approvalTypeNames = getRequiredApprovals(policyObject, actionType);
|
|
418
|
+
if (!approvalTypeNames?.length) continue;
|
|
419
|
+
const templatesId = deriveTemplateRegistryAddress(config);
|
|
420
|
+
templateApprovals.set(key, approvalTypeNames);
|
|
421
|
+
templateIds.push(...approvalTypeNames.map((tn) => deriveTemplateAddress(templatesId, tn)));
|
|
422
|
+
}
|
|
423
|
+
const templates = /* @__PURE__ */ new Map();
|
|
424
|
+
if (templateIds.length > 0) {
|
|
425
|
+
const { objects: templateObjects } = await client.core.getObjects({
|
|
426
|
+
objectIds: templateIds,
|
|
427
|
+
include: { content: true }
|
|
428
|
+
});
|
|
429
|
+
for (const obj of templateObjects.filter((o) => "content" in o)) templates.set(obj.objectId, obj);
|
|
430
|
+
}
|
|
431
|
+
await validateTemplateObjects(client, Array.from(templates.values()));
|
|
432
|
+
return new Resolver({
|
|
433
|
+
transactionData,
|
|
434
|
+
objects,
|
|
435
|
+
templates,
|
|
436
|
+
templateApprovals,
|
|
437
|
+
accounts,
|
|
438
|
+
config
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
const resolvePASIntents = async (transactionData, buildOptions, next) => {
|
|
442
|
+
const client = buildOptions.client;
|
|
443
|
+
if (!client) throw new PASClientError("A SuiClient must be provided to build transactions with PAS intents.");
|
|
444
|
+
const requirements = collectIntentData(transactionData.commands);
|
|
445
|
+
if (!requirements) return next();
|
|
446
|
+
const { objectIds, accountRequests, intentDataList, cfg } = requirements;
|
|
447
|
+
const ctx = await initializeContext(transactionData, client, objectIds, accountRequests, intentDataList, cfg);
|
|
448
|
+
for (let index = 0; index < transactionData.commands.length; index++) {
|
|
449
|
+
const command = transactionData.commands[index];
|
|
450
|
+
if (command.$kind !== "$Intent" || command.$Intent.name !== PAS_INTENT_NAME) continue;
|
|
451
|
+
const data = command.$Intent.data;
|
|
452
|
+
if (data.action === "accountForAddress") {
|
|
453
|
+
const accountId = deriveAccountAddress(data.owner, cfg);
|
|
454
|
+
const [accountArg, commands] = ctx.resolveAccountArg(accountId, data.owner, index);
|
|
455
|
+
if (commands.length === 0) {
|
|
456
|
+
ctx.replaceIntentWithExistingAccount(index, accountArg);
|
|
457
|
+
index--;
|
|
458
|
+
} else ctx.replaceIntentWithCreatedAccount(index, commands);
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
let result;
|
|
462
|
+
switch (data.action) {
|
|
463
|
+
case "sendBalance":
|
|
464
|
+
result = ctx.buildSendBalance(data, index);
|
|
465
|
+
break;
|
|
466
|
+
case "unlockBalance":
|
|
467
|
+
case "unlockUnrestrictedBalance":
|
|
468
|
+
result = ctx.buildUnlockBalance(data, index);
|
|
469
|
+
break;
|
|
470
|
+
default: continue;
|
|
471
|
+
}
|
|
472
|
+
ctx.replaceIntent(index, result.commands, result.resultOffset);
|
|
473
|
+
}
|
|
474
|
+
ctx.shareNewAccounts();
|
|
475
|
+
return next();
|
|
476
|
+
};
|
|
477
|
+
/**
|
|
478
|
+
* Parses all template commands, collects every object they reference
|
|
479
|
+
* (fully-resolved refs, shared refs, and ext lookups), batch-fetches
|
|
480
|
+
* their current state, and rejects any that are not shared or immutable.
|
|
481
|
+
*/
|
|
482
|
+
async function validateTemplateObjects(client, templates) {
|
|
483
|
+
const objectIds = collectTemplateObjectIds(templates.map(getCommandFromTemplate));
|
|
484
|
+
if (objectIds.size === 0) return;
|
|
485
|
+
const { objects: fetchedObjects } = await client.core.getObjects({ objectIds: [...objectIds] });
|
|
486
|
+
for (const obj of fetchedObjects) {
|
|
487
|
+
if (obj instanceof Error) throw new PASClientError("Failed to fetch template object: " + obj.message);
|
|
488
|
+
if (obj.owner.$kind !== "Shared" && obj.owner.$kind !== "Immutable") throw new InvalidObjectOwnershipError(obj.objectId, obj.owner.$kind);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
//#endregion
|
|
493
|
+
export { accountForAddressIntent, sendBalanceIntent, unlockBalanceIntent, unlockUnrestrictedBalanceIntent };
|
|
494
|
+
//# sourceMappingURL=intents.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intents.mjs","names":["#tx","#inputCache","#templateCommandsCache","#config","resultOffset"],"sources":["../src/intents.ts"],"sourcesContent":["// Copyright (c) Mysten Labs, Inc.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { bcs } from '@mysten/sui/bcs';\nimport type { ClientWithCoreApi, SuiClientTypes } from '@mysten/sui/client';\nimport { Inputs, Transaction, TransactionCommands } from '@mysten/sui/transactions';\nimport type {\n\tArgument,\n\tCallArg,\n\tCommand,\n\tTransactionDataBuilder,\n\tTransactionPlugin,\n\tTransactionResult,\n} from '@mysten/sui/transactions';\nimport { normalizeStructTag } from '@mysten/sui/utils';\n\nimport {\n\tderiveAccountAddress,\n\tderivePolicyAddress,\n\tderiveTemplateAddress,\n\tderiveTemplateRegistryAddress,\n} from './derivation.js';\nimport { InvalidObjectOwnershipError, PASClientError, PolicyNotFoundError } from './error.js';\nimport {\n\tbuildMoveCallCommandFromTemplate,\n\tcollectTemplateObjectIds,\n\tgetCommandFromTemplate,\n\tgetRequiredApprovals,\n\tPASActionType,\n} from './resolution.js';\nimport type { PASPackageConfig } from './types.js';\n\nconst PAS_INTENT_NAME = 'PAS';\n\n// ---------------------------------------------------------------------------\n// Intent data types\n// ---------------------------------------------------------------------------\n\ntype SendBalanceIntentData = {\n\taction: 'sendBalance';\n\tfrom: string;\n\tto: string;\n\tamount: string;\n\tassetType: string;\n\tcfg: PASPackageConfig;\n};\n\ntype UnlockBalanceIntentData = {\n\taction: 'unlockBalance';\n\tfrom: string;\n\tamount: string;\n\tassetType: string;\n\tcfg: PASPackageConfig;\n};\n\ntype UnlockUnrestrictedBalanceIntentData = {\n\taction: 'unlockUnrestrictedBalance';\n\tfrom: string;\n\tamount: string;\n\tassetType: string;\n\tcfg: PASPackageConfig;\n};\n\ntype AccountForAddressIntentData = {\n\taction: 'accountForAddress';\n\towner: string;\n\tcfg: PASPackageConfig;\n};\n\ntype PASIntentData =\n\t| SendBalanceIntentData\n\t| UnlockBalanceIntentData\n\t| UnlockUnrestrictedBalanceIntentData\n\t| AccountForAddressIntentData;\n\n/**\n * Creates a memoized PAS intent closure. On first call it registers the\n * shared resolver and adds the $Intent command; subsequent calls return\n * the cached TransactionResult.\n */\nfunction createPASIntent(data: PASIntentData): (tx: Transaction) => TransactionResult {\n\tlet result: TransactionResult | null = null;\n\treturn (tx: Transaction) => {\n\t\tif (result) return result;\n\t\ttx.addIntentResolver(PAS_INTENT_NAME, resolvePASIntents);\n\t\tresult = tx.add(\n\t\t\tTransactionCommands.Intent({\n\t\t\t\tname: PAS_INTENT_NAME,\n\t\t\t\tinputs: {},\n\t\t\t\tdata: data as unknown as Record<string, unknown>,\n\t\t\t}),\n\t\t);\n\t\treturn result;\n\t};\n}\n\nexport function sendBalanceIntent(\n\tpackageConfig: PASPackageConfig,\n): (options: {\n\tfrom: string;\n\tto: string;\n\tamount: number | bigint;\n\tassetType: string;\n}) => (tx: Transaction) => TransactionResult {\n\treturn ({ from, to, amount, assetType }) =>\n\t\tcreatePASIntent({\n\t\t\taction: 'sendBalance',\n\t\t\tfrom,\n\t\t\tto,\n\t\t\tamount: String(amount),\n\t\t\tassetType,\n\t\t\tcfg: packageConfig,\n\t\t});\n}\n\nexport function unlockBalanceIntent(\n\tpackageConfig: PASPackageConfig,\n): (options: {\n\tfrom: string;\n\tamount: number | bigint;\n\tassetType: string;\n}) => (tx: Transaction) => TransactionResult {\n\treturn ({ from, amount, assetType }) =>\n\t\tcreatePASIntent({\n\t\t\taction: 'unlockBalance',\n\t\t\tfrom,\n\t\t\tamount: String(amount),\n\t\t\tassetType,\n\t\t\tcfg: packageConfig,\n\t\t});\n}\n\nexport function unlockUnrestrictedBalanceIntent(\n\tpackageConfig: PASPackageConfig,\n): (options: {\n\tfrom: string;\n\tamount: number | bigint;\n\tassetType: string;\n}) => (tx: Transaction) => TransactionResult {\n\treturn ({ from, amount, assetType }) =>\n\t\tcreatePASIntent({\n\t\t\taction: 'unlockUnrestrictedBalance',\n\t\t\tfrom,\n\t\t\tamount: String(amount),\n\t\t\tassetType,\n\t\t\tcfg: packageConfig,\n\t\t});\n}\n\nexport function accountForAddressIntent(\n\tpackageConfig: PASPackageConfig,\n): (owner: string) => (tx: Transaction) => TransactionResult {\n\treturn (owner: string) =>\n\t\tcreatePASIntent({ action: 'accountForAddress', owner, cfg: packageConfig });\n}\n\n// ---------------------------------------------------------------------------\n// Resolver -- holds mutable state shared across all intent builders\n// ---------------------------------------------------------------------------\n//\n// ## How intent resolution works\n//\n// Each PAS intent occupies a single $Intent slot in the transaction's command\n// list. At build time, the resolver replaces each $Intent with a sequence of\n// concrete MoveCall commands via `replaceCommand`.\n//\n// The tricky part is **indexing**. Commands within a PTB reference each\n// other's outputs by absolute command index (e.g. `{ Result: 5 }` means\n// \"the output of command #5\"). When we build the replacement commands for\n// an intent, we need to know what absolute index each new command will land\n// at in the final PTB. That's what `baseIdx` is for:\n//\n// baseIdx = the position of the $Intent slot being replaced\n//\n// So if baseIdx is 3 and we push 2 account-creation commands before the\n// new_auth call, new_auth lands at absolute index 5 (= 3 + 2).\n//\n// The SDK's `replaceCommand` handles index shifting automatically: after\n// splicing N commands in place of 1, it adjusts all Result/NestedResult\n// references in subsequent commands by (N - 1). So we iterate the live\n// command list directly -- no manual offset tracking needed.\n//\n// Each builder returns a `BuildResult` containing:\n// - `commands`: the replacement commands (local array, 0-indexed)\n// - `resultOffset`: which command in that array produces the intent's\n// output value (so external references to the intent can be remapped)\n//\n\ntype SuiObject = SuiClientTypes.Object<{ content: true }>;\n\ntype AccountState = { kind: 'existing' } | { kind: 'created'; resultIndex: number };\n\n/** Return value from each per-action builder. */\ninterface BuildResult {\n\tcommands: Command[];\n\t/** Offset within `commands` of the command whose Result is the intent's output. */\n\tresultOffset: number;\n}\n\nclass Resolver {\n\t/** Pre-fetched on-chain objects (accounts, rules). null = does not exist. */\n\treadonly objects: Map<string, SuiObject | null>;\n\t/** Pre-fetched template dynamic field objects. */\n\treadonly templates: Map<string, SuiObject>;\n\t/** Pre-parsed template lookup: policyId:actionType -> approval type names. */\n\treadonly templateApprovals: Map<string, string[]>;\n\t/** Account existence / creation tracking. */\n\treadonly accounts: Map<string, AccountState>;\n\n\treadonly #tx: TransactionDataBuilder;\n\treadonly #inputCache = new Map<string, Argument>();\n\treadonly #templateCommandsCache = new Map<string, ReturnType<typeof getCommandFromTemplate>[]>();\n\treadonly #config: PASPackageConfig;\n\n\tconstructor({\n\t\ttransactionData,\n\t\tobjects,\n\t\ttemplates,\n\t\ttemplateApprovals,\n\t\taccounts,\n\t\tconfig,\n\t}: {\n\t\ttransactionData: TransactionDataBuilder;\n\t\tobjects: Map<string, SuiObject | null>;\n\t\ttemplates: Map<string, SuiObject>;\n\t\ttemplateApprovals: Map<string, string[]>;\n\t\taccounts: Map<string, AccountState>;\n\t\tconfig: PASPackageConfig;\n\t}) {\n\t\tthis.#tx = transactionData;\n\t\tthis.objects = objects;\n\t\tthis.templates = templates;\n\t\tthis.templateApprovals = templateApprovals;\n\t\tthis.accounts = accounts;\n\t\tthis.#config = config;\n\t}\n\n\t// -- Input helpers (deduplicated) ----------------------------------------\n\n\taddObjectInput(objectId: string): Argument {\n\t\tlet arg = this.#inputCache.get(objectId);\n\t\tif (!arg) {\n\t\t\targ = this.#tx.addInput('object', {\n\t\t\t\t$kind: 'UnresolvedObject',\n\t\t\t\tUnresolvedObject: { objectId },\n\t\t\t});\n\t\t\tthis.#inputCache.set(objectId, arg);\n\t\t}\n\t\treturn arg;\n\t}\n\n\taddPureInput(key: string, value: ReturnType<typeof Inputs.Pure>): Argument {\n\t\tlet arg = this.#inputCache.get(key);\n\t\tif (!arg) {\n\t\t\targ = this.#tx.addInput('pure', value);\n\t\t\tthis.#inputCache.set(key, arg);\n\t\t}\n\t\treturn arg;\n\t}\n\n\taddTemplateInput(type: 'object' | 'pure', arg: CallArg): Argument {\n\t\tif (type === 'object' && arg.$kind === 'UnresolvedObject') {\n\t\t\treturn this.addObjectInput(arg.UnresolvedObject.objectId);\n\t\t}\n\t\treturn this.#tx.addInput(type, arg);\n\t}\n\n\t// -- Object lookup -------------------------------------------------------\n\n\tgetObjectOrThrow(objectId: string, errorFactory: () => Error): SuiObject {\n\t\tconst obj = this.objects.get(objectId);\n\t\tif (!obj) throw errorFactory();\n\t\treturn obj;\n\t}\n\n\t// -- Account resolution ----------------------------------------------------\n\n\t/**\n\t * Returns an Argument referencing the account for `accountId`.\n\t *\n\t * - Existing on-chain account: returns an object Input.\n\t * - Already created earlier in this PTB: returns the stored Result ref.\n\t * - Does not exist yet: **pushes** a `account::create` MoveCall into the\n\t * caller's `commands` array (mutating it) and records the creation so\n\t * subsequent calls for the same account reuse the same Result. The account\n\t * will be shared at the end of the PTB via `shareNewAccounts()`.\n\t *\n\t * @param commands - The caller's local command array (may be mutated).\n\t * @param baseIdx - Absolute PTB index where `commands[0]` will land.\n\t */\n\tresolveAccountArg(accountId: string, owner: string, baseIdx: number): [Argument, Command[]] {\n\t\tconst state = this.accounts.get(accountId);\n\t\tconst commands: Command[] = [];\n\n\t\tif (state?.kind === 'existing') return [this.addObjectInput(accountId), commands];\n\n\t\tif (state?.kind === 'created')\n\t\t\treturn [{ $kind: 'Result', Result: state.resultIndex }, commands];\n\n\t\tconst absoluteIndex = baseIdx + commands.length;\n\t\tcommands.push(\n\t\t\tTransactionCommands.MoveCall({\n\t\t\t\tpackage: this.#config.packageId,\n\t\t\t\tmodule: 'account',\n\t\t\t\tfunction: 'create',\n\t\t\t\targuments: [\n\t\t\t\t\tthis.addObjectInput(this.#config.namespaceId),\n\t\t\t\t\tthis.addPureInput(`address:${owner}`, Inputs.Pure(bcs.Address.serialize(owner))),\n\t\t\t\t],\n\t\t\t}),\n\t\t);\n\n\t\tthis.accounts.set(accountId, { kind: 'created', resultIndex: absoluteIndex });\n\t\treturn [{ $kind: 'Result', Result: absoluteIndex }, commands];\n\t}\n\n\t// -- Template resolution (synchronous, all data pre-fetched) -------------\n\n\tresolveTemplateCommands(policyObjectId: string, actionType: PASActionType) {\n\t\tconst cacheKey = `${policyObjectId}:${actionType}`;\n\t\tconst cached = this.#templateCommandsCache.get(cacheKey);\n\t\tif (cached) return cached;\n\n\t\tconst approvalTypeNames = this.templateApprovals.get(cacheKey);\n\t\tif (!approvalTypeNames) {\n\t\t\tthrow new PASClientError(\n\t\t\t\t`No required approvals found for action \"${actionType}\". The issuer has not configured this action.`,\n\t\t\t);\n\t\t}\n\n\t\tconst templatesId = deriveTemplateRegistryAddress(this.#config);\n\t\tconst commands = approvalTypeNames.map((tn) => {\n\t\t\tconst templateId = deriveTemplateAddress(templatesId, tn);\n\t\t\tconst template = this.templates.get(templateId);\n\t\t\tif (!template) {\n\t\t\t\tthrow new PASClientError(\n\t\t\t\t\t`Template not found for approval type \"${tn}\". The issuer has not set up the template command.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn getCommandFromTemplate(template);\n\t\t});\n\n\t\tthis.#templateCommandsCache.set(cacheKey, commands);\n\t\treturn commands;\n\t}\n\n\t/**\n\t * Replaces a standard action intent (transfer/unlock) with its built\n\t * commands. The resolve call at `actualIdx + resultOffset` produces the\n\t * intent's output value.\n\t */\n\treplaceIntent(actualIdx: number, commands: Command[], resultOffset: number) {\n\t\tthis.#tx.replaceCommand(actualIdx, commands, { Result: actualIdx + resultOffset });\n\t}\n\n\t/**\n\t * Replaces a accountForAddress intent when the account already exists.\n\t * The intent is removed (0 replacement commands) and external references\n\t * are remapped to the existing account's Input argument.\n\t *\n\t * Note: SDK's replaceCommand signature doesn't accept Input args as\n\t * resultIndex, but the runtime handles it correctly via ArgumentSchema.parse().\n\t */\n\treplaceIntentWithExistingAccount(actualIdx: number, accountArg: Argument) {\n\t\tthis.#tx.replaceCommand(actualIdx, [], accountArg as any);\n\t}\n\n\t/**\n\t * Replaces a accountForAddress intent when the account needs to be created.\n\t * The intent is replaced with the account::create command(s), and external\n\t * references are remapped to the first command's Result (the new account).\n\t */\n\treplaceIntentWithCreatedAccount(actualIdx: number, commands: Command[]) {\n\t\tthis.#tx.replaceCommand(actualIdx, commands, { Result: actualIdx });\n\t}\n\n\t// -- Per-action builders --------------------------------------------------\n\t//\n\t// Each builder constructs a local `commands` array representing the\n\t// sequence of MoveCall commands that replace the intent. Commands\n\t// reference each other using absolute indices (baseIdx + local offset).\n\t//\n\t// The general pattern for a transfer is:\n\t// [account::create (0..N)] -- only if accounts don't exist yet\n\t// account::new_auth -- create ownership proof\n\t// account::send_balance -- initiate the request\n\t// [approval commands] -- issuer-defined template commands\n\t// send_funds::resolve_balance -- finalize and produce the output\n\t//\n\t// `resultOffset` points at the last command (resolve), whose Result\n\t// becomes the intent's output value.\n\n\tbuildSendBalance(data: SendBalanceIntentData, baseIdx: number): BuildResult {\n\t\tconst { from, to, assetType, amount } = data;\n\t\tconst fromAccountId = deriveAccountAddress(from, this.#config);\n\t\tconst toAccountId = deriveAccountAddress(to, this.#config);\n\n\t\tconst policyId = derivePolicyAddress(assetType, this.#config);\n\t\tthis.getObjectOrThrow(policyId, () => new PolicyNotFoundError(assetType));\n\t\tconst templateCmds = this.resolveTemplateCommands(policyId, 'send_funds');\n\n\t\tconst [toAccountArg, commands] = this.resolveAccountArg(toAccountId, to, baseIdx);\n\t\tconst [fromAccountArg, fromAccountCommands] = this.resolveAccountArg(\n\t\t\tfromAccountId,\n\t\t\tfrom,\n\t\t\tbaseIdx + commands.length,\n\t\t);\n\t\tcommands.push(...fromAccountCommands);\n\n\t\tconst policyArg = this.addObjectInput(policyId);\n\n\t\t// account::new_auth\n\t\tconst authIdx = baseIdx + commands.length;\n\t\tcommands.push(\n\t\t\tTransactionCommands.MoveCall({\n\t\t\t\tpackage: this.#config.packageId,\n\t\t\t\tmodule: 'account',\n\t\t\t\tfunction: 'new_auth',\n\t\t\t}),\n\t\t);\n\n\t\t// account::send_balance\n\t\tconst requestIdx = baseIdx + commands.length;\n\t\tcommands.push(\n\t\t\tTransactionCommands.MoveCall({\n\t\t\t\tpackage: this.#config.packageId,\n\t\t\t\tmodule: 'account',\n\t\t\t\tfunction: 'send_balance',\n\t\t\t\targuments: [\n\t\t\t\t\tfromAccountArg,\n\t\t\t\t\t{ $kind: 'Result', Result: authIdx },\n\t\t\t\t\ttoAccountArg,\n\t\t\t\t\tthis.addTemplateInput('pure', Inputs.Pure(bcs.u64().serialize(BigInt(amount)))),\n\t\t\t\t],\n\t\t\t\ttypeArguments: [normalizeStructTag(assetType)],\n\t\t\t}),\n\t\t);\n\t\tconst requestArg: Argument = { $kind: 'Result', Result: requestIdx };\n\n\t\t// Issuer-defined approval commands from templates.\n\t\t// Template Result/NestedResult indices are relative to the first template\n\t\t// command, so we capture the absolute offset before pushing any of them.\n\t\tconst templateStartIdx = baseIdx + commands.length;\n\t\tfor (const templateCmd of templateCmds) {\n\t\t\tcommands.push(\n\t\t\t\tbuildMoveCallCommandFromTemplate(\n\t\t\t\t\ttemplateCmd,\n\t\t\t\t\t{\n\t\t\t\t\t\taddInput: (type, arg) => this.addTemplateInput(type, arg),\n\t\t\t\t\t\tsenderAccount: fromAccountArg,\n\t\t\t\t\t\treceiverAccount: toAccountArg,\n\t\t\t\t\t\tpolicy: policyArg,\n\t\t\t\t\t\trequest: requestArg,\n\t\t\t\t\t},\n\t\t\t\t\ttemplateStartIdx,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// send_funds::resolve\n\t\tconst resultOffset = commands.length;\n\t\tcommands.push(\n\t\t\tTransactionCommands.MoveCall({\n\t\t\t\tpackage: this.#config.packageId,\n\t\t\t\tmodule: 'send_funds',\n\t\t\t\tfunction: 'resolve_balance',\n\t\t\t\targuments: [requestArg, policyArg],\n\t\t\t\ttypeArguments: [normalizeStructTag(assetType)],\n\t\t\t}),\n\t\t);\n\n\t\treturn { commands, resultOffset };\n\t}\n\n\t/**\n\t * Builds commands for both restricted and unrestricted unlock flows.\n\t * Restricted: requires a Policy, runs issuer approval templates, then resolve.\n\t * Unrestricted: no Policy needed, calls resolve_unrestricted_balance directly.\n\t */\n\tbuildUnlockBalance(\n\t\tdata: UnlockBalanceIntentData | UnlockUnrestrictedBalanceIntentData,\n\t\tbaseIdx: number,\n\t): BuildResult {\n\t\tconst { from, assetType, amount } = data;\n\t\tconst fromAccountId = deriveAccountAddress(from, this.#config);\n\t\tconst policyId = derivePolicyAddress(assetType, this.#config);\n\n\t\tconst isRestricted = data.action === 'unlockBalance';\n\n\t\tif (isRestricted) {\n\t\t\tthis.getObjectOrThrow(\n\t\t\t\tpolicyId,\n\t\t\t\t() =>\n\t\t\t\t\tnew PASClientError(\n\t\t\t\t\t\t`Policy does not exist for asset type ${assetType}. ` +\n\t\t\t\t\t\t\t`That means that the issuer has not yet enabled funds management for this asset. ` +\n\t\t\t\t\t\t\t`If this is a non-managed asset, you can use the unrestricted unlock flow by calling unlockUnrestrictedBalance() instead.`,\n\t\t\t\t\t),\n\t\t\t);\n\t\t} else {\n\t\t\tif (this.objects.get(policyId) !== null) {\n\t\t\t\tthrow new PASClientError(\n\t\t\t\t\t`A policy exists for asset type ${assetType}. That means that the issuer has enabled funds management for this asset and you can no longer use the unrestricted unlock flow.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst [fromAccountArg, commands] = this.resolveAccountArg(fromAccountId, from, baseIdx);\n\t\tconst policyArg = isRestricted ? this.addObjectInput(policyId) : undefined;\n\n\t\t// account::new_auth\n\t\tconst authIdx = baseIdx + commands.length;\n\t\tcommands.push(\n\t\t\tTransactionCommands.MoveCall({\n\t\t\t\tpackage: this.#config.packageId,\n\t\t\t\tmodule: 'account',\n\t\t\t\tfunction: 'new_auth',\n\t\t\t}),\n\t\t);\n\n\t\t// account::unlock_funds\n\t\tconst requestIdx = baseIdx + commands.length;\n\t\tcommands.push(\n\t\t\tTransactionCommands.MoveCall({\n\t\t\t\tpackage: this.#config.packageId,\n\t\t\t\tmodule: 'account',\n\t\t\t\tfunction: 'unlock_balance',\n\t\t\t\targuments: [\n\t\t\t\t\tfromAccountArg,\n\t\t\t\t\t{ $kind: 'Result', Result: authIdx },\n\t\t\t\t\tthis.addTemplateInput('pure', Inputs.Pure(bcs.u64().serialize(BigInt(amount)))),\n\t\t\t\t],\n\t\t\t\ttypeArguments: [normalizeStructTag(assetType)],\n\t\t\t}),\n\t\t);\n\t\tconst requestArg: Argument = { $kind: 'Result', Result: requestIdx };\n\n\t\tif (isRestricted) {\n\t\t\t// Issuer-defined approval commands from templates.\n\t\t\tconst templateCmds = this.resolveTemplateCommands(policyId, 'unlock_funds');\n\t\t\tconst templateStartIdx = baseIdx + commands.length;\n\t\t\tfor (const templateCmd of templateCmds) {\n\t\t\t\tcommands.push(\n\t\t\t\t\tbuildMoveCallCommandFromTemplate(\n\t\t\t\t\t\ttemplateCmd,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taddInput: (type, arg) => this.addTemplateInput(type, arg),\n\t\t\t\t\t\t\tsenderAccount: fromAccountArg,\n\t\t\t\t\t\t\tpolicy: policyArg,\n\t\t\t\t\t\t\trequest: requestArg,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttemplateStartIdx,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// unlock_funds::resolve\n\t\t\tconst resultOffset = commands.length;\n\t\t\tcommands.push(\n\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\tpackage: this.#config.packageId,\n\t\t\t\t\tmodule: 'unlock_funds',\n\t\t\t\t\tfunction: 'resolve',\n\t\t\t\t\targuments: [requestArg, policyArg!],\n\t\t\t\t\ttypeArguments: [normalizeStructTag(assetType)],\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn { commands, resultOffset };\n\t\t}\n\n\t\t// unlock_funds::resolve_unrestricted_balance\n\t\tconst resultOffset = commands.length;\n\t\tcommands.push(\n\t\t\tTransactionCommands.MoveCall({\n\t\t\t\tpackage: this.#config.packageId,\n\t\t\t\tmodule: 'unlock_funds',\n\t\t\t\tfunction: 'resolve_unrestricted_balance',\n\t\t\t\targuments: [requestArg, this.addObjectInput(this.#config.namespaceId)],\n\t\t\t\ttypeArguments: [normalizeStructTag(assetType)],\n\t\t\t}),\n\t\t);\n\t\treturn { commands, resultOffset };\n\t}\n\n\t// -- Finalization ---------------------------------------------------------\n\n\t/**\n\t * Appends `account::share` commands for every account that was created during\n\t * resolution. Called once at the end, after all intents have been resolved,\n\t * so that each account is shared exactly once regardless of how many intents\n\t * referenced it.\n\t */\n\tshareNewAccounts() {\n\t\tfor (const state of this.accounts.values()) {\n\t\t\tif (state.kind !== 'created') continue;\n\t\t\tthis.#tx.commands.push(\n\t\t\t\tTransactionCommands.MoveCall({\n\t\t\t\t\tpackage: this.#config.packageId,\n\t\t\t\t\tmodule: 'account',\n\t\t\t\t\tfunction: 'share',\n\t\t\t\t\targuments: [{ $kind: 'Result', Result: state.resultIndex }],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Data collection + fetching (pre-resolution)\n// ---------------------------------------------------------------------------\n\ntype AccountOwner = { owner: string };\n\ninterface IntentDataCollection {\n\tobjectIds: Set<string>;\n\taccountRequests: Map<string, AccountOwner>;\n\tintentDataList: PASIntentData[];\n\tcfg: PASPackageConfig;\n}\n\n/** Scans commands for PAS intents and collects the object IDs we need to fetch. */\nfunction collectIntentData(commands: readonly Command[]): IntentDataCollection | null {\n\tconst objectIds = new Set<string>();\n\tconst accountRequests = new Map<string, AccountOwner>();\n\tconst intentDataList: PASIntentData[] = [];\n\tlet cfg: PASPackageConfig | null = null;\n\n\tfor (const command of commands) {\n\t\tif (command.$kind !== '$Intent' || command.$Intent.name !== PAS_INTENT_NAME) continue;\n\t\tconst data = command.$Intent.data as unknown as PASIntentData;\n\n\t\t// We intentionally initialize the configs to match the first command that uses PAS.\n\t\tif (!cfg) cfg = data.cfg;\n\t\tintentDataList.push(data);\n\n\t\tswitch (data.action) {\n\t\t\tcase 'sendBalance': {\n\t\t\t\tconst fromId = deriveAccountAddress(data.from, cfg);\n\t\t\t\tconst toId = deriveAccountAddress(data.to, cfg);\n\t\t\t\tobjectIds.add(fromId);\n\t\t\t\tobjectIds.add(toId);\n\t\t\t\tobjectIds.add(derivePolicyAddress(data.assetType, cfg));\n\t\t\t\taccountRequests.set(fromId, { owner: data.from });\n\t\t\t\taccountRequests.set(toId, { owner: data.to });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'unlockBalance':\n\t\t\tcase 'unlockUnrestrictedBalance': {\n\t\t\t\tconst fromId = deriveAccountAddress(data.from, cfg);\n\t\t\t\tobjectIds.add(fromId);\n\t\t\t\tobjectIds.add(derivePolicyAddress(data.assetType, cfg));\n\t\t\t\taccountRequests.set(fromId, { owner: data.from });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'accountForAddress': {\n\t\t\t\tconst id = deriveAccountAddress(data.owner, cfg);\n\t\t\t\tobjectIds.add(id);\n\t\t\t\taccountRequests.set(id, { owner: data.owner });\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (intentDataList.length === 0) return null;\n\n\tif (!cfg)\n\t\tthrow new PASClientError('No package configuration found in intents. This is an internal bug.');\n\n\treturn { objectIds, accountRequests, intentDataList, cfg };\n}\n\nasync function initializeContext(\n\ttransactionData: TransactionDataBuilder,\n\tclient: ClientWithCoreApi,\n\tobjectIds: Set<string>,\n\taccountRequests: Map<string, AccountOwner>,\n\tintentDataList: PASIntentData[],\n\tconfig: PASPackageConfig,\n): Promise<Resolver> {\n\t// 1. Batch-fetch all accounts + rules\n\tconst allIds = [...objectIds];\n\tconst { objects: fetched } = await client.core.getObjects({\n\t\tobjectIds: allIds,\n\t\tinclude: { content: true },\n\t});\n\n\tconst objects = new Map<string, SuiObject | null>();\n\n\tfor (const id of allIds) {\n\t\tconst obj = fetched.filter((o) => 'content' in o).find((o) => o.objectId === id);\n\t\tobjects.set(id, obj ?? null);\n\t}\n\n\t// 2. Build initial account map (existing vs needs-creation)\n\tconst accounts = new Map<string, AccountState>();\n\tfor (const [accountId] of accountRequests) {\n\t\tif (objects.get(accountId) !== null) {\n\t\t\taccounts.set(accountId, { kind: 'existing' });\n\t\t}\n\t}\n\n\t// 3. Collect template DF IDs by parsing rules\n\tconst templateApprovals = new Map<string, string[]>();\n\tconst templateIds: string[] = [];\n\tconst seen = new Set<string>();\n\n\tfor (const data of intentDataList) {\n\t\tlet actionType: PASActionType | null = null;\n\t\tlet assetType: string | null = null;\n\n\t\tif (data.action === 'sendBalance') {\n\t\t\tactionType = 'send_funds';\n\t\t\tassetType = data.assetType;\n\t\t} else if (data.action === 'unlockBalance') {\n\t\t\tactionType = 'unlock_funds';\n\t\t\tassetType = data.assetType;\n\t\t}\n\n\t\tif (!actionType || !assetType) continue;\n\n\t\tconst policyId = derivePolicyAddress(assetType, config);\n\t\tconst key = `${policyId}:${actionType}`;\n\t\tif (seen.has(key)) continue;\n\t\tseen.add(key);\n\n\t\tconst policyObject = objects.get(policyId);\n\t\tif (!policyObject) continue;\n\n\t\tconst approvalTypeNames = getRequiredApprovals(policyObject, actionType);\n\t\tif (!approvalTypeNames?.length) continue;\n\n\t\tconst templatesId = deriveTemplateRegistryAddress(config);\n\t\ttemplateApprovals.set(key, approvalTypeNames);\n\t\ttemplateIds.push(...approvalTypeNames.map((tn) => deriveTemplateAddress(templatesId, tn)));\n\t}\n\n\t// 4. Batch-fetch all template data\n\tconst templates = new Map<string, SuiObject>();\n\tif (templateIds.length > 0) {\n\t\tconst { objects: templateObjects } = await client.core.getObjects({\n\t\t\tobjectIds: templateIds,\n\t\t\tinclude: { content: true },\n\t\t});\n\n\t\tfor (const obj of templateObjects.filter((o) => 'content' in o)) {\n\t\t\ttemplates.set(obj.objectId, obj);\n\t\t}\n\t}\n\n\t// 5. Validate that all objects referenced by templates are shared or immutable.\n\tawait validateTemplateObjects(client, Array.from(templates.values()));\n\n\treturn new Resolver({\n\t\ttransactionData,\n\t\tobjects,\n\t\ttemplates,\n\t\ttemplateApprovals,\n\t\taccounts,\n\t\tconfig,\n\t});\n}\n\nconst resolvePASIntents: TransactionPlugin = async (transactionData, buildOptions, next) => {\n\tconst client = buildOptions.client;\n\tif (!client)\n\t\tthrow new PASClientError(\n\t\t\t'A SuiClient must be provided to build transactions with PAS intents.',\n\t\t);\n\n\tconst requirements = collectIntentData(transactionData.commands);\n\tif (!requirements) return next();\n\n\tconst { objectIds, accountRequests, intentDataList, cfg } = requirements;\n\n\tconst ctx = await initializeContext(\n\t\ttransactionData,\n\t\tclient,\n\t\tobjectIds,\n\t\taccountRequests,\n\t\tintentDataList,\n\t\tcfg,\n\t);\n\n\t// Always advance by 1 so we never skip (e.g. a replacement that contains another intent).\n\t// When we replace with 0 commands we decrement so the loop's increment nets 0 and we\n\t// re-read the slot (where the next intent shifted in).\n\tfor (let index = 0; index < transactionData.commands.length; index++) {\n\t\tconst command = transactionData.commands[index];\n\t\tif (command.$kind !== '$Intent' || command.$Intent.name !== PAS_INTENT_NAME) continue;\n\n\t\tconst data = command.$Intent.data as unknown as PASIntentData;\n\n\t\tif (data.action === 'accountForAddress') {\n\t\t\tconst accountId = deriveAccountAddress(data.owner, cfg);\n\t\t\tconst [accountArg, commands] = ctx.resolveAccountArg(accountId, data.owner, index);\n\n\t\t\tif (commands.length === 0) {\n\t\t\t\tctx.replaceIntentWithExistingAccount(index, accountArg);\n\t\t\t\tindex--; // Next iteration will ++, so we re-read this index (next intent moved here)\n\t\t\t} else {\n\t\t\t\tctx.replaceIntentWithCreatedAccount(index, commands);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet result: BuildResult;\n\t\tswitch (data.action) {\n\t\t\tcase 'sendBalance':\n\t\t\t\tresult = ctx.buildSendBalance(data, index);\n\t\t\t\tbreak;\n\t\t\tcase 'unlockBalance':\n\t\t\tcase 'unlockUnrestrictedBalance':\n\t\t\t\tresult = ctx.buildUnlockBalance(data, index);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontinue;\n\t\t}\n\n\t\tctx.replaceIntent(index, result.commands, result.resultOffset);\n\t}\n\n\tctx.shareNewAccounts();\n\treturn next();\n};\n\n/**\n * Parses all template commands, collects every object they reference\n * (fully-resolved refs, shared refs, and ext lookups), batch-fetches\n * their current state, and rejects any that are not shared or immutable.\n */\nexport async function validateTemplateObjects(\n\tclient: ClientWithCoreApi,\n\ttemplates: SuiObject[],\n): Promise<void> {\n\tconst allTemplateCommands = templates.map(getCommandFromTemplate);\n\tconst objectIds = collectTemplateObjectIds(allTemplateCommands);\n\n\tif (objectIds.size === 0) return;\n\n\tconst { objects: fetchedObjects } = await client.core.getObjects({\n\t\tobjectIds: [...objectIds],\n\t});\n\n\tfor (const obj of fetchedObjects) {\n\t\tif (obj instanceof Error)\n\t\t\tthrow new PASClientError('Failed to fetch template object: ' + obj.message);\n\n\t\tif (obj.owner.$kind !== 'Shared' && obj.owner.$kind !== 'Immutable') {\n\t\t\tthrow new InvalidObjectOwnershipError(obj.objectId, obj.owner.$kind);\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|