Skip to content

Commit

Permalink
add new utxo fn, add tx wait, save singleton contract in class, allow…
Browse files Browse the repository at this point in the history
… spend to use saved singleton contract instead of koios
  • Loading branch information
mgpai22 committed Jan 25, 2024
1 parent f70b9a7 commit cfb1ea5
Showing 1 changed file with 73 additions and 92 deletions.
165 changes: 73 additions & 92 deletions src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,26 @@ 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';

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<typeof ObjectDatum>;
Expand All @@ -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;
Expand All @@ -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, []));
Expand All @@ -77,8 +74,7 @@ export class EventFactory {
seed?: Seed,
privateKey?: PrivateKey
): Promise<this> {
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);
Expand All @@ -89,24 +85,20 @@ export class EventFactory {
return this;
}

public async setObjectContract(
objectDatumParameters: ObjectDatumParameters
): Promise<this> {
if (!this.providerSetup)
throw new Error('setProvider must be called before setObjectContract');
public async setObjectContract(objectDatumParameters: ObjectDatumParameters): Promise<this> {
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,
Expand All @@ -119,63 +111,62 @@ export class EventFactory {
}

public async getWalletUtxos(): Promise<UTxO[]> {
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<UTxO[]> {
if (!this.providerSetup) throw new Error('setProvider must be called before getAddressUtxos');
return this.lucid.utxosAt(address);
}

public async getWalletAddress(): Promise<string> {
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<UTxO[]> {
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<boolean> {
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<TxComplete> {
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) }
)
Expand All @@ -193,22 +184,17 @@ 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
ObjectDatum
);
}

public async recreate(
signerAddress: string,
utxos: UTxO[]
): Promise<TxComplete> {
public async recreate(signerAddress: string, utxos: UTxO[]): Promise<TxComplete> {
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()
Expand All @@ -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
Expand All @@ -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<ObjectDatum>(
utxo.datum!,
utxos[index].datum!,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
ObjectDatum
Expand All @@ -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();
}
Expand Down

0 comments on commit cfb1ea5

Please sign in to comment.