@kaspacom/kcc20-tx-builder 0.8.0-fee.13 → 0.8.0-fee.14
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/dist/cjs/engine.js +147 -91
- package/dist/cjs/engine.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +147 -91
- package/dist/engine.js.map +1 -1
- package/docs/COMPATIBILITY.json +1 -1
- package/package.json +1 -1
- package/src/engine.ts +140 -93
package/docs/COMPATIBILITY.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema": "kcc20-example-compatibility/v1",
|
|
3
|
-
"packageVersion": "0.8.0-fee.
|
|
3
|
+
"packageVersion": "0.8.0-fee.14",
|
|
4
4
|
"contractsRepository": "https://github.com/KASPACOM/kaspa-covenants",
|
|
5
5
|
"contractsCommit": "1a15362b35661464587cf07ffe8a42186a545897",
|
|
6
6
|
"compiler": {
|
package/package.json
CHANGED
package/src/engine.ts
CHANGED
|
@@ -5517,101 +5517,148 @@ export function createKcc20PsktBuilderEngine(
|
|
|
5517
5517
|
: priorityFee + 10_000n;
|
|
5518
5518
|
const fundingEntry = selectFundingEntry(walletUtxos.entries, fundingNeeded,
|
|
5519
5519
|
spentEntries.map(entry => entry.outpoint), params.fundingOutpoint);
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
setVersionOneInputMassFields(transaction, 1000);
|
|
5539
|
-
for (let slot = 0; slot < legs.length; slot++) transaction.inputs[slot + 1].computeBudget = 100;
|
|
5540
|
-
const ticketInputIndex = ticketEntry ? legs.length + 1 : -1;
|
|
5541
|
-
if (ticketEntry) transaction.inputs[ticketInputIndex].computeBudget = 600;
|
|
5542
|
-
const fundingInputIndex = legs.length + 1 + (ticketEntry ? 1 : 0);
|
|
5543
|
-
transaction.inputs[fundingInputIndex].computeBudget = 30;
|
|
5544
|
-
assertUniqueTransactionInputOutpoints(transaction, "fee-market seller group");
|
|
5545
|
-
const legBytes = new Uint8Array(legs.length * 32);
|
|
5546
|
-
const view = new DataView(legBytes.buffer);
|
|
5547
|
-
for (let slot = 0; slot < legs.length; slot++) {
|
|
5548
|
-
const indexes = legIndexes[slot];
|
|
5549
|
-
for (const [field, value] of [legs[slot].fill, indexes.buyerIndex,
|
|
5550
|
-
indexes.continuationIndex < 0 ? 0 : indexes.continuationIndex,
|
|
5551
|
-
indexes.sellerIndex].entries()) {
|
|
5552
|
-
view.setBigInt64(slot * 32 + field * 8, BigInt(value), true);
|
|
5553
|
-
}
|
|
5554
|
-
}
|
|
5555
|
-
const receiptParts = templateParts(normalizeSilverScriptArtifact(artifacts.receipt));
|
|
5556
|
-
const scripts = [{ inputIndex: 0,
|
|
5557
|
-
scriptHex: bytesToHex(holderPlan.holder.redeemScript), signType: 1,
|
|
5558
|
-
signatureScript: { mode: "ordered-args", args: [
|
|
5559
|
-
{ type: "signature" }, i64TemplateArg(legs.length), dataTemplateArg(legBytes),
|
|
5560
|
-
dataTemplateArg(vector), i64TemplateArg(ticketInputIndex), i64TemplateArg(receiptOutputIndex),
|
|
5561
|
-
i64TemplateArg(changeIndex), i64TemplateArg(refundIndex), i64TemplateArg(ticketRefundIndex),
|
|
5562
|
-
dataTemplateArg(feeTotal > 0n ? receiptParts.prefix : new Uint8Array()),
|
|
5563
|
-
dataTemplateArg(feeTotal > 0n ? receiptParts.suffix : new Uint8Array()),
|
|
5564
|
-
dataTemplateArg(dispatchTagFor(artifacts.holder, "sellIntoBids")),
|
|
5565
|
-
] } }];
|
|
5566
|
-
if (ticketEntry) scripts.push(feeTicketBurnScriptHint(ticketInputIndex,
|
|
5567
|
-
ticket.script, ticket.burnDispatchTag, ticketRefundIndex,
|
|
5568
|
-
ticket.burnTakesRefundOutput));
|
|
5569
|
-
for (let slot = 0; slot < legs.length; slot++) {
|
|
5570
|
-
const delegate = covenantScriptBuilder(kw);
|
|
5571
|
-
for (const value of [0, slot, legs[slot].remaining,
|
|
5572
|
-
legIndexes[slot].continuationIndex]) delegate.addI64(BigInt(value));
|
|
5573
|
-
delegate.addData(dispatchTagFor(artifacts.bid, "delegateSellerGroup"));
|
|
5574
|
-
transaction.inputs[slot + 1].signatureScript = encodeCovenantP2shSignatureScript(
|
|
5575
|
-
delegate.drain(), legs[slot].bidScript.redeemScript);
|
|
5576
|
-
}
|
|
5577
|
-
const feeResult = transactionWithPredictedSignedFeeChange(
|
|
5578
|
-
kw, transaction, fundingChangeIndex, network, "fee-market seller group", candidate => {
|
|
5579
|
-
const serialized = candidate.serializeToSafeJSON();
|
|
5580
|
-
if (typeof serialized !== "string") throw new Error("seller group preflight serialization failed");
|
|
5581
|
-
const predicted = transactionJsonWithScriptHints(kw, JSON.parse(serialized), scripts);
|
|
5582
|
-
predicted.inputs[fundingInputIndex].signatureScript = encodeP2pkSignatureScript(
|
|
5583
|
-
bytesToHex(dummySignatureBytes()));
|
|
5584
|
-
return typeof kw.Transaction.deserializeFromSafeJSON === "function"
|
|
5585
|
-
? kw.Transaction.deserializeFromSafeJSON(JSON.stringify(predicted))
|
|
5586
|
-
: kw.Transaction.deserializeFromJSON(JSON.stringify(predicted));
|
|
5520
|
+
let estimatedFundingFeeSompi = priorityFee;
|
|
5521
|
+
const buildWithFunding = (fundingEntry) => {
|
|
5522
|
+
const fundingChange = covenantSompi + utxoAmountSompi(fundingEntry) - outputSompi - priorityFee;
|
|
5523
|
+
if (fundingChange <= 10_000n) throw new Error("seller group funding change is too small");
|
|
5524
|
+
const fundingChangeIndex = outputs.length;
|
|
5525
|
+
const candidateOutputs = [...outputs,
|
|
5526
|
+
new TransactionOutput(fundingChange, payToAddressScript(walletAddress))];
|
|
5527
|
+
const transaction = new Transaction({ version: 1, lockTime: 0n,
|
|
5528
|
+
inputs: [
|
|
5529
|
+
{ previousOutpoint: holderEntry.outpoint, utxo: holderEntry,
|
|
5530
|
+
sequence: 0n, sigOpCount: 0, computeBudget: 1000 },
|
|
5531
|
+
...bidEntries.map(({ entry }) => ({ previousOutpoint: entry.outpoint, utxo: entry,
|
|
5532
|
+
sequence: 0n, sigOpCount: 0, computeBudget: 100 })),
|
|
5533
|
+
...(ticketEntry ? [{ previousOutpoint: ticketEntry.outpoint, utxo: ticketEntry,
|
|
5534
|
+
sequence: 0n, sigOpCount: 0, computeBudget: 600 }] : []),
|
|
5535
|
+
{ previousOutpoint: fundingEntry.outpoint, utxo: fundingEntry,
|
|
5536
|
+
sequence: 0n, sigOpCount: 0, computeBudget: 30 },
|
|
5537
|
+
], outputs: candidateOutputs, subnetworkId: SUBNETWORK_ID_NATIVE, gas: 0n, payload: "",
|
|
5587
5538
|
});
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5539
|
+
// Pinned signed consensus proof uses 394 units for ten-bid leader and
|
|
5540
|
+
// 43 for each bid delegator. Leave more than 2x headroom per role.
|
|
5541
|
+
setVersionOneInputMassFields(transaction, 1000);
|
|
5542
|
+
for (let slot = 0; slot < legs.length; slot++) transaction.inputs[slot + 1].computeBudget = 100;
|
|
5543
|
+
const ticketInputIndex = ticketEntry ? legs.length + 1 : -1;
|
|
5544
|
+
if (ticketEntry) transaction.inputs[ticketInputIndex].computeBudget = 600;
|
|
5545
|
+
const fundingInputIndex = legs.length + 1 + (ticketEntry ? 1 : 0);
|
|
5546
|
+
transaction.inputs[fundingInputIndex].computeBudget = 30;
|
|
5547
|
+
assertUniqueTransactionInputOutpoints(transaction, "fee-market seller group");
|
|
5548
|
+
const legBytes = new Uint8Array(legs.length * 32);
|
|
5549
|
+
const view = new DataView(legBytes.buffer);
|
|
5550
|
+
for (let slot = 0; slot < legs.length; slot++) {
|
|
5551
|
+
const indexes = legIndexes[slot];
|
|
5552
|
+
for (const [field, value] of [legs[slot].fill, indexes.buyerIndex,
|
|
5553
|
+
indexes.continuationIndex < 0 ? 0 : indexes.continuationIndex,
|
|
5554
|
+
indexes.sellerIndex].entries()) {
|
|
5555
|
+
view.setBigInt64(slot * 32 + field * 8, BigInt(value), true);
|
|
5556
|
+
}
|
|
5557
|
+
}
|
|
5558
|
+
const receiptParts = templateParts(normalizeSilverScriptArtifact(artifacts.receipt));
|
|
5559
|
+
const scripts = [{ inputIndex: 0,
|
|
5560
|
+
scriptHex: bytesToHex(holderPlan.holder.redeemScript), signType: 1,
|
|
5561
|
+
signatureScript: { mode: "ordered-args", args: [
|
|
5562
|
+
{ type: "signature" }, i64TemplateArg(legs.length), dataTemplateArg(legBytes),
|
|
5563
|
+
dataTemplateArg(vector), i64TemplateArg(ticketInputIndex), i64TemplateArg(receiptOutputIndex),
|
|
5564
|
+
i64TemplateArg(changeIndex), i64TemplateArg(refundIndex), i64TemplateArg(ticketRefundIndex),
|
|
5565
|
+
dataTemplateArg(feeTotal > 0n ? receiptParts.prefix : new Uint8Array()),
|
|
5566
|
+
dataTemplateArg(feeTotal > 0n ? receiptParts.suffix : new Uint8Array()),
|
|
5567
|
+
dataTemplateArg(dispatchTagFor(artifacts.holder, "sellIntoBids")),
|
|
5568
|
+
] } }];
|
|
5569
|
+
if (ticketEntry) scripts.push(feeTicketBurnScriptHint(ticketInputIndex,
|
|
5570
|
+
ticket.script, ticket.burnDispatchTag, ticketRefundIndex,
|
|
5571
|
+
ticket.burnTakesRefundOutput));
|
|
5572
|
+
for (let slot = 0; slot < legs.length; slot++) {
|
|
5573
|
+
const delegate = covenantScriptBuilder(kw);
|
|
5574
|
+
for (const value of [0, slot, legs[slot].remaining,
|
|
5575
|
+
legIndexes[slot].continuationIndex]) delegate.addI64(BigInt(value));
|
|
5576
|
+
delegate.addData(dispatchTagFor(artifacts.bid, "delegateSellerGroup"));
|
|
5577
|
+
transaction.inputs[slot + 1].signatureScript = encodeCovenantP2shSignatureScript(
|
|
5578
|
+
delegate.drain(), legs[slot].bidScript.redeemScript);
|
|
5579
|
+
}
|
|
5580
|
+
const feeResult = transactionWithPredictedSignedFeeChange(
|
|
5581
|
+
kw, transaction, fundingChangeIndex, network, "fee-market seller group", candidate => {
|
|
5582
|
+
const serialized = candidate.serializeToSafeJSON();
|
|
5583
|
+
if (typeof serialized !== "string") throw new Error("seller group preflight serialization failed");
|
|
5584
|
+
const predicted = transactionJsonWithScriptHints(kw, JSON.parse(serialized), scripts);
|
|
5585
|
+
predicted.inputs[fundingInputIndex].signatureScript = encodeP2pkSignatureScript(
|
|
5586
|
+
bytesToHex(dummySignatureBytes()));
|
|
5587
|
+
return typeof kw.Transaction.deserializeFromSafeJSON === "function"
|
|
5588
|
+
? kw.Transaction.deserializeFromSafeJSON(JSON.stringify(predicted))
|
|
5589
|
+
: kw.Transaction.deserializeFromJSON(JSON.stringify(predicted));
|
|
5590
|
+
});
|
|
5591
|
+
estimatedFundingFeeSompi = feeResult.requiredFeeSompi;
|
|
5592
|
+
const signedMass = calculateToccataV1NonContextualMass(
|
|
5593
|
+
feeResult.predictedTransaction, "fee-market seller group");
|
|
5594
|
+
const storageMass = calculateToccataStorageMass(
|
|
5595
|
+
feeResult.predictedTransaction, "fee-market seller group");
|
|
5596
|
+
if (signedMass.computeMass > CONSOLIDATION_TARGET_TRANSACTION_MASS ||
|
|
5597
|
+
signedMass.transientMass > CONSOLIDATION_TARGET_TRANSACTION_MASS * 2n ||
|
|
5598
|
+
storageMass > CONSOLIDATION_TARGET_TRANSACTION_MASS) {
|
|
5599
|
+
throw kcc20PsktBuilderError("fee-market seller group exceeds signed mass target",
|
|
5600
|
+
"TRANSACTION_MASS_EXCEEDED", { computeMass: signedMass.computeMass,
|
|
5601
|
+
transientMass: signedMass.transientMass, storageMass });
|
|
5602
|
+
}
|
|
5603
|
+
return { schema: OUTPUT_SCHEMA,
|
|
5604
|
+
psktTransactionJson: feeResult.transaction.serializeToSafeJSON(),
|
|
5605
|
+
signInputs: [{ index: fundingInputIndex, sighashType: 1 }], scripts,
|
|
5606
|
+
submitTransactionSupported: true,
|
|
5607
|
+
metadata: { builderKey: "kcc20fee.sell-into-bids",
|
|
5608
|
+
operation: "fee-market-sell-into-bids", network, walletAddress,
|
|
5609
|
+
tokenId, marketId, policyId, fillCount: legs.length,
|
|
5610
|
+
soldAmount: sold.toString(),
|
|
5611
|
+
grossSompi: legs.reduce((sum, leg) => sum + leg.gross, 0n).toString(),
|
|
5612
|
+
protocolFeeSompi: protocolTotal.toString(),
|
|
5613
|
+
creatorFeeSompi: creatorTotal.toString(), receiptOutputIndex,
|
|
5614
|
+
ticketInputIndex, ticketRefundIndex,
|
|
5615
|
+
sellerChangeIndex: changeIndex, sellerRefundIndex: refundIndex,
|
|
5616
|
+
signedMass: { compute: signedMass.computeMass.toString(),
|
|
5617
|
+
transient: signedMass.transientMass.toString(), storage: storageMass.toString() } },
|
|
5618
|
+
};
|
|
5614
5619
|
};
|
|
5620
|
+
try {
|
|
5621
|
+
return buildWithFunding(fundingEntry);
|
|
5622
|
+
} catch (initialError) {
|
|
5623
|
+
// A large funding input can increase contextual storage mass. Preserve
|
|
5624
|
+
// caller-selected inputs; otherwise try at most seven smaller live
|
|
5625
|
+
// wallet UTXO amounts without changing the trade, fees, or mass limits.
|
|
5626
|
+
const details = initialError?.builderErrorDetails;
|
|
5627
|
+
if (params.fundingOutpoint != null ||
|
|
5628
|
+
initialError?.builderErrorCode !== "TRANSACTION_MASS_EXCEEDED" ||
|
|
5629
|
+
BigInt(details?.computeMass ?? 0) > CONSOLIDATION_TARGET_TRANSACTION_MASS ||
|
|
5630
|
+
BigInt(details?.transientMass ?? 0) > CONSOLIDATION_TARGET_TRANSACTION_MASS * 2n ||
|
|
5631
|
+
BigInt(details?.storageMass ?? 0) <= CONSOLIDATION_TARGET_TRANSACTION_MASS) {
|
|
5632
|
+
throw initialError;
|
|
5633
|
+
}
|
|
5634
|
+
const skipped = [...spentEntries.map(entry => entry.outpoint), fundingEntry.outpoint];
|
|
5635
|
+
const conservativeFundingNeeded = fundingNeeded +
|
|
5636
|
+
(estimatedFundingFeeSompi > priorityFee ? estimatedFundingFeeSompi - priorityFee : 0n);
|
|
5637
|
+
const candidates = walletUtxos.entries.filter(entry =>
|
|
5638
|
+
utxoAmountSompi(entry) >= conservativeFundingNeeded &&
|
|
5639
|
+
utxoAmountSompi(entry) < utxoAmountSompi(fundingEntry))
|
|
5640
|
+
.sort((a, b) => utxoAmountSompi(a) < utxoAmountSompi(b) ? -1 :
|
|
5641
|
+
utxoAmountSompi(a) > utxoAmountSompi(b) ? 1 : 0);
|
|
5642
|
+
let attempted = 0;
|
|
5643
|
+
const attemptedAmounts = new Set<string>();
|
|
5644
|
+
for (const candidate of candidates) {
|
|
5645
|
+
if (attempted === 7) break;
|
|
5646
|
+
const amountKey = utxoAmountSompi(candidate).toString();
|
|
5647
|
+
if (attemptedAmounts.has(amountKey)) continue;
|
|
5648
|
+
let selected;
|
|
5649
|
+
try { selected = selectFundingEntry([candidate], fundingNeeded, skipped, params.fundingOutpoint); }
|
|
5650
|
+
catch { continue; }
|
|
5651
|
+
skipped.push(selected.outpoint);
|
|
5652
|
+
attemptedAmounts.add(amountKey);
|
|
5653
|
+
attempted++;
|
|
5654
|
+
try { return buildWithFunding(selected); }
|
|
5655
|
+
catch (error) {
|
|
5656
|
+
if (!["TRANSACTION_MASS_EXCEEDED", "KAS_FUNDING_CHANGE_TOO_SMALL",
|
|
5657
|
+
"KAS_FUNDING_FEE_INSUFFICIENT"].includes(error?.builderErrorCode)) throw error;
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5660
|
+
throw initialError;
|
|
5661
|
+
}
|
|
5615
5662
|
} finally { await rpc.disconnect(); }
|
|
5616
5663
|
}
|
|
5617
5664
|
|