@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.
@@ -4329,99 +4329,155 @@ function createKcc20PsktBuilderEngine(options) {
4329
4329
  ? outputSompi + priorityFee - covenantSompi + 10000n
4330
4330
  : priorityFee + 10000n;
4331
4331
  const fundingEntry = (0, sale_common_js_1.selectFundingEntry)(walletUtxos.entries, fundingNeeded, spentEntries.map(entry => entry.outpoint), params.fundingOutpoint);
4332
- const fundingChange = covenantSompi + (0, sale_common_js_1.utxoAmountSompi)(fundingEntry) - outputSompi - priorityFee;
4333
- if (fundingChange <= 10000n)
4334
- throw new Error("seller group funding change is too small");
4335
- const fundingChangeIndex = outputs.length;
4336
- outputs.push(new TransactionOutput(fundingChange, payToAddressScript(walletAddress)));
4337
- const transaction = new Transaction({ version: 1, lockTime: 0n,
4338
- inputs: [
4339
- { previousOutpoint: holderEntry.outpoint, utxo: holderEntry,
4340
- sequence: 0n, sigOpCount: 0, computeBudget: 1000 },
4341
- ...bidEntries.map(({ entry }) => ({ previousOutpoint: entry.outpoint, utxo: entry,
4342
- sequence: 0n, sigOpCount: 0, computeBudget: 100 })),
4343
- ...(ticketEntry ? [{ previousOutpoint: ticketEntry.outpoint, utxo: ticketEntry,
4344
- sequence: 0n, sigOpCount: 0, computeBudget: 600 }] : []),
4345
- { previousOutpoint: fundingEntry.outpoint, utxo: fundingEntry,
4346
- sequence: 0n, sigOpCount: 0, computeBudget: 30 },
4347
- ], outputs, subnetworkId: sale_common_js_1.SUBNETWORK_ID_NATIVE, gas: 0n, payload: "",
4348
- });
4349
- // Pinned signed consensus proof uses 394 units for ten-bid leader and
4350
- // 43 for each bid delegator. Leave more than 2x headroom per role.
4351
- (0, sale_common_js_1.setVersionOneInputMassFields)(transaction, 1000);
4352
- for (let slot = 0; slot < legs.length; slot++)
4353
- transaction.inputs[slot + 1].computeBudget = 100;
4354
- const ticketInputIndex = ticketEntry ? legs.length + 1 : -1;
4355
- if (ticketEntry)
4356
- transaction.inputs[ticketInputIndex].computeBudget = 600;
4357
- const fundingInputIndex = legs.length + 1 + (ticketEntry ? 1 : 0);
4358
- transaction.inputs[fundingInputIndex].computeBudget = 30;
4359
- assertUniqueTransactionInputOutpoints(transaction, "fee-market seller group");
4360
- const legBytes = new Uint8Array(legs.length * 32);
4361
- const view = new DataView(legBytes.buffer);
4362
- for (let slot = 0; slot < legs.length; slot++) {
4363
- const indexes = legIndexes[slot];
4364
- for (const [field, value] of [legs[slot].fill, indexes.buyerIndex,
4365
- indexes.continuationIndex < 0 ? 0 : indexes.continuationIndex,
4366
- indexes.sellerIndex].entries()) {
4367
- view.setBigInt64(slot * 32 + field * 8, BigInt(value), true);
4332
+ let estimatedFundingFeeSompi = priorityFee;
4333
+ const buildWithFunding = (fundingEntry) => {
4334
+ const fundingChange = covenantSompi + (0, sale_common_js_1.utxoAmountSompi)(fundingEntry) - outputSompi - priorityFee;
4335
+ if (fundingChange <= 10000n)
4336
+ throw new Error("seller group funding change is too small");
4337
+ const fundingChangeIndex = outputs.length;
4338
+ const candidateOutputs = [...outputs,
4339
+ new TransactionOutput(fundingChange, payToAddressScript(walletAddress))];
4340
+ const transaction = new Transaction({ version: 1, lockTime: 0n,
4341
+ inputs: [
4342
+ { previousOutpoint: holderEntry.outpoint, utxo: holderEntry,
4343
+ sequence: 0n, sigOpCount: 0, computeBudget: 1000 },
4344
+ ...bidEntries.map(({ entry }) => ({ previousOutpoint: entry.outpoint, utxo: entry,
4345
+ sequence: 0n, sigOpCount: 0, computeBudget: 100 })),
4346
+ ...(ticketEntry ? [{ previousOutpoint: ticketEntry.outpoint, utxo: ticketEntry,
4347
+ sequence: 0n, sigOpCount: 0, computeBudget: 600 }] : []),
4348
+ { previousOutpoint: fundingEntry.outpoint, utxo: fundingEntry,
4349
+ sequence: 0n, sigOpCount: 0, computeBudget: 30 },
4350
+ ], outputs: candidateOutputs, subnetworkId: sale_common_js_1.SUBNETWORK_ID_NATIVE, gas: 0n, payload: "",
4351
+ });
4352
+ // Pinned signed consensus proof uses 394 units for ten-bid leader and
4353
+ // 43 for each bid delegator. Leave more than 2x headroom per role.
4354
+ (0, sale_common_js_1.setVersionOneInputMassFields)(transaction, 1000);
4355
+ for (let slot = 0; slot < legs.length; slot++)
4356
+ transaction.inputs[slot + 1].computeBudget = 100;
4357
+ const ticketInputIndex = ticketEntry ? legs.length + 1 : -1;
4358
+ if (ticketEntry)
4359
+ transaction.inputs[ticketInputIndex].computeBudget = 600;
4360
+ const fundingInputIndex = legs.length + 1 + (ticketEntry ? 1 : 0);
4361
+ transaction.inputs[fundingInputIndex].computeBudget = 30;
4362
+ assertUniqueTransactionInputOutpoints(transaction, "fee-market seller group");
4363
+ const legBytes = new Uint8Array(legs.length * 32);
4364
+ const view = new DataView(legBytes.buffer);
4365
+ for (let slot = 0; slot < legs.length; slot++) {
4366
+ const indexes = legIndexes[slot];
4367
+ for (const [field, value] of [legs[slot].fill, indexes.buyerIndex,
4368
+ indexes.continuationIndex < 0 ? 0 : indexes.continuationIndex,
4369
+ indexes.sellerIndex].entries()) {
4370
+ view.setBigInt64(slot * 32 + field * 8, BigInt(value), true);
4371
+ }
4368
4372
  }
4369
- }
4370
- const receiptParts = templateParts((0, abi_js_1.normalizeSilverScriptArtifact)(artifacts.receipt));
4371
- const scripts = [{ inputIndex: 0,
4372
- scriptHex: (0, sale_common_js_1.bytesToHex)(holderPlan.holder.redeemScript), signType: 1,
4373
- signatureScript: { mode: "ordered-args", args: [
4374
- { type: "signature" }, i64TemplateArg(legs.length), dataTemplateArg(legBytes),
4375
- dataTemplateArg(vector), i64TemplateArg(ticketInputIndex), i64TemplateArg(receiptOutputIndex),
4376
- i64TemplateArg(changeIndex), i64TemplateArg(refundIndex), i64TemplateArg(ticketRefundIndex),
4377
- dataTemplateArg(feeTotal > 0n ? receiptParts.prefix : new Uint8Array()),
4378
- dataTemplateArg(feeTotal > 0n ? receiptParts.suffix : new Uint8Array()),
4379
- dataTemplateArg((0, abi_js_1.dispatchTagFor)(artifacts.holder, "sellIntoBids")),
4380
- ] } }];
4381
- if (ticketEntry)
4382
- scripts.push(feeTicketBurnScriptHint(ticketInputIndex, ticket.script, ticket.burnDispatchTag, ticketRefundIndex, ticket.burnTakesRefundOutput));
4383
- for (let slot = 0; slot < legs.length; slot++) {
4384
- const delegate = covenantScriptBuilder(kw);
4385
- for (const value of [0, slot, legs[slot].remaining,
4386
- legIndexes[slot].continuationIndex])
4387
- delegate.addI64(BigInt(value));
4388
- delegate.addData((0, abi_js_1.dispatchTagFor)(artifacts.bid, "delegateSellerGroup"));
4389
- transaction.inputs[slot + 1].signatureScript = encodeCovenantP2shSignatureScript(delegate.drain(), legs[slot].bidScript.redeemScript);
4390
- }
4391
- const feeResult = transactionWithPredictedSignedFeeChange(kw, transaction, fundingChangeIndex, network, "fee-market seller group", candidate => {
4392
- const serialized = candidate.serializeToSafeJSON();
4393
- if (typeof serialized !== "string")
4394
- throw new Error("seller group preflight serialization failed");
4395
- const predicted = transactionJsonWithScriptHints(kw, JSON.parse(serialized), scripts);
4396
- predicted.inputs[fundingInputIndex].signatureScript = encodeP2pkSignatureScript((0, sale_common_js_1.bytesToHex)(dummySignatureBytes()));
4397
- return typeof kw.Transaction.deserializeFromSafeJSON === "function"
4398
- ? kw.Transaction.deserializeFromSafeJSON(JSON.stringify(predicted))
4399
- : kw.Transaction.deserializeFromJSON(JSON.stringify(predicted));
4400
- });
4401
- const signedMass = calculateToccataV1NonContextualMass(feeResult.predictedTransaction, "fee-market seller group");
4402
- const storageMass = calculateToccataStorageMass(feeResult.predictedTransaction, "fee-market seller group");
4403
- if (signedMass.computeMass > CONSOLIDATION_TARGET_TRANSACTION_MASS ||
4404
- signedMass.transientMass > CONSOLIDATION_TARGET_TRANSACTION_MASS * 2n ||
4405
- storageMass > CONSOLIDATION_TARGET_TRANSACTION_MASS) {
4406
- throw (0, sale_common_js_1.kcc20PsktBuilderError)("fee-market seller group exceeds signed mass target", "TRANSACTION_MASS_EXCEEDED", { computeMass: signedMass.computeMass,
4407
- transientMass: signedMass.transientMass, storageMass });
4408
- }
4409
- return { schema: OUTPUT_SCHEMA,
4410
- psktTransactionJson: feeResult.transaction.serializeToSafeJSON(),
4411
- signInputs: [{ index: fundingInputIndex, sighashType: 1 }], scripts,
4412
- submitTransactionSupported: true,
4413
- metadata: { builderKey: "kcc20fee.sell-into-bids",
4414
- operation: "fee-market-sell-into-bids", network, walletAddress,
4415
- tokenId, marketId, policyId, fillCount: legs.length,
4416
- soldAmount: sold.toString(),
4417
- grossSompi: legs.reduce((sum, leg) => sum + leg.gross, 0n).toString(),
4418
- protocolFeeSompi: protocolTotal.toString(),
4419
- creatorFeeSompi: creatorTotal.toString(), receiptOutputIndex,
4420
- ticketInputIndex, ticketRefundIndex,
4421
- sellerChangeIndex: changeIndex, sellerRefundIndex: refundIndex,
4422
- signedMass: { compute: signedMass.computeMass.toString(),
4423
- transient: signedMass.transientMass.toString(), storage: storageMass.toString() } },
4373
+ const receiptParts = templateParts((0, abi_js_1.normalizeSilverScriptArtifact)(artifacts.receipt));
4374
+ const scripts = [{ inputIndex: 0,
4375
+ scriptHex: (0, sale_common_js_1.bytesToHex)(holderPlan.holder.redeemScript), signType: 1,
4376
+ signatureScript: { mode: "ordered-args", args: [
4377
+ { type: "signature" }, i64TemplateArg(legs.length), dataTemplateArg(legBytes),
4378
+ dataTemplateArg(vector), i64TemplateArg(ticketInputIndex), i64TemplateArg(receiptOutputIndex),
4379
+ i64TemplateArg(changeIndex), i64TemplateArg(refundIndex), i64TemplateArg(ticketRefundIndex),
4380
+ dataTemplateArg(feeTotal > 0n ? receiptParts.prefix : new Uint8Array()),
4381
+ dataTemplateArg(feeTotal > 0n ? receiptParts.suffix : new Uint8Array()),
4382
+ dataTemplateArg((0, abi_js_1.dispatchTagFor)(artifacts.holder, "sellIntoBids")),
4383
+ ] } }];
4384
+ if (ticketEntry)
4385
+ scripts.push(feeTicketBurnScriptHint(ticketInputIndex, ticket.script, ticket.burnDispatchTag, ticketRefundIndex, ticket.burnTakesRefundOutput));
4386
+ for (let slot = 0; slot < legs.length; slot++) {
4387
+ const delegate = covenantScriptBuilder(kw);
4388
+ for (const value of [0, slot, legs[slot].remaining,
4389
+ legIndexes[slot].continuationIndex])
4390
+ delegate.addI64(BigInt(value));
4391
+ delegate.addData((0, abi_js_1.dispatchTagFor)(artifacts.bid, "delegateSellerGroup"));
4392
+ transaction.inputs[slot + 1].signatureScript = encodeCovenantP2shSignatureScript(delegate.drain(), legs[slot].bidScript.redeemScript);
4393
+ }
4394
+ const feeResult = transactionWithPredictedSignedFeeChange(kw, transaction, fundingChangeIndex, network, "fee-market seller group", candidate => {
4395
+ const serialized = candidate.serializeToSafeJSON();
4396
+ if (typeof serialized !== "string")
4397
+ throw new Error("seller group preflight serialization failed");
4398
+ const predicted = transactionJsonWithScriptHints(kw, JSON.parse(serialized), scripts);
4399
+ predicted.inputs[fundingInputIndex].signatureScript = encodeP2pkSignatureScript((0, sale_common_js_1.bytesToHex)(dummySignatureBytes()));
4400
+ return typeof kw.Transaction.deserializeFromSafeJSON === "function"
4401
+ ? kw.Transaction.deserializeFromSafeJSON(JSON.stringify(predicted))
4402
+ : kw.Transaction.deserializeFromJSON(JSON.stringify(predicted));
4403
+ });
4404
+ estimatedFundingFeeSompi = feeResult.requiredFeeSompi;
4405
+ const signedMass = calculateToccataV1NonContextualMass(feeResult.predictedTransaction, "fee-market seller group");
4406
+ const storageMass = calculateToccataStorageMass(feeResult.predictedTransaction, "fee-market seller group");
4407
+ if (signedMass.computeMass > CONSOLIDATION_TARGET_TRANSACTION_MASS ||
4408
+ signedMass.transientMass > CONSOLIDATION_TARGET_TRANSACTION_MASS * 2n ||
4409
+ storageMass > CONSOLIDATION_TARGET_TRANSACTION_MASS) {
4410
+ throw (0, sale_common_js_1.kcc20PsktBuilderError)("fee-market seller group exceeds signed mass target", "TRANSACTION_MASS_EXCEEDED", { computeMass: signedMass.computeMass,
4411
+ transientMass: signedMass.transientMass, storageMass });
4412
+ }
4413
+ return { schema: OUTPUT_SCHEMA,
4414
+ psktTransactionJson: feeResult.transaction.serializeToSafeJSON(),
4415
+ signInputs: [{ index: fundingInputIndex, sighashType: 1 }], scripts,
4416
+ submitTransactionSupported: true,
4417
+ metadata: { builderKey: "kcc20fee.sell-into-bids",
4418
+ operation: "fee-market-sell-into-bids", network, walletAddress,
4419
+ tokenId, marketId, policyId, fillCount: legs.length,
4420
+ soldAmount: sold.toString(),
4421
+ grossSompi: legs.reduce((sum, leg) => sum + leg.gross, 0n).toString(),
4422
+ protocolFeeSompi: protocolTotal.toString(),
4423
+ creatorFeeSompi: creatorTotal.toString(), receiptOutputIndex,
4424
+ ticketInputIndex, ticketRefundIndex,
4425
+ sellerChangeIndex: changeIndex, sellerRefundIndex: refundIndex,
4426
+ signedMass: { compute: signedMass.computeMass.toString(),
4427
+ transient: signedMass.transientMass.toString(), storage: storageMass.toString() } },
4428
+ };
4424
4429
  };
4430
+ try {
4431
+ return buildWithFunding(fundingEntry);
4432
+ }
4433
+ catch (initialError) {
4434
+ // A large funding input can increase contextual storage mass. Preserve
4435
+ // caller-selected inputs; otherwise try at most seven smaller live
4436
+ // wallet UTXO amounts without changing the trade, fees, or mass limits.
4437
+ const details = initialError?.builderErrorDetails;
4438
+ if (params.fundingOutpoint != null ||
4439
+ initialError?.builderErrorCode !== "TRANSACTION_MASS_EXCEEDED" ||
4440
+ BigInt(details?.computeMass ?? 0) > CONSOLIDATION_TARGET_TRANSACTION_MASS ||
4441
+ BigInt(details?.transientMass ?? 0) > CONSOLIDATION_TARGET_TRANSACTION_MASS * 2n ||
4442
+ BigInt(details?.storageMass ?? 0) <= CONSOLIDATION_TARGET_TRANSACTION_MASS) {
4443
+ throw initialError;
4444
+ }
4445
+ const skipped = [...spentEntries.map(entry => entry.outpoint), fundingEntry.outpoint];
4446
+ const conservativeFundingNeeded = fundingNeeded +
4447
+ (estimatedFundingFeeSompi > priorityFee ? estimatedFundingFeeSompi - priorityFee : 0n);
4448
+ const candidates = walletUtxos.entries.filter(entry => (0, sale_common_js_1.utxoAmountSompi)(entry) >= conservativeFundingNeeded &&
4449
+ (0, sale_common_js_1.utxoAmountSompi)(entry) < (0, sale_common_js_1.utxoAmountSompi)(fundingEntry))
4450
+ .sort((a, b) => (0, sale_common_js_1.utxoAmountSompi)(a) < (0, sale_common_js_1.utxoAmountSompi)(b) ? -1 :
4451
+ (0, sale_common_js_1.utxoAmountSompi)(a) > (0, sale_common_js_1.utxoAmountSompi)(b) ? 1 : 0);
4452
+ let attempted = 0;
4453
+ const attemptedAmounts = new Set();
4454
+ for (const candidate of candidates) {
4455
+ if (attempted === 7)
4456
+ break;
4457
+ const amountKey = (0, sale_common_js_1.utxoAmountSompi)(candidate).toString();
4458
+ if (attemptedAmounts.has(amountKey))
4459
+ continue;
4460
+ let selected;
4461
+ try {
4462
+ selected = (0, sale_common_js_1.selectFundingEntry)([candidate], fundingNeeded, skipped, params.fundingOutpoint);
4463
+ }
4464
+ catch {
4465
+ continue;
4466
+ }
4467
+ skipped.push(selected.outpoint);
4468
+ attemptedAmounts.add(amountKey);
4469
+ attempted++;
4470
+ try {
4471
+ return buildWithFunding(selected);
4472
+ }
4473
+ catch (error) {
4474
+ if (!["TRANSACTION_MASS_EXCEEDED", "KAS_FUNDING_CHANGE_TOO_SMALL",
4475
+ "KAS_FUNDING_FEE_INSUFFICIENT"].includes(error?.builderErrorCode))
4476
+ throw error;
4477
+ }
4478
+ }
4479
+ throw initialError;
4480
+ }
4425
4481
  }
4426
4482
  finally {
4427
4483
  await rpc.disconnect();