Skip to content

Commit

Permalink
feat: turn lockVestingTokens to effectul system
Browse files Browse the repository at this point in the history
  • Loading branch information
solidsnakedev committed Jan 29, 2024
1 parent 94f2d4a commit 83c3e12
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 76 deletions.
12 changes: 9 additions & 3 deletions src/core/errors.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {Data} from "effect"
import { Data } from "effect";
export class NoUTXOsInScriptError {
readonly _tag = "NoUTXOsInScriptError";
}
export class NoUTXOsInWallet {
readonly _tag = "NoUTXOsInScriptError";
}
export class MissingDatumError {
readonly _tag = "NoUTXOsInScriptError";
}
Expand All @@ -10,7 +13,10 @@ export class InvalidDatumError {
readonly _tag = "InvalidDatumError";
}

export class TransactionError extends Data.TaggedError("TransactionError")<{
message: string
export class FromAddressError extends Data.TaggedError("FromAddressError")<{
message: string;
}> {}

export class TransactionError extends Data.TaggedError("TransactionError")<{
message: string;
}> {}
4 changes: 1 addition & 3 deletions src/endpoints/Contract.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import {
ContractConfig,
LockTokensConfig,
Lucid,
Result,
TxComplete,
VestingDatum,
collectVestingTokens,
getVestingByAddress,
Expand All @@ -12,6 +9,7 @@ import {
} from "../index.js";
import linearVesting from "../../test/linearVesting.json" assert { type: "json" };

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const withMesh = (_scripts: ContractConfig) => (mesh: string) => {
return {
lockTokens: () => {
Expand Down
2 changes: 1 addition & 1 deletion src/endpoints/collectVestingTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { divCeil, safeParseDatum, toAddress } from "../core/utils/utils.js";
import { CollectPartialConfig, CollectPartialScripts } from "../core/types.js";
import { VestingRedeemer, VestingDatum } from "../core/contract.types.js";
import { TIME_TOLERANCE_MS } from "../index.js";
import { Effect, Either } from "effect";
import { Effect } from "effect";
import { TransactionError } from "../core/errors.js";

export const collectVestingTokens =
Expand Down
143 changes: 78 additions & 65 deletions src/endpoints/lockVestingTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,90 +2,103 @@ import {
Lucid,
SpendingValidator,
Data,
TxComplete,
toUnit,
} from "@anastasia-labs/lucid-cardano-fork";
import { fromAddress } from "../core/utils/utils.js";
import { LockTokensConfig, LockTokensScripts, Result } from "../core/types.js";
import { LockTokensConfig, LockTokensScripts } from "../core/types.js";
import { VestingDatum } from "../core/contract.types.js";
import {
PROTOCOL_FEE,
PROTOCOL_PAYMENT_KEY,
PROTOCOL_STAKE_KEY,
} from "../index.js";
import { Effect, pipe } from "effect";
import {
FromAddressError,
NoUTXOsInWallet,
TransactionError,
} from "../core/errors.js";

export const lockTokens =
(scripts: LockTokensScripts) =>
(lucid: Lucid) =>
(config: LockTokensConfig) => {
return {
build: async (): Promise<Result<TxComplete>> => {
const walletUtxos = await lucid.wallet.getUtxos();
if (!walletUtxos.length)
return { type: "error", error: new Error("No utxos in wallet") };
const program = Effect.gen(function* ($) {
const walletUtxos = yield* $(
Effect.promise(() => lucid.wallet.getUtxos())
);
if (!walletUtxos.length) yield* $(Effect.fail(new NoUTXOsInWallet()));

const vestingValidator: SpendingValidator = {
type: "PlutusV2",
script: scripts.vesting,
};
const validatorAddress =
lucid.utils.validatorToAddress(vestingValidator);
const vestingValidator: SpendingValidator = {
type: "PlutusV2",
script: scripts.vesting,
};
const validatorAddress = lucid.utils.validatorToAddress(vestingValidator);

const datum = Data.to(
{
beneficiary: fromAddress(config.beneficiary),
assetClass: {
symbol: config.vestingAsset.policyId,
name: config.vestingAsset.tokenName,
},
totalVestingQty: BigInt(
config.totalVestingQty - config.totalVestingQty * PROTOCOL_FEE
),
vestingPeriodStart: BigInt(config.vestingPeriodStart),
vestingPeriodEnd: BigInt(config.vestingPeriodEnd),
firstUnlockPossibleAfter: BigInt(config.firstUnlockPossibleAfter),
totalInstallments: BigInt(config.totalInstallments),
const safeBeneficiary = yield* $(
Effect.try({
try: () => fromAddress(config.beneficiary),
catch: (error) => new FromAddressError({ message: String(error) }),
})
);

const datum = Data.to(
{
beneficiary: safeBeneficiary,
assetClass: {
symbol: config.vestingAsset.policyId,
name: config.vestingAsset.tokenName,
},
VestingDatum
);
totalVestingQty: BigInt(
config.totalVestingQty - config.totalVestingQty * PROTOCOL_FEE
),
vestingPeriodStart: BigInt(config.vestingPeriodStart),
vestingPeriodEnd: BigInt(config.vestingPeriodEnd),
firstUnlockPossibleAfter: BigInt(config.firstUnlockPossibleAfter),
totalInstallments: BigInt(config.totalInstallments),
},
VestingDatum
);

const unit = config.vestingAsset.policyId
? toUnit(config.vestingAsset.policyId, config.vestingAsset.tokenName)
: "lovelace";
const unit = config.vestingAsset.policyId
? toUnit(config.vestingAsset.policyId, config.vestingAsset.tokenName)
: "lovelace";

try {
const tx = await lucid
.newTx()
.collectFrom(walletUtxos)
.payToContract(
validatorAddress,
{ inline: datum },
{
[unit]: BigInt(
config.totalVestingQty - config.totalVestingQty * PROTOCOL_FEE
return yield* $(
Effect.tryPromise({
try: () => {
const tx = lucid
.newTx()
.collectFrom(walletUtxos)
.payToContract(
validatorAddress,
{ inline: datum },
{
[unit]: BigInt(
config.totalVestingQty -
config.totalVestingQty * PROTOCOL_FEE
),
}
)
.payToAddress(
lucid.utils.credentialToAddress(
lucid.utils.keyHashToCredential(PROTOCOL_PAYMENT_KEY),
lucid.utils.keyHashToCredential(PROTOCOL_STAKE_KEY)
),
}
)
.payToAddress(
lucid.utils.credentialToAddress(
lucid.utils.keyHashToCredential(PROTOCOL_PAYMENT_KEY),
lucid.utils.keyHashToCredential(PROTOCOL_STAKE_KEY)
),
{
[unit]: BigInt(config.totalVestingQty * PROTOCOL_FEE),
}
)
.complete();

return { type: "ok", data: tx };
} catch (error) {
if (error instanceof Error) return { type: "error", error: error };

return {
type: "error",
error: new Error(`${JSON.stringify(error)}`),
};
}
},
{
[unit]: BigInt(config.totalVestingQty * PROTOCOL_FEE),
}
)
.complete();
return tx;
},
catch: (e) => new TransactionError({ message: String(e) }),
})
);
});
return {
program: () => program,
build: () => pipe(program, Effect.either, Effect.runPromise),
unsafeBuild: () => pipe(program, Effect.runPromise),
};
};
25 changes: 25 additions & 0 deletions test/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { expect } from "vitest";
import { Emulator, Result, TxComplete } from "../src";
import { Contract } from "../src/endpoints/Contract";
import { pipe } from "effect";

export const submitAction = async (tx: Result<TxComplete>) => {
expect(tx.type).toBe("ok");
if (tx.type === "ok") {
return await (await tx.data.sign().complete()).submit();
}
};

export const collectAction = async (
user: Awaited<ReturnType<typeof Contract.withLucid>>,
emulator: Emulator
) =>
pipe(
await user
.collectVestingTokens({
vestingOutRef: (await user.getVestedTokens())[0].outRef,
currentTime: emulator.now(),
})
.build(),
submitAction
);
12 changes: 8 additions & 4 deletions test/lock-unlock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ test<LucidContext>("Test - LockTokens, Unlock Tokens", async ({
users,
emulator,
}) => {
await pipe(
await users[0]
const tx0 = await pipe(
users[0]
.lockTokens({
beneficiary: await users[1].lucid.wallet.address(),
vestingAsset: {
Expand All @@ -74,9 +74,13 @@ test<LucidContext>("Test - LockTokens, Unlock Tokens", async ({
firstUnlockPossibleAfter: emulator.now(),
totalInstallments: 4,
})
.build(),
submitAction
.program(),
Effect.andThen((tx) => Effect.promise(() => tx.sign().complete())),
Effect.andThen((tx) => Effect.promise(() => tx.submit())),
Effect.either,
Effect.runPromise
);
expect(Either.isRight(tx0)).toBe(true);

// //NOTE: INSTALLMENT 1
emulator.awaitBlock(1080);
Expand Down

0 comments on commit 83c3e12

Please sign in to comment.