@kaspacom/kcc20-tx-builder 0.4.0 → 0.6.0
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/CHANGELOG.md +12 -1
- package/README.md +2 -2
- package/RELEASING.md +19 -14
- package/artifacts/ArgentKCC20.reference.json +12428 -0
- package/artifacts/ArgentKCC20Wrapper.placeholder.json +12455 -0
- package/artifacts/KCC20.placeholder.json +1447 -1973
- package/artifacts/KCC20FeeTicket.placeholder.json +816 -1447
- package/artifacts/KCC20Orderbook.placeholder.json +1 -2
- package/artifacts/KCC20SnapshotBootstrap.placeholder.json +101 -100
- package/artifacts/KCC20SnapshotController.placeholder.json +101 -100
- package/artifacts/KCC20Vesting.placeholder.json +427 -429
- package/artifacts/KCC20Wrapper.placeholder.json +733 -734
- package/artifacts/kcc20-snapshot-bootstrap/KCC20SnapshotBootstrap.placeholder.json +101 -100
- package/artifacts/kcc20-snapshot-controller/KCC20SnapshotController.placeholder.json +101 -100
- package/artifacts/snapshot-standards.lock.json +6 -3
- package/artifacts/token-profiles.json +109 -0
- package/artifacts/token-profiles.sha256 +1 -0
- package/dist/action-source-resolver.d.ts.map +1 -1
- package/dist/action-source-resolver.js +11 -2
- package/dist/action-source-resolver.js.map +1 -1
- package/dist/artifacts.d.ts +10 -10
- package/dist/artifacts.d.ts.map +1 -1
- package/dist/artifacts.js +10 -10
- package/dist/artifacts.js.map +1 -1
- package/dist/cjs/action-source-resolver.js +11 -2
- package/dist/cjs/action-source-resolver.js.map +1 -1
- package/dist/cjs/artifacts.d.ts +10 -10
- package/dist/cjs/artifacts.js +10 -10
- package/dist/cjs/artifacts.js.map +1 -1
- package/dist/cjs/engine.d.ts +10 -0
- package/dist/cjs/engine.js +124 -77
- package/dist/cjs/engine.js.map +1 -1
- package/dist/cjs/index.d.ts +5 -0
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/profile-transfer.d.ts +60 -0
- package/dist/cjs/profile-transfer.js +248 -0
- package/dist/cjs/profile-transfer.js.map +1 -0
- package/dist/cjs/snapshot-operation.d.ts +0 -1
- package/dist/cjs/snapshot-operation.js +1 -1126
- package/dist/cjs/snapshot-operation.js.map +1 -1
- package/dist/cjs/snapshot-signerless-transactions.d.ts +1 -1
- package/dist/cjs/snapshot-signerless-transactions.js +3 -6
- package/dist/cjs/snapshot-signerless-transactions.js.map +1 -1
- package/dist/cjs/snapshot-signerless.js +5 -3
- package/dist/cjs/snapshot-signerless.js.map +1 -1
- package/dist/cjs/snapshot-verification.d.ts +1 -1
- package/dist/cjs/snapshot-verification.js +16 -12
- package/dist/cjs/snapshot-verification.js.map +1 -1
- package/dist/cjs/token-authorization.d.ts +42 -0
- package/dist/cjs/token-authorization.js +93 -0
- package/dist/cjs/token-authorization.js.map +1 -0
- package/dist/cjs/token-profiles.d.ts +82 -0
- package/dist/cjs/token-profiles.generated.d.ts +3 -0
- package/dist/cjs/token-profiles.generated.js +114 -0
- package/dist/cjs/token-profiles.generated.js.map +1 -0
- package/dist/cjs/token-profiles.js +257 -0
- package/dist/cjs/token-profiles.js.map +1 -0
- package/dist/cjs/transfer-payload.d.ts +9 -0
- package/dist/cjs/transfer-payload.js +37 -0
- package/dist/cjs/transfer-payload.js.map +1 -0
- package/dist/cjs/wrapper-operation.d.ts +1 -0
- package/dist/cjs/wrapper-operation.js +1 -0
- package/dist/cjs/wrapper-operation.js.map +1 -1
- package/dist/engine.d.ts +10 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +125 -78
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/profile-transfer.d.ts +61 -0
- package/dist/profile-transfer.d.ts.map +1 -0
- package/dist/profile-transfer.js +245 -0
- package/dist/profile-transfer.js.map +1 -0
- package/dist/snapshot-operation.d.ts +0 -1
- package/dist/snapshot-operation.d.ts.map +1 -1
- package/dist/snapshot-operation.js +0 -1125
- package/dist/snapshot-operation.js.map +1 -1
- package/dist/snapshot-signerless-transactions.d.ts +1 -1
- package/dist/snapshot-signerless-transactions.d.ts.map +1 -1
- package/dist/snapshot-signerless-transactions.js +3 -6
- package/dist/snapshot-signerless-transactions.js.map +1 -1
- package/dist/snapshot-signerless.d.ts.map +1 -1
- package/dist/snapshot-signerless.js +5 -3
- package/dist/snapshot-signerless.js.map +1 -1
- package/dist/snapshot-verification.d.ts +1 -1
- package/dist/snapshot-verification.d.ts.map +1 -1
- package/dist/snapshot-verification.js +16 -12
- package/dist/snapshot-verification.js.map +1 -1
- package/dist/token-authorization.d.ts +43 -0
- package/dist/token-authorization.d.ts.map +1 -0
- package/dist/token-authorization.js +88 -0
- package/dist/token-authorization.js.map +1 -0
- package/dist/token-profiles.d.ts +83 -0
- package/dist/token-profiles.d.ts.map +1 -0
- package/dist/token-profiles.generated.d.ts +4 -0
- package/dist/token-profiles.generated.d.ts.map +1 -0
- package/dist/token-profiles.generated.js +111 -0
- package/dist/token-profiles.generated.js.map +1 -0
- package/dist/token-profiles.js +245 -0
- package/dist/token-profiles.js.map +1 -0
- package/dist/transfer-payload.d.ts +10 -0
- package/dist/transfer-payload.d.ts.map +1 -0
- package/dist/transfer-payload.js +34 -0
- package/dist/transfer-payload.js.map +1 -0
- package/dist/wrapper-operation.d.ts +1 -0
- package/dist/wrapper-operation.d.ts.map +1 -1
- package/dist/wrapper-operation.js +1 -0
- package/dist/wrapper-operation.js.map +1 -1
- package/docs/API.md +1 -1
- package/docs/COMPATIBILITY.json +24 -33
- package/docs/INTEGRATION.md +2 -2
- package/docs/LAST_CALL.md +50 -0
- package/examples/README.md +5 -2
- package/examples/backend/cli.ts +2 -1
- package/examples/frontend/main.ts +2 -1
- package/examples/shared/build.ts +3 -0
- package/examples/shared/offline-profile.ts +27 -0
- package/examples/shared/recipe-fixtures.ts +3 -2
- package/examples/test/browser.ts +2 -2
- package/examples/test/build.test.ts +21 -8
- package/examples/test/sources.test.ts +3 -2
- package/package.json +2 -2
- package/src/action-source-resolver.ts +22 -4
- package/src/artifacts.ts +12 -10
- package/src/engine.ts +208 -105
- package/src/index.ts +7 -0
- package/src/profile-transfer.ts +425 -0
- package/src/snapshot-operation.ts +59 -1731
- package/src/snapshot-signerless-transactions.ts +8 -17
- package/src/snapshot-signerless.ts +5 -3
- package/src/snapshot-verification.ts +17 -13
- package/src/token-authorization.ts +135 -0
- package/src/token-profiles.generated.ts +112 -0
- package/src/token-profiles.ts +456 -0
- package/src/transfer-payload.ts +45 -0
- package/src/wrapper-operation.ts +2 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import {
|
|
2
|
+
authenticateTokenArtifact,
|
|
3
|
+
requireTokenCapability,
|
|
4
|
+
resolveAuthenticatedTokenProfile,
|
|
5
|
+
type AuthenticatedTokenProfileRegistry,
|
|
6
|
+
} from "./token-profiles.js";
|
|
7
|
+
import {
|
|
8
|
+
buildKcc20ScriptForState,
|
|
9
|
+
encodeKcc20StateRecordArrays,
|
|
10
|
+
} from "./abi.js";
|
|
11
|
+
import {
|
|
12
|
+
planTokenOwnerWitness,
|
|
13
|
+
buildTokenBorrowWitness,
|
|
14
|
+
type TokenSigningCapabilities,
|
|
15
|
+
} from "./token-authorization.js";
|
|
16
|
+
|
|
17
|
+
export interface ProfileTokenState {
|
|
18
|
+
amount: bigint;
|
|
19
|
+
owner: Uint8Array;
|
|
20
|
+
ownerScheme: number;
|
|
21
|
+
borrowScheme: number;
|
|
22
|
+
borrowGuard: Uint8Array;
|
|
23
|
+
extensionCommitment: Uint8Array;
|
|
24
|
+
}
|
|
25
|
+
export interface ProfileTransferInput {
|
|
26
|
+
/** A current, authenticated RPC UTXO. This API does not fetch chain state. */
|
|
27
|
+
entry: any;
|
|
28
|
+
state: ProfileTokenState;
|
|
29
|
+
publicKey?: string;
|
|
30
|
+
authorityInputIndex?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface ProfileAuthorityInput {
|
|
33
|
+
entry: any;
|
|
34
|
+
revealScript: Uint8Array;
|
|
35
|
+
/** Exact authority witness, supplied by its reviewed authority adapter. */
|
|
36
|
+
arguments: Array<
|
|
37
|
+
| { type: "data"; hex: string }
|
|
38
|
+
| { type: "i64"; value: string }
|
|
39
|
+
| { type: "signature"; prefixHex?: string }
|
|
40
|
+
>;
|
|
41
|
+
signatureAlgorithm?: "schnorr" | "ecdsa";
|
|
42
|
+
}
|
|
43
|
+
export interface ProfileTransferPlan {
|
|
44
|
+
/** Host-authenticated registry input; never populate this from transaction request data. */
|
|
45
|
+
tokenProfileRegistry: AuthenticatedTokenProfileRegistry;
|
|
46
|
+
profileId: string;
|
|
47
|
+
artifact: unknown;
|
|
48
|
+
tokenInputs: ProfileTransferInput[];
|
|
49
|
+
nextStates: ProfileTokenState[];
|
|
50
|
+
authorities?: ProfileAuthorityInput[];
|
|
51
|
+
fundingEntry: any;
|
|
52
|
+
fundingAddress: string;
|
|
53
|
+
feeSompi: bigint;
|
|
54
|
+
network: string;
|
|
55
|
+
tokenOutputSompi?: bigint;
|
|
56
|
+
capabilities: TokenSigningCapabilities;
|
|
57
|
+
mode: "owner" | "borrow";
|
|
58
|
+
borrow?: {
|
|
59
|
+
nextGuard?: string;
|
|
60
|
+
oneTimePublicKey?: string;
|
|
61
|
+
signingPublicKey?: string;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const hex = (v: Uint8Array) =>
|
|
65
|
+
Array.from(v, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
66
|
+
const amount = (e: any) =>
|
|
67
|
+
BigInt(e.amount ?? e.entry?.amount ?? e.utxoEntry?.amount);
|
|
68
|
+
const spk = (e: any) =>
|
|
69
|
+
e.scriptPublicKey ?? e.entry?.scriptPublicKey ?? e.utxoEntry?.scriptPublicKey;
|
|
70
|
+
const cid = (e: any): string | undefined => {
|
|
71
|
+
const c = e.covenantId ?? e.entry?.covenantId ?? e.utxoEntry?.covenantId;
|
|
72
|
+
return c == null ? undefined : String(c).toLowerCase();
|
|
73
|
+
};
|
|
74
|
+
const outpoint = (e: any) => e.outpoint ?? e.entry?.outpoint;
|
|
75
|
+
const assert = (ok: unknown, message: string) => {
|
|
76
|
+
if (!ok) throw Error(message);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Deterministic standard transfer construction for explicit authority/borrow flows.
|
|
81
|
+
* Callers authenticate RPC freshness and canonical acceptance before signing.
|
|
82
|
+
* Authority continuations preserve their script, value and covenant identity.
|
|
83
|
+
*/
|
|
84
|
+
export function buildProfileTransferTransaction(
|
|
85
|
+
kw: any,
|
|
86
|
+
plan: ProfileTransferPlan,
|
|
87
|
+
): any {
|
|
88
|
+
const profile = resolveAuthenticatedTokenProfile(
|
|
89
|
+
plan.tokenProfileRegistry,
|
|
90
|
+
plan.profileId,
|
|
91
|
+
);
|
|
92
|
+
const { tokenInputs, nextStates } = plan;
|
|
93
|
+
requireTokenCapability(
|
|
94
|
+
profile,
|
|
95
|
+
plan.mode === "borrow" ? "borrow" : "transfer",
|
|
96
|
+
);
|
|
97
|
+
assert(
|
|
98
|
+
plan.mode === "owner" || plan.mode === "borrow",
|
|
99
|
+
"explicit owner or borrow mode is required",
|
|
100
|
+
);
|
|
101
|
+
const artifact = authenticateTokenArtifact(profile, plan.artifact);
|
|
102
|
+
assert(
|
|
103
|
+
tokenInputs.length > 0 && tokenInputs.length <= profile.maxTokenInputs,
|
|
104
|
+
"token input bound exceeded",
|
|
105
|
+
);
|
|
106
|
+
assert(
|
|
107
|
+
nextStates.length > 0 && nextStates.length <= profile.maxTokenOutputs,
|
|
108
|
+
"token output bound exceeded",
|
|
109
|
+
);
|
|
110
|
+
assert(
|
|
111
|
+
plan.feeSompi >= 0n && plan.feeSompi <= 50_000_000n,
|
|
112
|
+
"fee exceeds transaction limit",
|
|
113
|
+
);
|
|
114
|
+
const tokenId = cid(tokenInputs[0].entry);
|
|
115
|
+
assert(
|
|
116
|
+
tokenId && /^[0-9a-f]{64}$/.test(tokenId),
|
|
117
|
+
"native token identity is required",
|
|
118
|
+
);
|
|
119
|
+
const extension = hex(tokenInputs[0].state.extensionCommitment);
|
|
120
|
+
let total = 0n;
|
|
121
|
+
for (const input of tokenInputs) {
|
|
122
|
+
assert(cid(input.entry) === tokenId, "mixed native token identities");
|
|
123
|
+
assert(
|
|
124
|
+
input.state.amount >= 0n &&
|
|
125
|
+
hex(input.state.extensionCommitment) === extension,
|
|
126
|
+
"invalid input amount or extension",
|
|
127
|
+
);
|
|
128
|
+
total += input.state.amount;
|
|
129
|
+
const expected = kw.payToScriptHashScript(
|
|
130
|
+
buildKcc20ScriptForState(artifact, input.state),
|
|
131
|
+
);
|
|
132
|
+
assert(
|
|
133
|
+
kw.addressFromScriptPublicKey(expected, plan.network).toString() ===
|
|
134
|
+
kw
|
|
135
|
+
.addressFromScriptPublicKey(spk(input.entry), plan.network)
|
|
136
|
+
.toString(),
|
|
137
|
+
"token input script does not authenticate its state",
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
assert(total <= 0x7fffffffffffffffn, "transfer total exceeds integer limit");
|
|
141
|
+
for (const state of nextStates) {
|
|
142
|
+
assert(
|
|
143
|
+
state.amount >= 0n && hex(state.extensionCommitment) === extension,
|
|
144
|
+
"invalid output amount or extension",
|
|
145
|
+
);
|
|
146
|
+
assert(
|
|
147
|
+
profile.ownerSchemes.includes(state.ownerScheme) &&
|
|
148
|
+
[0, 1, 2, 3].includes(state.borrowScheme),
|
|
149
|
+
"unsupported output owner or borrow scheme",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
assert(
|
|
153
|
+
nextStates.reduce((sum, s) => sum + s.amount, 0n) === total,
|
|
154
|
+
"token conservation failed",
|
|
155
|
+
);
|
|
156
|
+
const authorities = plan.authorities ?? [];
|
|
157
|
+
assert(
|
|
158
|
+
authorities.length <= 255 - tokenInputs.length,
|
|
159
|
+
"too many authority inputs",
|
|
160
|
+
);
|
|
161
|
+
const authorityEvidence = authorities.map((a, i) => {
|
|
162
|
+
assert(
|
|
163
|
+
a.revealScript.length > 0 && a.revealScript.length <= 100_000,
|
|
164
|
+
"invalid authority reveal size",
|
|
165
|
+
);
|
|
166
|
+
const expected = kw.payToScriptHashScript(a.revealScript);
|
|
167
|
+
assert(
|
|
168
|
+
kw.addressFromScriptPublicKey(expected, plan.network).toString() ===
|
|
169
|
+
kw.addressFromScriptPublicKey(spk(a.entry), plan.network).toString(),
|
|
170
|
+
"authority reveal does not match its RPC output",
|
|
171
|
+
);
|
|
172
|
+
assert(
|
|
173
|
+
cid(a.entry) !== tokenId,
|
|
174
|
+
"token family inputs must be listed as token inputs",
|
|
175
|
+
);
|
|
176
|
+
assert(a.arguments.length <= 128, "authority witness exceeds limit");
|
|
177
|
+
for (const arg of a.arguments) {
|
|
178
|
+
assert(
|
|
179
|
+
["data", "i64", "signature"].includes(arg.type),
|
|
180
|
+
"unsupported authority witness argument",
|
|
181
|
+
);
|
|
182
|
+
if (arg.type === "data")
|
|
183
|
+
assert(
|
|
184
|
+
/^(?:[a-f0-9]{2})*$/i.test(arg.hex) && arg.hex.length <= 200_000,
|
|
185
|
+
"invalid authority witness data",
|
|
186
|
+
);
|
|
187
|
+
if (arg.type === "i64")
|
|
188
|
+
assert(
|
|
189
|
+
/^-?\d+$/.test(arg.value) &&
|
|
190
|
+
BigInt(arg.value) >= -0x7fffffffffffffffn &&
|
|
191
|
+
BigInt(arg.value) <= 0x7fffffffffffffffn,
|
|
192
|
+
"invalid authority integer",
|
|
193
|
+
);
|
|
194
|
+
if (arg.type === "signature") {
|
|
195
|
+
assert(
|
|
196
|
+
a.signatureAlgorithm && plan.capabilities[a.signatureAlgorithm],
|
|
197
|
+
"wallet cannot sign the authority scheme",
|
|
198
|
+
);
|
|
199
|
+
assert(
|
|
200
|
+
/^(?:[a-f0-9]{2})*$/i.test(arg.prefixHex ?? ""),
|
|
201
|
+
"invalid authority signature prefix",
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
index: tokenInputs.length + i,
|
|
207
|
+
scriptPublicKeyHex: String(expected.script),
|
|
208
|
+
covenantId: cid(a.entry),
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
const entries = [
|
|
212
|
+
...tokenInputs.map((i) => i.entry),
|
|
213
|
+
...authorities.map((i) => i.entry),
|
|
214
|
+
plan.fundingEntry,
|
|
215
|
+
];
|
|
216
|
+
const keys = entries.map((e) => {
|
|
217
|
+
const o = outpoint(e);
|
|
218
|
+
assert(
|
|
219
|
+
o &&
|
|
220
|
+
/^[0-9a-f]{64}$/i.test(String(o.transactionId)) &&
|
|
221
|
+
Number.isInteger(o.index) &&
|
|
222
|
+
o.index >= 0,
|
|
223
|
+
"invalid source outpoint",
|
|
224
|
+
);
|
|
225
|
+
return `${String(o.transactionId).toLowerCase()}:${o.index}`;
|
|
226
|
+
});
|
|
227
|
+
assert(new Set(keys).size === keys.length, "duplicate transaction input");
|
|
228
|
+
assert(!cid(plan.fundingEntry), "funding input must not spend a covenant");
|
|
229
|
+
assert(
|
|
230
|
+
kw
|
|
231
|
+
.addressFromScriptPublicKey(spk(plan.fundingEntry), plan.network)
|
|
232
|
+
.toString() === plan.fundingAddress,
|
|
233
|
+
"funding input does not belong to wallet",
|
|
234
|
+
);
|
|
235
|
+
const scripts: any[] = [];
|
|
236
|
+
for (let index = 0; index < tokenInputs.length; index++) {
|
|
237
|
+
const input = tokenInputs[index];
|
|
238
|
+
let authorization: any;
|
|
239
|
+
if (index === 0 && plan.mode === "borrow") {
|
|
240
|
+
const previous = input.state,
|
|
241
|
+
next = nextStates[0],
|
|
242
|
+
borrow = plan.borrow ?? {};
|
|
243
|
+
assert(previous.borrowScheme !== 0, "borrowing is disabled");
|
|
244
|
+
assert(
|
|
245
|
+
hex(previous.owner) === hex(next.owner) &&
|
|
246
|
+
previous.ownerScheme === next.ownerScheme &&
|
|
247
|
+
previous.borrowScheme === next.borrowScheme,
|
|
248
|
+
"borrow successor changes ownership or scheme",
|
|
249
|
+
);
|
|
250
|
+
const delta = next.amount - previous.amount;
|
|
251
|
+
let threshold = 0n;
|
|
252
|
+
if (previous.borrowScheme === 1) {
|
|
253
|
+
for (let i = 7; i >= 0; i--)
|
|
254
|
+
threshold = (threshold << 8n) | BigInt(previous.borrowGuard[i]);
|
|
255
|
+
assert(
|
|
256
|
+
(threshold & (1n << 63n)) === 0n || threshold === 1n << 63n,
|
|
257
|
+
"negative thresholds are not supported by this adapter",
|
|
258
|
+
);
|
|
259
|
+
if (threshold === 1n << 63n) threshold = 0n;
|
|
260
|
+
}
|
|
261
|
+
assert(delta > threshold, "borrow increase does not exceed threshold");
|
|
262
|
+
if (previous.borrowScheme !== 3)
|
|
263
|
+
assert(
|
|
264
|
+
hex(previous.borrowGuard) === hex(next.borrowGuard),
|
|
265
|
+
"borrow guard must be preserved",
|
|
266
|
+
);
|
|
267
|
+
else
|
|
268
|
+
assert(
|
|
269
|
+
hex(next.borrowGuard) === borrow.nextGuard,
|
|
270
|
+
"borrow successor guard differs from committed next link",
|
|
271
|
+
);
|
|
272
|
+
if (previous.borrowScheme !== 1) {
|
|
273
|
+
assert(
|
|
274
|
+
plan.capabilities.schnorr,
|
|
275
|
+
"wallet cannot sign borrowed receive",
|
|
276
|
+
);
|
|
277
|
+
assert(
|
|
278
|
+
borrow.signingPublicKey ===
|
|
279
|
+
(previous.borrowScheme === 2
|
|
280
|
+
? hex(previous.borrowGuard)
|
|
281
|
+
: borrow.oneTimePublicKey),
|
|
282
|
+
"borrow signing key does not match authorization",
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
// Validate the complete witness shape/hash link before leaving a signature slot.
|
|
286
|
+
const witness = buildTokenBorrowWitness({
|
|
287
|
+
profile,
|
|
288
|
+
borrowScheme: previous.borrowScheme,
|
|
289
|
+
currentGuard: hex(previous.borrowGuard),
|
|
290
|
+
...(previous.borrowScheme === 1
|
|
291
|
+
? {}
|
|
292
|
+
: {
|
|
293
|
+
signature: "00".repeat(64) + "01",
|
|
294
|
+
nextGuard: borrow.nextGuard,
|
|
295
|
+
oneTimePublicKey: borrow.oneTimePublicKey,
|
|
296
|
+
}),
|
|
297
|
+
});
|
|
298
|
+
authorization =
|
|
299
|
+
previous.borrowScheme === 1
|
|
300
|
+
? { argument: { type: "data", hex: hex(witness) } }
|
|
301
|
+
: {
|
|
302
|
+
argument: {
|
|
303
|
+
type: "signature",
|
|
304
|
+
prefixHex: hex(witness.slice(0, -65)),
|
|
305
|
+
},
|
|
306
|
+
signatureAlgorithm: "schnorr",
|
|
307
|
+
};
|
|
308
|
+
} else {
|
|
309
|
+
authorization = planTokenOwnerWitness({
|
|
310
|
+
profile,
|
|
311
|
+
owner: hex(input.state.owner),
|
|
312
|
+
ownerScheme: input.state.ownerScheme,
|
|
313
|
+
publicKey: input.publicKey,
|
|
314
|
+
capabilities: plan.capabilities,
|
|
315
|
+
role: index === 0 ? "leader" : "delegator",
|
|
316
|
+
authorityInputIndex: input.authorityInputIndex,
|
|
317
|
+
authorityInputs: authorityEvidence,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
scripts.push({
|
|
321
|
+
inputIndex: index,
|
|
322
|
+
scriptHex: hex(buildKcc20ScriptForState(artifact, input.state)),
|
|
323
|
+
signType: 1,
|
|
324
|
+
...(authorization.signatureAlgorithm
|
|
325
|
+
? { signatureAlgorithm: authorization.signatureAlgorithm }
|
|
326
|
+
: {}),
|
|
327
|
+
signatureScript: {
|
|
328
|
+
mode: "ordered-args",
|
|
329
|
+
args: [
|
|
330
|
+
...(index === 0
|
|
331
|
+
? encodeKcc20StateRecordArrays(nextStates).map((v: Uint8Array) => ({
|
|
332
|
+
type: "data",
|
|
333
|
+
hex: hex(v),
|
|
334
|
+
}))
|
|
335
|
+
: []),
|
|
336
|
+
authorization.argument,
|
|
337
|
+
{
|
|
338
|
+
type: "data",
|
|
339
|
+
hex: index === 0 ? profile.transferTag : profile.delegatorTag,
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
for (let i = 0; i < authorities.length; i++)
|
|
346
|
+
scripts.push({
|
|
347
|
+
inputIndex: tokenInputs.length + i,
|
|
348
|
+
scriptHex: hex(authorities[i].revealScript),
|
|
349
|
+
signType: 1,
|
|
350
|
+
signatureAlgorithm: authorities[i].signatureAlgorithm,
|
|
351
|
+
signatureScript: { mode: "ordered-args", args: authorities[i].arguments },
|
|
352
|
+
});
|
|
353
|
+
const tokenValue = plan.tokenOutputSompi ?? 50_000_000n;
|
|
354
|
+
assert(tokenValue > 0n, "token output KAS must be positive");
|
|
355
|
+
if (plan.mode === "borrow")
|
|
356
|
+
assert(
|
|
357
|
+
tokenValue >= amount(tokenInputs[0].entry),
|
|
358
|
+
"borrow successor must preserve its KAS value",
|
|
359
|
+
);
|
|
360
|
+
const outputs = nextStates.map(
|
|
361
|
+
(state) =>
|
|
362
|
+
new kw.TransactionOutput(
|
|
363
|
+
tokenValue,
|
|
364
|
+
kw.payToScriptHashScript(buildKcc20ScriptForState(artifact, state)),
|
|
365
|
+
new kw.CovenantBinding(0, new kw.Hash(tokenId)),
|
|
366
|
+
),
|
|
367
|
+
);
|
|
368
|
+
for (let i = 0; i < authorities.length; i++) {
|
|
369
|
+
const entry = authorities[i].entry,
|
|
370
|
+
identity = cid(entry);
|
|
371
|
+
outputs.push(
|
|
372
|
+
new kw.TransactionOutput(
|
|
373
|
+
amount(entry),
|
|
374
|
+
spk(entry),
|
|
375
|
+
identity
|
|
376
|
+
? new kw.CovenantBinding(
|
|
377
|
+
tokenInputs.length + i,
|
|
378
|
+
new kw.Hash(identity),
|
|
379
|
+
)
|
|
380
|
+
: undefined,
|
|
381
|
+
),
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
const change =
|
|
385
|
+
entries.reduce((sum, e) => sum + amount(e), 0n) -
|
|
386
|
+
BigInt(nextStates.length) * tokenValue -
|
|
387
|
+
authorities.reduce((sum, a) => sum + amount(a.entry), 0n) -
|
|
388
|
+
plan.feeSompi;
|
|
389
|
+
assert(change > 10_000n, "insufficient funding or change");
|
|
390
|
+
outputs.push(
|
|
391
|
+
new kw.TransactionOutput(
|
|
392
|
+
change,
|
|
393
|
+
kw.payToAddressScript(plan.fundingAddress),
|
|
394
|
+
),
|
|
395
|
+
);
|
|
396
|
+
const transaction = new kw.Transaction({
|
|
397
|
+
version: 1,
|
|
398
|
+
lockTime: 0n,
|
|
399
|
+
subnetworkId: "00".repeat(20),
|
|
400
|
+
gas: 0n,
|
|
401
|
+
payload: "",
|
|
402
|
+
inputs: entries.map((e) => ({
|
|
403
|
+
previousOutpoint: outpoint(e),
|
|
404
|
+
utxo: e,
|
|
405
|
+
sequence: 0n,
|
|
406
|
+
sigOpCount: 0,
|
|
407
|
+
computeBudget: 1000,
|
|
408
|
+
})),
|
|
409
|
+
outputs,
|
|
410
|
+
});
|
|
411
|
+
transaction.inputs[entries.length - 1].computeBudget = 30;
|
|
412
|
+
return {
|
|
413
|
+
schema: "kcc20-in-process-pskt-builder-output/v1",
|
|
414
|
+
psktTransactionJson: transaction.serializeToSafeJSON(),
|
|
415
|
+
scripts,
|
|
416
|
+
signInputs: [{ index: entries.length - 1, sighashType: 1 }],
|
|
417
|
+
submitTransactionSupported: true,
|
|
418
|
+
metadata: {
|
|
419
|
+
tokenProfileId: profile.id,
|
|
420
|
+
covenantId: tokenId,
|
|
421
|
+
mode: plan.mode,
|
|
422
|
+
fundingChangeOutputIndex: outputs.length - 1,
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
}
|