From cfb1ea5d9f28cebe81d06605f762a2f387c92796 Mon Sep 17 00:00:00 2001 From: mgpai <64056966+mgpai22@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:56:30 -0500 Subject: [PATCH] add new utxo fn, add tx wait, save singleton contract in class, allow spend to use saved singleton contract instead of koios --- src/events.ts | 165 ++++++++++++++++++++++---------------------------- 1 file changed, 73 insertions(+), 92 deletions(-) diff --git a/src/events.ts b/src/events.ts index 57fb69f..86c4a78 100644 --- a/src/events.ts +++ b/src/events.ts @@ -5,20 +5,18 @@ import { Data, fromText, Lucid, - Network, OutRef, + MintingPolicy, + Network, + OutRef, PrivateKey, Provider, SpendingValidator, TxComplete, UTxO -} from "lucid-cardano"; -import { - WINTER_FEE, - WINTER_FEE_ADDRESS_MAINNET, - WINTER_FEE_ADDRESS_TESTNET, -} from './fee'; +} from 'lucid-cardano'; +import { WINTER_FEE, WINTER_FEE_ADDRESS_MAINNET, WINTER_FEE_ADDRESS_TESTNET } from './fee'; import { Koios } from './koios/api'; -import { ObjectDatumParameters, Seed, Validators } from "./models/event.types"; +import { ObjectDatumParameters, Seed, Validators } from './models/event.types'; import { PlutusJson } from './models/plutus.types'; import { PLUTUSJSON } from './plutus'; @@ -26,7 +24,7 @@ const ObjectDatum = Data.Object({ protocol_version: Data.Integer(), data_reference: Data.Bytes(), event_creation_info: Data.Bytes(), - signers: Data.Array(Data.Bytes()), + signers: Data.Array(Data.Bytes()) }); type ObjectDatum = Data.Static; @@ -37,6 +35,7 @@ export class EventFactory { public readonly network: Network; public readonly plutusJson: PlutusJson; public readonly validators: Validators; + public singletonContract: MintingPolicy; public objectEventContract: SpendingValidator; public objectEventContractAddress: string; public objectDatum: string; @@ -51,20 +50,18 @@ export class EventFactory { constructor(network: Network) { this.network = network; this.isMainnet = network === 'Mainnet'; - this.feeAddress = this.isMainnet - ? WINTER_FEE_ADDRESS_MAINNET - : WINTER_FEE_ADDRESS_TESTNET; + this.feeAddress = this.isMainnet ? WINTER_FEE_ADDRESS_MAINNET : WINTER_FEE_ADDRESS_TESTNET; this.feeAmount = WINTER_FEE; this.plutusJson = PLUTUSJSON; this.validators = { objectEvent: { type: 'PlutusV2', - script: this.plutusJson.validators[0].compiledCode, + script: this.plutusJson.validators[0].compiledCode }, singleton: { type: 'PlutusV2', - script: this.plutusJson.validators[1].compiledCode, - }, + script: this.plutusJson.validators[1].compiledCode + } }; this.recreateRedeemer = Data.to(new Constr(0, [])); this.mintRedeemer = Data.to(new Constr(0, [])); @@ -77,8 +74,7 @@ export class EventFactory { seed?: Seed, privateKey?: PrivateKey ): Promise { - if (!seed && !privateKey) - throw new Error('either seed or privateKey must be set'); + if (!seed && !privateKey) throw new Error('either seed or privateKey must be set'); this.lucid = await Lucid.new(provider, this.network); if (seed) { this.lucid.selectWalletFromSeed(seed.seed, seed.options); @@ -89,24 +85,20 @@ export class EventFactory { return this; } - public async setObjectContract( - objectDatumParameters: ObjectDatumParameters - ): Promise { - if (!this.providerSetup) - throw new Error('setProvider must be called before setObjectContract'); + public async setObjectContract(objectDatumParameters: ObjectDatumParameters): Promise { + if (!this.providerSetup) throw new Error('setProvider must be called before setObjectContract'); const serializedPaymentCredential = new Constr(0, [ - this.lucid.utils.getAddressDetails(this.feeAddress).paymentCredential! - .hash, + this.lucid.utils.getAddressDetails(this.feeAddress).paymentCredential!.hash + ]); + const objectEventBytes = applyParamsToScript(this.validators.objectEvent.script, [ + serializedPaymentCredential, + this.feeAmount ]); - const objectEventBytes = applyParamsToScript( - this.validators.objectEvent.script, - [serializedPaymentCredential, this.feeAmount] - ); this.objectEventContract = { type: 'PlutusV2', - script: objectEventBytes, - } + script: objectEventBytes + }; this.objectEventContractAddress = this.lucid.utils.validatorToAddress(this.objectEventContract); this.objectDatum = EventFactory.getObjectDatum( objectDatumParameters.protocolVersion, @@ -119,63 +111,62 @@ export class EventFactory { } public async getWalletUtxos(): Promise { - if (!this.providerSetup) - throw new Error('setProvider must be called before getWalletUtxos'); + if (!this.providerSetup) throw new Error('setProvider must be called before getWalletUtxos'); return this.lucid.wallet.getUtxos(); } + public async getAddressUtxos(address: string): Promise { + if (!this.providerSetup) throw new Error('setProvider must be called before getAddressUtxos'); + return this.lucid.utxosAt(address); + } + public async getWalletAddress(): Promise { - if (!this.providerSetup) - throw new Error('setProvider must be called before getWalletAddress'); + if (!this.providerSetup) throw new Error('setProvider must be called before getWalletAddress'); return this.lucid.wallet.address(); } public async getUtxosByOutRef(outRefs: OutRef[]): Promise { - if (!this.providerSetup) - throw new Error('setProvider must be called before getUtxosByOutRef'); + if (!this.providerSetup) throw new Error('setProvider must be called before getUtxosByOutRef'); return this.lucid.utxosByOutRef(outRefs); } public getAddressPK(address: string): string { - if (!this.providerSetup) - throw new Error('setProvider must be called before getAddressPK'); + if (!this.providerSetup) throw new Error('setProvider must be called before getAddressPK'); return this.lucid.utils.getAddressDetails(address).paymentCredential!.hash; } + public async waitForTx(txHash: string): Promise { + if (!this.providerSetup) throw new Error('setProvider must be called before waitForTx'); + return this.lucid.awaitTx(txHash); + } + public async mintSingleton(name: string, utxos: UTxO[]): Promise { if (!this.providerSetup || !this.objectContractSetup) - throw new Error( - 'setProvider and setObjectContract must be called before mintSingleton' - ); + throw new Error('setProvider and setObjectContract must be called before mintSingleton'); - const outRef = new Constr(0, [ - new Constr(0, [utxos[0].txHash]), - BigInt(utxos[0].outputIndex), - ]); + const outRef = new Constr(0, [new Constr(0, [utxos[0].txHash]), BigInt(utxos[0].outputIndex)]); const singletonBytes = applyParamsToScript(this.validators.singleton.script, [ fromText(name), - outRef, + outRef ]); - const singletonContract = { type: 'PlutusV2', script: applyDoubleCborEncoding(singletonBytes) } + this.singletonContract = { type: 'PlutusV2', script: applyDoubleCborEncoding(singletonBytes) }; const policyId = this.lucid.utils.validatorToScriptHash({ type: 'PlutusV2', - script: singletonBytes, + script: singletonBytes }); - return this.lucid.newTx() + return this.lucid + .newTx() .collectFrom(utxos) - .attachMintingPolicy(singletonContract as never) - .mintAssets( - { [`${policyId}${fromText(name)}`]: BigInt(1) }, - this.mintRedeemer - ) + .attachMintingPolicy(this.singletonContract) + .mintAssets({ [`${policyId}${fromText(name)}`]: BigInt(1) }, this.mintRedeemer) .payToContract( this.objectEventContractAddress, { - inline: this.objectDatum, + inline: this.objectDatum }, { [`${policyId}${fromText(name)}`]: BigInt(1) } ) @@ -193,7 +184,7 @@ export class EventFactory { protocol_version: protocolVersion, data_reference: dataReference, event_creation_info: eventCreationInfo, - signers: signers, + signers: signers }, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error @@ -201,14 +192,9 @@ export class EventFactory { ); } - public async recreate( - signerAddress: string, - utxos: UTxO[] - ): Promise { + public async recreate(signerAddress: string, utxos: UTxO[]): Promise { if (!this.providerSetup || !this.objectContractSetup) - throw new Error( - 'setProvider and setObjectContract must be called before recreate' - ); + throw new Error('setProvider and setObjectContract must be called before recreate'); const tx = await this.lucid .newTx() @@ -235,7 +221,7 @@ export class EventFactory { objectDatum!.event_creation_info === '' ? utxo.txHash : objectDatum!.event_creation_info, - signers: objectDatum!.signers, + signers: objectDatum!.signers }, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error @@ -244,42 +230,41 @@ export class EventFactory { tx.payToContract( utxo.address, { - inline: newObjectDatum, + inline: newObjectDatum }, utxo.assets ); }); return tx - .payToAddress( - this.isMainnet - ? WINTER_FEE_ADDRESS_MAINNET - : WINTER_FEE_ADDRESS_TESTNET, - { lovelace: WINTER_FEE } - ) + .payToAddress(this.isMainnet ? WINTER_FEE_ADDRESS_MAINNET : WINTER_FEE_ADDRESS_TESTNET, { + lovelace: WINTER_FEE + }) .attachSpendingValidator(this.objectEventContract) .complete(); } public async spend( recipientAddress: string, - KOIOS_URL: string, signerAddress: string, - utxos: UTxO[] + utxos: UTxO[], + KOIOS_URL?: string, + singletonContracts?: MintingPolicy[] ) { if (!this.providerSetup || !this.objectContractSetup) - throw new Error( - 'setProvider and setObjectContract must be called before spend' - ); + throw new Error('setProvider and setObjectContract must be called before spend'); + + if (!KOIOS_URL && !singletonContracts) + throw new Error('either KOIOS_URL or singletonContracts must be provided'); - const cardanoKoiosClient = new Koios(KOIOS_URL); + const cardanoKoiosClient = KOIOS_URL ? new Koios(KOIOS_URL) : undefined; const tx = await this.lucid.newTx().collectFrom(utxos, this.spendRedeemer); - for (const utxo of utxos) { + for (let index = 0; index < utxos.length; index++) { try { Data.from( - utxo.datum!, + utxos[index].datum!, // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error ObjectDatum @@ -288,33 +273,29 @@ export class EventFactory { throw new Error('issue with datum'); } - const tokenId = Object.keys(utxo.assets).filter( - (k) => k !== 'lovelace' - )[0]; + const tokenId = Object.keys(utxos[index].assets).filter((k) => k !== 'lovelace')[0]; // const tokenName = tokenId.substring(56); const policyId = tokenId.substring(0, 56); - const scriptBytes = (await cardanoKoiosClient.scriptInfo([policyId]))[0] - .bytes; + const scriptBytes = cardanoKoiosClient + ? (await cardanoKoiosClient.scriptInfo([policyId]))[0].bytes + : singletonContracts![index].script; const validator = { type: 'PlutusV2', - script: scriptBytes, + script: scriptBytes }; - tx.payToAddress(recipientAddress, { lovelace: utxo.assets.lovelace }) + tx.payToAddress(recipientAddress, { lovelace: utxos[index].assets.lovelace }) .attachMintingPolicy(validator as never) .mintAssets({ [tokenId]: BigInt(-1) }, this.burnRedeemer); } return tx .addSigner(signerAddress) - .payToAddress( - this.isMainnet - ? WINTER_FEE_ADDRESS_MAINNET - : WINTER_FEE_ADDRESS_TESTNET, - { lovelace: WINTER_FEE } - ) + .payToAddress(this.isMainnet ? WINTER_FEE_ADDRESS_MAINNET : WINTER_FEE_ADDRESS_TESTNET, { + lovelace: WINTER_FEE + }) .attachSpendingValidator(this.objectEventContract) .complete(); }