UNPKG

@lucid-evolution/lucid

Version:

Next-generation transaction builder for highly scalable dApps on Cardano

1,374 lines (1,346 loc) 87.1 kB
// src/core.ts import { Effect } from "effect"; import * as CML from "@anastasia-labs/cardano-multiplatform-lib-nodejs"; var makeReturn = (program) => { return { unsafeRun: () => Effect.runPromise(program), safeRun: () => Effect.runPromise(Effect.either(program)), program: () => program }; }; // src/lucid-evolution/utils.ts import { fromUnit, toUnit } from "@lucid-evolution/utils"; import { Data } from "@lucid-evolution/plutus"; var datumOf = async (provider, utxo, type) => { if (!utxo.datum) { if (!utxo.datumHash) { throw new Error("This UTxO does not have a datum hash."); } utxo.datum = await provider.getDatum(utxo.datumHash); } return Data.from(utxo.datum, type); }; var metadataOf = async (provider, unit) => { const { policyId, name, label } = fromUnit(unit); switch (label) { case 222: case 333: case 444: { const utxo = await provider.getUtxoByUnit(toUnit(policyId, name, 100)); const metadata = await datumOf(provider, utxo); return Data.toJson(metadata.fields[0]); } default: throw new Error("No variant matched."); } }; // src/lucid-evolution/LucidEvolution.ts import { createCostModels, unixTimeToSlot as unixTimeToSlot2 } from "@lucid-evolution/utils"; // src/tx-builder/internal/Collect.ts import { Effect as Effect4, pipe as pipe2 } from "effect"; import { Data as Data3 } from "@lucid-evolution/plutus"; import { utxoToCore } from "@lucid-evolution/utils"; // src/Errors.ts import { Data as Data2 } from "effect"; var ERROR_MESSAGE = { MULTIPLE_POLICIES: "MULTIPLE_POLICIES: Only one policy id allowed. You can chain multiple mintAssets functions together if you need to mint assets with different policy ids. ", EMPTY_UTXO: "EMPTY_UTXO: UTxO array is empty. If a Tx has been recently submitted, consider waiting for chain sync", MISSING_WALLET: "MISSING_WALLET: please ensure that your wallet has been properly configured and initialized", MISSING_REDEEMER: "MISSING_REDEEMER: redeemer can not be undefined", DATUM_NOT_SET: "DATUM_NOT_SET: Script inputs becomes unspendable without datum.", EMPTY_ASSETS: "EMPTY_ASSETS: Attempting to pay to an address with an empty assets object", MISSING_REWARD_TYPE: "MISSING_REWARD_TYPE: Address type must be Reward type.", MISSING_STAKE_CREDENTIAL: "MISSING_STAKE_CREDENTIAL: Address does not contain stake credential", MISSING_PAYMENT_CREDENTIAL: "MISSING_PAYMENT_CREDENTIAL: Address does not contain payment credential", INVALID_METADATA: "INVALID_METADATA: metadata is invalid", SCRIPT_CREDENTIAL_NOT_ALLOWED: "SCRIPT_CREDENTIAL_NOT_ALLOWED: Only verification key credential is allowed", INVALID_SCRIPT: "INVALID_SCRIPT: Script is invalid", EXPECTED_KEY_HASH: "EXPECTED_KEY_HASH", INVALID_NETWORK: (address, actualNetworkId, network) => `Invalid address: ${address}, Expected address with network id ${actualNetworkId}, current network ${network}`, MISSING_SCRIPT: (hash) => `MISSING_SCRIPT: Script not found when building transaction, consider using attach modules. script_hash: ${hash}`, MISSING_POLICY: (policyId) => `MISSING_POLICY: No policy found, policy_id: ${policyId}` }; var NullableError = class extends Data2.TaggedError("NullableError") { }; var UnauthorizedNetwork = class extends Data2.TaggedError( "UnauthorizedNetwork" ) { }; var TxBuilderError = class extends Data2.TaggedError("TxBuilderError") { get message() { return `${this.cause}`; } }; var TxSignerError = class extends Data2.TaggedError("TxSignerError") { get message() { return `${this.cause}`; } }; var TxSubmitError = class extends Data2.TaggedError("TxSubmitError") { get message() { return `${this.cause}`; } }; var RunTimeError = class extends Data2.TaggedError("RunTimeError") { get message() { return `${this.cause}`; } }; // src/tx-builder/internal/Collect.ts import * as CML3 from "@anastasia-labs/cardano-multiplatform-lib-nodejs"; // src/tx-builder/internal/TxUtils.ts import * as CML2 from "@anastasia-labs/cardano-multiplatform-lib-nodejs"; import { Effect as Effect2, pipe } from "effect"; import { networkToId, getAddressDetails } from "@lucid-evolution/utils"; var txBuilderError = (cause) => new TxBuilderError({ cause: `{ TxBuilderError : ${cause} }` }); var toCMLAddress = (address, lucidConfig) => Effect2.gen(function* ($) { const { type } = yield* validateAddressDetails(address, lucidConfig); return type === "Byron" ? CML2.ByronAddress.from_base58(address).to_address() : CML2.Address.from_bech32(address); }); var toV1 = (script) => CML2.PlutusScript.from_v1(CML2.PlutusV1Script.from_cbor_hex(script)); var toV2 = (script) => CML2.PlutusScript.from_v2(CML2.PlutusV2Script.from_cbor_hex(script)); var toV3 = (script) => CML2.PlutusScript.from_v3(CML2.PlutusV3Script.from_cbor_hex(script)); var toPartial = (script, redeemer) => CML2.PartialPlutusWitness.new( CML2.PlutusScriptWitness.new_script(script), CML2.PlutusData.from_cbor_hex(redeemer) ); var handleRedeemerBuilder = (config, partialProgram, redeemer) => { if (typeof redeemer === "object") { config.partialPrograms.set(redeemer, partialProgram); } else { const program = partialProgram(redeemer); config.programs.push(program); } }; var validateAddressDetails = (address, lucidConfig) => Effect2.gen(function* ($) { const addressDetails = yield* $( Effect2.try({ try: () => getAddressDetails(address), catch: (cause) => new TxBuilderError({ cause }) }) ); const actualNetworkId = networkToId(lucidConfig.network); if (addressDetails.networkId !== actualNetworkId) yield* new TxBuilderError({ cause: ERROR_MESSAGE.INVALID_NETWORK( address, actualNetworkId, lucidConfig.network ) }); return addressDetails; }); var processCertificate = (stakeCredential, config, buildCert, redeemer) => Effect2.gen(function* () { switch (stakeCredential.type) { case "Key": { const credential = CML2.Credential.new_pub_key( CML2.Ed25519KeyHash.from_hex(stakeCredential.hash) ); const certBuilder = buildCert(credential); config.txBuilder.add_cert(certBuilder.payment_key()); break; } case "Script": { const credential = CML2.Credential.new_script( CML2.ScriptHash.from_hex(stakeCredential.hash) ); const certBuilder = buildCert(credential); const script = yield* pipe( Effect2.fromNullable(config.scripts.get(stakeCredential.hash)), Effect2.orElseFail( () => txBuilderError(ERROR_MESSAGE.MISSING_SCRIPT(stakeCredential.hash)) ) ); const addPlutusCertificate = (scriptVersion) => { return Effect2.gen(function* () { const red = yield* pipe( Effect2.fromNullable(redeemer), Effect2.orElseFail( () => txBuilderError(ERROR_MESSAGE.MISSING_REDEEMER) ) ); config.txBuilder.add_cert( certBuilder.plutus_script( toPartial(scriptVersion, red), CML2.Ed25519KeyHashList.new() ) ); }); }; switch (script.type) { case "PlutusV1": yield* addPlutusCertificate(toV1(script.script)); break; case "PlutusV2": yield* addPlutusCertificate(toV2(script.script)); break; case "PlutusV3": yield* addPlutusCertificate(toV3(script.script)); break; case "Native": config.txBuilder.add_cert( certBuilder.native_script( CML2.NativeScript.from_cbor_hex(script.script), CML2.NativeScriptWitnessInfo.assume_signature_count() ) ); break; } break; } } }); var validateAndGetStakeCredential = (rewardAddress, config) => Effect2.gen(function* () { const addressDetails = yield* pipe( validateAddressDetails(rewardAddress, config.lucidConfig), Effect2.andThen( (address) => address.type !== "Reward" ? txBuilderError(ERROR_MESSAGE.MISSING_REWARD_TYPE) : Effect2.succeed(address) ) ); const stakeCredential = yield* pipe( Effect2.fromNullable(addressDetails.stakeCredential), Effect2.orElseFail( () => txBuilderError(ERROR_MESSAGE.MISSING_STAKE_CREDENTIAL) ) ); return stakeCredential; }); var resolveDatum = (datumHash, datum, provider) => Effect2.gen(function* () { if (!datumHash || datum) return datum; return yield* Effect2.tryPromise({ try: () => provider.getDatum(datumHash), catch: txBuilderError }); }); // src/tx-builder/internal/Collect.ts import { paymentCredentialOf } from "@lucid-evolution/utils"; // src/tx-builder/internal/Service.ts import { Context } from "effect"; var TxConfig = class extends Context.Tag("TxConfig")() { }; // src/tx-builder/internal/Collect.ts var collectError = (cause) => new TxBuilderError({ cause: `{ Collect: ${cause} }` }); var collectFromUTxO = (utxos, collectInputs = true) => (redeemer) => Effect4.gen(function* () { const { config } = yield* TxConfig; if (utxos.length === 0) yield* collectError(ERROR_MESSAGE.EMPTY_UTXO); for (const utxo of utxos) { const resolvedDatum = yield* resolveDatum( utxo.datumHash, utxo.datum, config.lucidConfig.provider ); utxo.datum = resolvedDatum; if (collectInputs) config.collectedInputs.push(utxo); const input = CML3.SingleInputBuilder.from_transaction_unspent_output( utxoToCore({ ...utxo, datum: resolvedDatum }) ); const credential = paymentCredentialOf(utxo.address); if (credential.type == "Script") { const script = yield* pipe2( Effect4.fromNullable(config.scripts.get(credential.hash)), Effect4.orElseFail( () => collectError( collectError(ERROR_MESSAGE.MISSING_SCRIPT(credential.hash)) ) ) ); switch (script.type) { case "Native": config.txBuilder.add_input( input.native_script( CML3.NativeScript.from_cbor_hex(script.script), CML3.NativeScriptWitnessInfo.assume_signature_count() ) ); break; case "PlutusV1": { const red = yield* pipe2( Effect4.fromNullable(redeemer), Effect4.orElseFail( () => collectError(ERROR_MESSAGE.MISSING_REDEEMER) ) ); config.txBuilder.add_input( input.plutus_script( toPartial(toV1(script.script), red), CML3.Ed25519KeyHashList.new(), CML3.PlutusData.from_cbor_hex(utxo.datum) ) ); break; } case "PlutusV2": { const v2 = toV2(script.script); const red = yield* pipe2( Effect4.fromNullable(redeemer), Effect4.orElseFail( () => collectError(ERROR_MESSAGE.MISSING_REDEEMER) ) ); const partial = toPartial(v2, red); config.txBuilder.add_input( utxo.datum && utxo.datumHash ? input.plutus_script( partial, CML3.Ed25519KeyHashList.new(), CML3.PlutusData.from_cbor_hex(utxo.datum) ) : input.plutus_script_inline_datum( partial, CML3.Ed25519KeyHashList.new() ) ); break; } case "PlutusV3": { const v3 = toV3(script.script); const red = yield* pipe2( Effect4.fromNullable(redeemer), Effect4.orElseFail( () => collectError(ERROR_MESSAGE.MISSING_REDEEMER) ) ); const partial = toPartial(v3, red); config.txBuilder.add_input( utxo.datum && utxo.datumHash ? input.plutus_script( partial, CML3.Ed25519KeyHashList.new(), CML3.PlutusData.from_cbor_hex(utxo.datum) ) : input.plutus_script_inline_datum( partial, CML3.Ed25519KeyHashList.new() ) ); break; } } } else { config.txBuilder.add_input(input.payment_key()); } } }); var collectFromUTxOPartial = (utxos, redeemerBuilder) => Effect4.gen(function* () { const { config } = yield* TxConfig; if (utxos.length === 0) yield* collectError(ERROR_MESSAGE.EMPTY_UTXO); if (redeemerBuilder.kind === "self") redeemerBuilder.inputs = utxos; for (const utxo of utxos) { if (utxo.datumHash && !utxo.datum) { const data = yield* Effect4.tryPromise({ try: () => datumOf(config.lucidConfig.provider, utxo), catch: (cause) => collectError({ cause }) }); utxo.datum = Data3.to(data); } config.collectedInputs.push(utxo); } const partialProgram = collectFromUTxO(utxos, false); config.partialPrograms.set(redeemerBuilder, partialProgram); }); // src/tx-builder/internal/Read.ts import { Effect as Effect5 } from "effect"; import { utxoToCore as utxoToCore2 } from "@lucid-evolution/utils"; var readError = (cause) => new TxBuilderError({ cause: `{ Read : ${cause} }` }); var readFrom = (utxos) => Effect5.gen(function* () { const { config } = yield* TxConfig; if (utxos.length === 0) yield* readError(ERROR_MESSAGE.EMPTY_UTXO); for (const utxo of utxos) { const resolvedDatum = yield* resolveDatum( utxo.datumHash, utxo.datum, config.lucidConfig.provider ); const coreUtxo = utxoToCore2({ ...utxo, datum: resolvedDatum }); const exists = config.readInputs.some( (input) => input.txHash === utxo.txHash && input.outputIndex === utxo.outputIndex ); if (!exists) { config.txBuilder.add_reference_input(coreUtxo); config.readInputs.push(utxo); } } }); // src/tx-builder/internal/Attach.ts import { applyDoubleCborEncoding } from "@lucid-evolution/utils"; var attachScript = ({ type, script }) => { switch (type) { case "Native": return { key: CML.NativeScript.from_cbor_hex(script).hash().to_hex(), value: { type, script } }; case "PlutusV1": return { key: CML.PlutusV1Script.from_cbor_hex(applyDoubleCborEncoding(script)).hash().to_hex(), value: { type, script: applyDoubleCborEncoding(script) } }; case "PlutusV2": return { key: CML.PlutusV2Script.from_cbor_hex(applyDoubleCborEncoding(script)).hash().to_hex(), value: { type, script: applyDoubleCborEncoding(script) } }; case "PlutusV3": return { key: CML.PlutusV3Script.from_cbor_hex(applyDoubleCborEncoding(script)).hash().to_hex(), value: { type, script: applyDoubleCborEncoding(script) } }; default: throw new Error(`Exhaustive check failed: Unhandled case ${type}`); } }; var attachSpendingValidator = (spendingValidator) => attachScript(spendingValidator); var attachMintingPolicy = (mintingPolicy) => attachScript(mintingPolicy); var attachCertificateValidator = (certValidator) => attachScript(certValidator); var attachWithdrawalValidator = (withdrawalValidator) => attachScript(withdrawalValidator); var attachVoteValidator = (voteValidator) => attachScript(voteValidator); var attachProposeValidator = (proposeValidator) => attachScript(proposeValidator); // src/tx-builder/internal/Pay.ts import { Effect as Effect6 } from "effect"; import { addAssets, assetsToValue, coreToTxOutput, toScriptRef, valueToAssets } from "@lucid-evolution/utils"; var payError = (cause) => new TxBuilderError({ cause: `{ Pay: ${cause} }` }); var payToAddress = (address, assets) => Effect6.gen(function* () { const { config } = yield* TxConfig; const outputBuilder = CML.TransactionOutputBuilder.new().with_address(yield* toCMLAddress(address, config.lucidConfig)).next(); if (Object.keys(assets).length == 0) yield* payError(ERROR_MESSAGE.EMPTY_ASSETS); const value = assetsToValue(assets); let outputResult = outputBuilder.with_asset_and_min_required_coin( value.multi_asset(), config.lucidConfig.protocolParameters.coinsPerUtxoByte ).build(); const setLovelaces = assets["lovelace"]; if (setLovelaces) { const minLovelace = outputResult.output().amount().coin(); if (setLovelaces > minLovelace) { outputResult = outputBuilder.with_value(value).build(); } } config.totalOutputAssets = addAssets( config.totalOutputAssets, valueToAssets(outputResult.output().amount()) ); config.payToOutputs = [ ...config.payToOutputs, coreToTxOutput(outputResult.output()) ]; config.txBuilder.add_output(outputResult); }); var ToAddressWithData = (address, outputDatum, assets, scriptRef) => Effect6.gen(function* () { const { config } = yield* TxConfig; const outputBuilder = buildBaseOutput(address, outputDatum, scriptRef); assets ??= {}; const value = assetsToValue(assets); let outputResult = outputBuilder.with_asset_and_min_required_coin( value.multi_asset(), config.lucidConfig.protocolParameters.coinsPerUtxoByte ).build(); const setLovelaces = assets["lovelace"]; if (setLovelaces) { const minLovelace = outputResult.output().amount().coin(); if (setLovelaces > minLovelace) { outputResult = outputBuilder.with_value(value).build(); } } config.totalOutputAssets = addAssets( config.totalOutputAssets, valueToAssets(outputResult.output().amount()) ); config.payToOutputs = [ ...config.payToOutputs, coreToTxOutput(outputResult.output()) ]; config.txBuilder.add_output(outputResult); }); var ToContract = (address, outputDatum, assets, scriptRef) => ToAddressWithData(address, outputDatum, assets, scriptRef); var buildBaseOutput = (address, outputDatum, scriptRef) => { let baseBuilder; const addressBuilder = CML.TransactionOutputBuilder.new().with_address( CML.Address.from_bech32(address) ); if (outputDatum) { if (outputDatum.value.trim() === "") { throw new Error( "datum value is missing. Please provide a non-empty cbor hex data." ); } switch (outputDatum.kind) { case "hash": { const datumOption = CML.DatumOption.new_hash( CML.DatumHash.from_hex(outputDatum.value) ); baseBuilder = addressBuilder.with_data(datumOption); break; } case "asHash": { const plutusData = CML.PlutusData.from_cbor_hex(outputDatum.value); baseBuilder = addressBuilder.with_communication_data(plutusData); break; } case "inline": { const plutusData = CML.PlutusData.from_cbor_hex(outputDatum.value); const datumOption = CML.DatumOption.new_datum(plutusData); baseBuilder = addressBuilder.with_data(datumOption); break; } default: throw new Error(`Unknown outputDatum: ${outputDatum}`); } } else { baseBuilder = addressBuilder; } return scriptRef ? baseBuilder.with_reference_script(toScriptRef(scriptRef)).next() : baseBuilder.next(); }; // src/tx-builder/internal/Mint.ts import { Effect as Effect7, pipe as pipe3 } from "effect"; import * as CML4 from "@anastasia-labs/cardano-multiplatform-lib-nodejs"; var mintError = (cause) => new TxBuilderError({ cause: `{ Mint: ${cause} }` }); var mintAssets = (assets) => (redeemer) => Effect7.gen(function* () { const { config } = yield* TxConfig; const units = Object.keys(assets); const policyId = units[0].slice(0, 56); const mintAssets2 = CML4.MapAssetNameToNonZeroInt64.new(); for (const unit of units) { if (unit.slice(0, 56) !== policyId) { yield* mintError(ERROR_MESSAGE.MULTIPLE_POLICIES); } mintAssets2.insert(CML4.AssetName.from_hex(unit.slice(56)), assets[unit]); } const mintBuilder = CML4.SingleMintBuilder.new(mintAssets2); const policy = yield* pipe3( Effect7.fromNullable(config.scripts.get(policyId)), Effect7.orElseFail( () => mintError(ERROR_MESSAGE.MISSING_POLICY(policyId)) ) ); switch (policy.type) { case "Native": config.txBuilder.add_mint( mintBuilder.native_script( CML4.NativeScript.from_cbor_hex(policy.script), CML4.NativeScriptWitnessInfo.assume_signature_count() ) ); break; case "PlutusV1": { const red = yield* pipe3( Effect7.fromNullable(redeemer), Effect7.orElseFail(() => mintError(ERROR_MESSAGE.MISSING_REDEEMER)) ); config.txBuilder.add_mint( mintBuilder.plutus_script( toPartial(toV1(policy.script), red), CML4.Ed25519KeyHashList.new() ) ); break; } case "PlutusV2": { const red = yield* pipe3( Effect7.fromNullable(redeemer), Effect7.orElseFail(() => mintError(ERROR_MESSAGE.MISSING_REDEEMER)) ); config.txBuilder.add_mint( mintBuilder.plutus_script( toPartial(toV2(policy.script), red), CML4.Ed25519KeyHashList.new() ) ); break; } case "PlutusV3": { const red = yield* pipe3( Effect7.fromNullable(redeemer), Effect7.orElseFail(() => mintError(ERROR_MESSAGE.MISSING_REDEEMER)) ); config.txBuilder.add_mint( mintBuilder.plutus_script( toPartial(toV3(policy.script), red), CML4.Ed25519KeyHashList.new() ) ); break; } } }); // src/tx-builder/internal/Interval.ts import { Effect as Effect8 } from "effect"; import { unixTimeToSlot } from "@lucid-evolution/utils"; var validFrom = (unixTime) => Effect8.gen(function* () { const { config } = yield* TxConfig; const slot = unixTimeToSlot(config.lucidConfig.network, unixTime); config.txBuilder.set_validity_start_interval(BigInt(slot)); }); var validTo = (unixTime) => Effect8.gen(function* () { const { config } = yield* TxConfig; const slot = unixTimeToSlot(config.lucidConfig.network, unixTime); config.txBuilder.set_ttl(BigInt(slot)); }); // src/tx-builder/internal/Signer.ts import { Effect as Effect9, pipe as pipe4 } from "effect"; import * as CML5 from "@anastasia-labs/cardano-multiplatform-lib-nodejs"; var addSignerError = (cause) => new TxBuilderError({ cause: `{ Signer: ${cause} }` }); var addSigner = (address) => Effect9.gen(function* () { const { config } = yield* TxConfig; const addressDetails = yield* validateAddressDetails( address, config.lucidConfig ); const credential = addressDetails.type === "Reward" ? yield* pipe4( Effect9.fromNullable(addressDetails.stakeCredential), Effect9.orElseFail( () => addSignerError(ERROR_MESSAGE.MISSING_STAKE_CREDENTIAL) ) ) : yield* pipe4( Effect9.fromNullable(addressDetails.paymentCredential), Effect9.orElseFail( () => addSignerError(ERROR_MESSAGE.MISSING_PAYMENT_CREDENTIAL) ) ); if (credential.type === "Script") yield* addSignerError(ERROR_MESSAGE.SCRIPT_CREDENTIAL_NOT_ALLOWED); return credential.hash; }).pipe(Effect9.flatMap((keyHash) => addSignerKey(keyHash))); var addSignerKey = (keyHash) => Effect9.gen(function* () { const { config } = yield* TxConfig; config.txBuilder.add_required_signer(CML5.Ed25519KeyHash.from_hex(keyHash)); }); // src/tx-builder/internal/Stake.ts import { Effect as Effect10, pipe as pipe5 } from "effect"; import * as CML6 from "@anastasia-labs/cardano-multiplatform-lib-nodejs"; var stakeError = (cause) => new TxBuilderError({ cause: `{ Stake: ${cause} }` }); var registerStake = (rewardAddress) => Effect10.gen(function* () { const { config } = yield* TxConfig; const addressDetails = yield* pipe5( validateAddressDetails(rewardAddress, config.lucidConfig), Effect10.andThen( (address) => address.type !== "Reward" ? stakeError(ERROR_MESSAGE.MISSING_REWARD_TYPE) : Effect10.succeed(address) ) ); const stakeCredential = yield* pipe5( Effect10.fromNullable(addressDetails.stakeCredential), Effect10.orElseFail( () => stakeError(ERROR_MESSAGE.MISSING_STAKE_CREDENTIAL) ) ); const credential = stakeCredential.type === "Key" ? CML6.Credential.new_pub_key( CML6.Ed25519KeyHash.from_hex(stakeCredential.hash) ) : CML6.Credential.new_script( CML6.ScriptHash.from_hex(stakeCredential.hash) ); const certBuilder = CML6.SingleCertificateBuilder.new( CML6.Certificate.new_stake_registration(credential) ); config.txBuilder.add_cert(certBuilder.skip_witness()); }); var deRegisterStake = (rewardAddress, redeemer) => Effect10.gen(function* () { const { config } = yield* TxConfig; const addressDetails = yield* pipe5( validateAddressDetails(rewardAddress, config.lucidConfig), Effect10.andThen( (address) => address.type !== "Reward" ? stakeError(ERROR_MESSAGE.MISSING_REWARD_TYPE) : Effect10.succeed(address) ) ); const stakeCredential = yield* pipe5( Effect10.fromNullable(addressDetails.stakeCredential), Effect10.orElseFail( () => stakeError(ERROR_MESSAGE.MISSING_STAKE_CREDENTIAL) ) ); const createCertBuilder = (credential, config2) => { return CML6.SingleCertificateBuilder.new( CML6.Certificate.new_unreg_cert( credential, config2.lucidConfig.protocolParameters.keyDeposit ) ); }; switch (stakeCredential.type) { case "Key": { const credential = CML6.Credential.new_pub_key( CML6.Ed25519KeyHash.from_hex(stakeCredential.hash) ); const certBuilder = createCertBuilder(credential, config); config.txBuilder.add_cert(certBuilder.payment_key()); break; } case "Script": { const credential = CML6.Credential.new_script( CML6.ScriptHash.from_hex(stakeCredential.hash) ); const certBuilder = createCertBuilder(credential, config); const script = yield* pipe5( Effect10.fromNullable(config.scripts.get(stakeCredential.hash)), Effect10.orElseFail( () => stakeError(ERROR_MESSAGE.MISSING_SCRIPT(stakeCredential.hash)) ) ); const handleRedeemer = () => pipe5( Effect10.fromNullable(redeemer), Effect10.orElseFail(() => stakeError(ERROR_MESSAGE.MISSING_REDEEMER)) ); switch (script.type) { case "PlutusV1": { const red = yield* handleRedeemer(); config.txBuilder.add_cert( certBuilder.plutus_script( toPartial(toV1(script.script), red), CML6.Ed25519KeyHashList.new() ) ); break; } case "PlutusV2": { const red = yield* handleRedeemer(); config.txBuilder.add_cert( certBuilder.plutus_script( toPartial(toV2(script.script), red), CML6.Ed25519KeyHashList.new() ) ); break; } case "PlutusV3": { const red = yield* handleRedeemer(); config.txBuilder.add_cert( certBuilder.plutus_script( toPartial(toV3(script.script), red), CML6.Ed25519KeyHashList.new() ) ); break; } case "Native": { config.txBuilder.add_cert( certBuilder.native_script( CML6.NativeScript.from_cbor_hex(script.script), CML6.NativeScriptWitnessInfo.assume_signature_count() ) ); break; } } } } }); var withdraw = (rewardAddress, amount) => (redeemer) => Effect10.gen(function* () { const { config } = yield* TxConfig; const addressDetails = yield* pipe5( validateAddressDetails(rewardAddress, config.lucidConfig), Effect10.andThen( (address) => address.type !== "Reward" ? stakeError(ERROR_MESSAGE.MISSING_REWARD_TYPE) : Effect10.succeed(address) ) ); const withdrawBuilder = yield* pipe5( Effect10.fromNullable( CML6.RewardAddress.from_address( CML6.Address.from_bech32(rewardAddress) ) ), Effect10.orElseFail( () => stakeError(ERROR_MESSAGE.MISSING_STAKE_CREDENTIAL) ), Effect10.andThen( (address) => CML6.SingleWithdrawalBuilder.new(address, amount) ) ); const stakeCredential = yield* pipe5( Effect10.fromNullable(addressDetails.stakeCredential), Effect10.orElseFail( () => stakeError(ERROR_MESSAGE.MISSING_STAKE_CREDENTIAL) ) ); const handleRedeemer = () => pipe5( Effect10.fromNullable(redeemer), Effect10.orElseFail(() => stakeError(ERROR_MESSAGE.MISSING_REDEEMER)) ); switch (stakeCredential.type) { case "Key": { config.txBuilder.add_withdrawal(withdrawBuilder.payment_key()); break; } case "Script": { const script = yield* pipe5( Effect10.fromNullable(config.scripts.get(stakeCredential.hash)), Effect10.orElseFail( () => stakeError(ERROR_MESSAGE.MISSING_SCRIPT(stakeCredential.hash)) ) ); switch (script.type) { case "PlutusV1": { const red = yield* handleRedeemer(); config.txBuilder.add_withdrawal( withdrawBuilder.plutus_script( toPartial(toV1(script.script), red), CML6.Ed25519KeyHashList.new() ) ); break; } case "PlutusV2": { const red = yield* handleRedeemer(); config.txBuilder.add_withdrawal( withdrawBuilder.plutus_script( toPartial(toV2(script.script), red), CML6.Ed25519KeyHashList.new() ) ); break; } case "PlutusV3": { const red = yield* handleRedeemer(); config.txBuilder.add_withdrawal( withdrawBuilder.plutus_script( toPartial(toV3(script.script), red), CML6.Ed25519KeyHashList.new() ) ); break; } case "Native": { config.txBuilder.add_withdrawal( withdrawBuilder.native_script( CML6.NativeScript.from_cbor_hex(script.script), CML6.NativeScriptWitnessInfo.assume_signature_count() ) ); break; } } } } }); // src/tx-builder/internal/Pool.ts import { Effect as Effect11, pipe as pipe6 } from "effect"; import * as CML7 from "@anastasia-labs/cardano-multiplatform-lib-nodejs"; import { fromText } from "@lucid-evolution/core-utils"; var poolError = (cause) => new TxBuilderError({ cause: `{ Pool : ${cause} }` }); var delegateTo = (rewardAddress, poolId, redeemer) => Effect11.gen(function* () { const { config } = yield* TxConfig; const addressDetails = yield* pipe6( validateAddressDetails(rewardAddress, config.lucidConfig), Effect11.andThen( (address) => address.type !== "Reward" ? poolError(ERROR_MESSAGE.MISSING_REWARD_TYPE) : Effect11.succeed(address) ) ); const stakeCredential = yield* pipe6( Effect11.fromNullable(addressDetails.stakeCredential), Effect11.orElseFail( () => poolError(ERROR_MESSAGE.MISSING_STAKE_CREDENTIAL) ) ); switch (stakeCredential.type) { case "Key": { const credential = CML7.Credential.new_pub_key( CML7.Ed25519KeyHash.from_hex(stakeCredential.hash) ); const certBuilder = CML7.SingleCertificateBuilder.new( CML7.Certificate.new_stake_delegation( credential, CML7.Ed25519KeyHash.from_bech32(poolId) ) ); config.txBuilder.add_cert(certBuilder.payment_key()); break; } case "Script": { const credential = CML7.Credential.new_script( CML7.ScriptHash.from_hex(stakeCredential.hash) ); const certBuilder = CML7.SingleCertificateBuilder.new( CML7.Certificate.new_stake_delegation( credential, CML7.Ed25519KeyHash.from_bech32(poolId) ) ); const script = yield* pipe6( Effect11.fromNullable(config.scripts.get(stakeCredential.hash)), Effect11.orElseFail( () => poolError(ERROR_MESSAGE.MISSING_SCRIPT(stakeCredential.hash)) ) ); const handleRedeemer = () => pipe6( Effect11.fromNullable(redeemer), Effect11.orElseFail(() => poolError(ERROR_MESSAGE.MISSING_REDEEMER)) ); switch (script.type) { case "PlutusV1": { const red = yield* handleRedeemer(); config.txBuilder.add_cert( certBuilder.plutus_script( toPartial(toV1(script.script), red), CML7.Ed25519KeyHashList.new() ) ); break; } case "PlutusV2": { const red = yield* handleRedeemer(); config.txBuilder.add_cert( certBuilder.plutus_script( toPartial(toV2(script.script), red), CML7.Ed25519KeyHashList.new() ) ); break; } case "PlutusV3": { const red = yield* handleRedeemer(); config.txBuilder.add_cert( certBuilder.plutus_script( toPartial(toV3(script.script), red), CML7.Ed25519KeyHashList.new() ) ); break; } case "Native": { config.txBuilder.add_cert( certBuilder.native_script( CML7.NativeScript.from_cbor_hex(script.script), CML7.NativeScriptWitnessInfo.assume_signature_count() ) ); break; } } } } }); // src/tx-builder/internal/Governance.ts import * as CML8 from "@anastasia-labs/cardano-multiplatform-lib-nodejs"; import { Effect as Effect12 } from "effect"; var isDRepCredential = (deleg) => !("__typename" in deleg); var isDRepAlwaysAbstain = (deleg) => !isDRepCredential(deleg) && deleg.__typename === "AlwaysAbstain"; var isDRepAlwaysNoConfidence = (deleg) => !isDRepCredential(deleg) && deleg.__typename === "AlwaysNoConfidence"; var toCMLDRep = (drep) => { if (isDRepAlwaysAbstain(drep)) { return CML8.DRep.new_always_abstain(); } else if (isDRepAlwaysNoConfidence(drep)) { return CML8.DRep.new_always_no_confidence(); } else if (isDRepCredential(drep)) { switch (drep.type) { case "Key": return CML8.DRep.new_key(CML8.Ed25519KeyHash.from_hex(drep.hash)); case "Script": return CML8.DRep.new_script(CML8.ScriptHash.from_hex(drep.hash)); default: throw new Error(`Unsupported DRep type: ${drep.type}`); } } throw new Error(`Unexpected DRep type: ${drep}`); }; var delegateVoteToDRep = (rewardAddress, drep, redeemer) => Effect12.gen(function* () { const { config } = yield* TxConfig; const stakeCredential = yield* validateAndGetStakeCredential( rewardAddress, config ); const cmlDRep = toCMLDRep(drep); const buildCert = (credential) => CML8.SingleCertificateBuilder.new( CML8.Certificate.new_vote_deleg_cert(credential, cmlDRep) ); yield* processCertificate(stakeCredential, config, buildCert, redeemer); }); var delegateVoteToPoolAndDRep = (rewardAddress, poolId, drep, redeemer) => Effect12.gen(function* () { const { config } = yield* TxConfig; const stakeCredential = yield* validateAndGetStakeCredential( rewardAddress, config ); const cmlDRep = toCMLDRep(drep); const buildCert = (credential) => CML8.SingleCertificateBuilder.new( CML8.Certificate.new_stake_vote_deleg_cert( credential, CML8.Ed25519KeyHash.from_bech32(poolId), cmlDRep ) ); yield* processCertificate(stakeCredential, config, buildCert, redeemer); }); var registerAndDelegateToPool = (rewardAddress, poolId, redeemer) => Effect12.gen(function* () { const { config } = yield* TxConfig; const stakeCredential = yield* validateAndGetStakeCredential( rewardAddress, config ); const buildCert = (credential) => CML8.SingleCertificateBuilder.new( CML8.Certificate.new_stake_reg_deleg_cert( credential, CML8.Ed25519KeyHash.from_bech32(poolId), config.lucidConfig.protocolParameters.keyDeposit ) ); yield* processCertificate(stakeCredential, config, buildCert, redeemer); }); var registerAndDelegateToDRep = (rewardAddress, drep, redeemer) => Effect12.gen(function* () { const { config } = yield* TxConfig; const stakeCredential = yield* validateAndGetStakeCredential( rewardAddress, config ); const cmlDRep = toCMLDRep(drep); const buildCert = (credential) => CML8.SingleCertificateBuilder.new( CML8.Certificate.new_vote_reg_deleg_cert( credential, cmlDRep, config.lucidConfig.protocolParameters.keyDeposit ) ); yield* processCertificate(stakeCredential, config, buildCert, redeemer); }); var registerAndDelegateToPoolAndDRep = (rewardAddress, poolId, drep, redeemer) => Effect12.gen(function* () { const { config } = yield* TxConfig; const stakeCredential = yield* validateAndGetStakeCredential( rewardAddress, config ); const cmlDRep = toCMLDRep(drep); const buildCert = (credential) => CML8.SingleCertificateBuilder.new( CML8.Certificate.new_stake_vote_reg_deleg_cert( credential, CML8.Ed25519KeyHash.from_bech32(poolId), cmlDRep, config.lucidConfig.protocolParameters.keyDeposit ) ); yield* processCertificate(stakeCredential, config, buildCert, redeemer); }); var registerDRep = (rewardAddress, anchor, redeemer) => Effect12.gen(function* () { const { config } = yield* TxConfig; const stakeCredential = yield* validateAndGetStakeCredential( rewardAddress, config ); const cmlAnchor = anchor ? CML8.Anchor.new( CML8.Url.from_json(anchor.url), CML8.AnchorDocHash.from_hex(anchor.dataHash) ) : void 0; const buildCert = (credential) => CML8.SingleCertificateBuilder.new( CML8.Certificate.new_reg_drep_cert( credential, config.lucidConfig.protocolParameters.drepDeposit, cmlAnchor ) ); yield* processCertificate(stakeCredential, config, buildCert, redeemer); }); var deregisterDRep = (rewardAddress, redeemer) => Effect12.gen(function* () { const { config } = yield* TxConfig; const stakeCredential = yield* validateAndGetStakeCredential( rewardAddress, config ); const buildCert = (credential) => CML8.SingleCertificateBuilder.new( CML8.Certificate.new_unreg_drep_cert( credential, config.lucidConfig.protocolParameters.drepDeposit ) ); yield* processCertificate(stakeCredential, config, buildCert, redeemer); }); var updateDRep = (rewardAddress, anchor, redeemer) => Effect12.gen(function* () { const { config } = yield* TxConfig; const stakeCredential = yield* validateAndGetStakeCredential( rewardAddress, config ); const cmlAnchor = anchor ? CML8.Anchor.new( CML8.Url.from_json(anchor.url), CML8.AnchorDocHash.from_hex(anchor.dataHash) ) : void 0; const buildCert = (credential) => CML8.SingleCertificateBuilder.new( CML8.Certificate.new_update_drep_cert(credential, cmlAnchor) ); yield* processCertificate(stakeCredential, config, buildCert, redeemer); }); var authCommitteeHot = (coldAddress, hotAddress, redeemer) => Effect12.gen(function* () { const { config } = yield* TxConfig; const coldCred = yield* validateAndGetStakeCredential(coldAddress, config); const hotCred = yield* validateAndGetStakeCredential(hotAddress, config); const hotCredential = hotCred.type === "Key" ? CML8.Credential.new_pub_key(CML8.Ed25519KeyHash.from_hex(hotCred.hash)) : CML8.Credential.new_script(CML8.ScriptHash.from_hex(hotCred.hash)); const buildCert = (credential) => CML8.SingleCertificateBuilder.new( CML8.Certificate.new_auth_committee_hot_cert(credential, hotCredential) ); yield* processCertificate(coldCred, config, buildCert, redeemer); }); var resignCommitteeHot = (coldAddress, anchor, redeemer) => Effect12.gen(function* () { const { config } = yield* TxConfig; const coldCred = yield* validateAndGetStakeCredential(coldAddress, config); const cmlAnchor = anchor ? CML8.Anchor.new( CML8.Url.from_json(anchor.url), CML8.AnchorDocHash.from_hex(anchor.dataHash) ) : void 0; const buildCert = (credential) => CML8.SingleCertificateBuilder.new( CML8.Certificate.new_resign_committee_cold_cert(credential, cmlAnchor) ); yield* processCertificate(coldCred, config, buildCert, redeemer); }); // src/tx-builder/internal/Metadata.ts import { Effect as Effect13 } from "effect"; import * as S from "@effect/schema/Schema"; import { toHex } from "@lucid-evolution/core-utils"; var attachMetadata = (config, label, metadata) => Effect13.gen(function* () { const auxiliaryData = CML.AuxiliaryData.new(); const meta = CML.Metadata.new(); meta.set( BigInt(label), CML.TransactionMetadatum.from_json( JSON.stringify(toCardanoMetadata(metadata)) ) ); auxiliaryData.add_metadata(meta); config.txBuilder.add_auxiliary_data(auxiliaryData); auxiliaryData.free(); meta.free(); }); var TextSchema = S.String.pipe(S.maxLength(64)); var TransactionMetadataSchema = S.Union( TextSchema, S.Number, S.Uint8ArrayFromSelf, S.Array(S.suspend(() => TransactionMetadataSchema)), S.Record( S.String, S.suspend(() => TransactionMetadataSchema) ) ); var toCardanoMetadata = (json) => { const d = S.asserts(TransactionMetadataSchema)(json); if (S.is(TextSchema)(json)) { return { string: json }; } if (typeof json === "number") { return { int: json }; } if (json instanceof Uint8Array) { return { bytes: toHex(json) }; } if (Array.isArray(json)) { return { list: json.map((value) => toCardanoMetadata(value)) }; } if (typeof json === "object" && json !== null) { const mapEntries = Object.entries(json).map(([k, v]) => ({ k: toCardanoMetadata(k), v: toCardanoMetadata(v) })); return { map: mapEntries }; } throw new Error("Unsupported type"); }; // src/tx-builder/internal/CompleteTxBuilder.ts import { Effect as Effect17, pipe as pipe9, Record as Record2, Array as _Array, BigInt as _BigInt, Tuple, Option } from "effect"; import * as UPLC from "@lucid-evolution/uplc"; // src/tx-sign-builder/TxSignBuilder.ts import * as S3 from "@effect/schema/Schema"; // src/tx-sign-builder/internal/CompleteTxSigner.ts import { Effect as Effect16, pipe as pipe8 } from "effect"; // src/tx-submit/TxSubmit.ts import { Effect as Effect14 } from "effect"; import * as S2 from "@effect/schema/Schema"; var makeSubmit = (wallet, txSigned) => { const submit = (options) => Effect14.tryPromise({ try: () => wallet.submitTx( options.canonical ? txSigned.to_canonical_cbor_hex() : txSigned.to_cbor_hex() ), catch: (cause) => new TxSubmitError({ cause }) }); return { submit: (options = { canonical: false }) => makeReturn(submit(options)).unsafeRun(), submitProgram: (options = { canonical: false }) => submit(options), submitSafe: (options = { canonical: false }) => makeReturn(submit(options)).safeRun(), toCBOR: (options = { canonical: false }) => options.canonical ? txSigned.to_canonical_cbor_hex() : txSigned.to_cbor_hex(), toTransaction: () => txSigned, toJSON: () => S2.decodeUnknownSync(S2.parseJson(S2.Object))(txSigned.to_json()), toHash: () => CML.hash_transaction(txSigned.body()).to_hex() }; }; // src/tx-sign-builder/internal/Sign.ts import { Effect as Effect15, pipe as pipe7 } from "effect"; var signError = (cause) => new TxSignerError({ cause }); var mkWitnessFromWallet = (wallet, txComplete) => pipe7( Effect15.fromNullable(wallet), Effect15.catchAll(() => signError(ERROR_MESSAGE.MISSING_WALLET)), Effect15.tryMapPromise({ try: (wallet2) => wallet2.signTx(txComplete), catch: (cause) => signError(cause) }) ); var withWallet = (config) => pipe7( mkWitnessFromWallet(config.wallet, config.txComplete), Effect15.map((witness) => config.witnessSetBuilder.add_existing(witness)) ); var partialWithWallet = (config) => pipe7( mkWitnessFromWallet(config.wallet, config.txComplete), Effect15.map((witness) => witness.to_cbor_hex()) ); var mkWitnessFromPrivateKey = (privateKey, txComplete) => pipe7( Effect15.try({ try: () => CML.PrivateKey.from_bech32(privateKey), catch: signError }), Effect15.map( (privateKey2) => CML.make_vkey_witness( CML.hash_transaction(txComplete.body()), privateKey2 ) ) ); var withPrivateKey = (config, privateKey) => pipe7( mkWitnessFromPrivateKey(privateKey, config.txComplete), Effect15.map((witness) => config.witnessSetBuilder.add_vkey(witness)) ); var partialWithPrivateKey = (config, privateKey) => pipe7( mkWitnessFromPrivateKey(privateKey, config.txComplete), Effect15.map((witness) => { const witnessBuilder = CML.TransactionWitnessSetBuilder.new(); witnessBuilder.add_vkey(witness); return witnessBuilder.build().to_cbor_hex(); }) ); var assemble = (config, witnesses) => Effect15.forEach( witnesses, (witness) => pipe7( Effect15.try({ try: () => CML.TransactionWitnessSet.from_cbor_hex(witness), catch: signError }), Effect15.map((witness2) => config.witnessSetBuilder.add_existing(witness2)) ) ); // src/tx-sign-builder/internal/CompleteTxSigner.ts var completeTxSigner = (config) => Effect16.gen(function* () { yield* Effect16.all(config.programs, { concurrency: "unbounded" }); const plutus_datums = config.txComplete.witness_set().plutus_datums(); config.witnessSetBuilder.add_existing(config.txComplete.witness_set()); if (plutus_datums) { for (let i = 0; i < plutus_datums.len(); i++) { config.witnessSetBuilder.add_plutus_datum(plutus_datums.get(i)); } } const txWitnessSet = config.witnessSetBuilder.build(); const signedTx = CML.Transaction.new( config.txComplete.body(), txWitnessSet, true, config.txComplete.auxiliary_data() ); const wallet = yield* pipe8( Effect16.fromNullable(config.wallet), Effect16.orElseFail(() => signError(ERROR_MESSAGE.MISSING_WALLET)) ); return makeSubmit(wallet, signedTx); }).pipe(Effect16.catchAllDefect((cause) => new RunTimeError({ cause }))); // src/tx-sign-builder/TxSignBuilder.ts var makeTxSignBuilder = (wallet, tx) => { const redeemers = tx.witness_set().redeemers(); const exUnits = { cpu: 0, mem: 0 }; if (redeemers) { const arrLegacyRedeemer = redeemers?.as_arr_legacy_redeemer(); if (arrLegacyRedeemer) { for (let i = 0; i < arrLegacyRedeemer.len(); i++) { const redeemer = arrLegacyRedeemer.get(i); exUnits.cpu += parseInt(redeemer.ex_units().steps().toString()); exUnits.mem += parseInt(redeemer.ex_units().mem().toString()); } } const mapRedeemerKeyToRedeemerVal = redeemers?.as_map_redeemer_key_to_redeemer_val(); if (mapRedeemerKeyToRedeemerVal) { const keys = mapRedeemerKeyToRedeemerVal.keys(); for (let i = 0; i < (keys.len() || 0); i++) { const key = keys.get(i); const value = mapRedeemerKeyToRedeemerVal.get(key); exUnits.cpu += parseInt(value.ex_units().steps().toString()); exUnits.mem += parseInt(value.ex_units().mem().toString()); } } } const config = { txComplete: tx, witnessSetBuilder: CML.TransactionWitnessSetBuilder.new(), programs: [], wallet, fee: parseInt(tx.body().fee().toString()), exUnits }; const txSignBuilder = { sign: { withWallet: () => { const program = withWallet(config); config.programs.push(program); return txSignBuilder; }, withPrivateKey: (privateKey) => { const program = withPrivateKey(config, privateKey); config.programs.push(program); return txSignBuilder; } }, partialSign: { withWallet: () => makeReturn(partialWithWallet(config)).unsafeRun(), withWalletEffect: () => partialWithWallet(config), withWalletSafe: () => makeReturn(partialWithWallet(config)).safeRun(), withPrivateKey: (privateKey) => makeReturn(partialWithPrivateKey(config, privateKey)).unsafeRun(), withPrivateKeyEffect: (privateKey) => partialWithPrivateKey(config, privateKey), withPrivateKeySafe: (privateKey) => makeReturn(partialWithPrivateKey(config, privateKey)).safeRun() }, assemble: (witnesses) => { const program = assemble(config, witnesses); config.programs.push(program); return txSignBuilder; }, toCBOR: (options = { canonical: false }) => options.canonical ? config.txComplete.to_canonical_cbor_hex() : config.txComplete.to_cbor_hex(), toTransaction: () => config.txComplete, toJSON: () => S3.decodeUnknownSync(S3.parseJson(S3.Object))(config.txComplete.to_json()), toHash: () => CML.hash_transaction(config.txComplete.body()).to_hex(), complete: () => makeReturn(completeTxSigner(config)).unsafeRun(), completeProgram: () => completeTxSigner(config), completeSafe: () => makeReturn(completeTxSigner(config)).safeRun() }; return txSignBuilder; }; // src/tx-builder/internal/CompleteTxBuilder.ts import { assetsToValue as assetsToValue2, coreToTxOutput as coreToTxOutput2, isEqualUTxO, selectUTxOs, sortUTxOs, stringify, utxoToCore as utxoToCore3, utxoToTransactionInput, utxoToTransactionOutput, toCMLRedeemerTag } from "@lucid-evolution/utils"; import { SLOT_CONFIG_NETWORK } from "@lucid-evolution/plutus"; import { isError } from "effect/Predicate"; var completeTxError = (cause) => new TxBuilderError({ cause: `{ Complete: ${cause} }` }); var complete = (options = {}) => Effect17.gen(function* () { const { config } = yield* TxConfig; const wallet = yield* pipe9( Effect17.fromNullable(config.lucidConfig.wallet), Effect17.orElseFail(() => completeTxError(ERROR_MESSAGE.MISSING_WALLET)) ); const walletAddress = yield* Effect17.promise(() => wallet.address()); const { coinSelection = true, changeAddress = walletAddress, localUPLCEval = true, setCollateral = 5000000n, canonical = false, includeLeftoverLovelaceAsFee = false,