diff --git a/.eslintrc.json b/.eslintrc.json index 7c1b546..69e8b9f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -58,6 +58,7 @@ ] } ], - "simple-import-sort/exports": "error" + "simple-import-sort/exports": "error", + "@typescript-eslint/no-explicit-any": "off" } } diff --git a/package.json b/package.json index 2df246e..268f0c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@koralabs/handles-marketplace-contracts", - "version": "0.1.1", + "version": "0.1.2", "description": "Smart Contract of Ada Handles Marketplace", "type": "module", "main": "index.js", diff --git a/src/buy.ts b/src/buy.ts index 6e70b3a..102f021 100644 --- a/src/buy.ts +++ b/src/buy.ts @@ -2,6 +2,7 @@ import { HANDLE_POLICY_ID, MIN_FEE, MIN_LOVELACE } from "./constants"; import { buildDatumTag, decodeDatum, decodeParametersDatum } from "./datum"; import { deployedScripts } from "./deployed"; import { mayFail, mayFailAsync, mayFailTransaction } from "./helpers"; +import { BuildTxError, SuccessResult } from "./types"; import { bigIntMax, fetchLatestmarketplaceScriptDetail, @@ -12,7 +13,7 @@ import { import * as helios from "@koralabs/helios"; import { IUTxO, Network } from "@koralabs/kora-labs-common"; import { Buy } from "redeemer"; -import { Err, Ok, Result } from "ts-res"; +import { Err, Result } from "ts-res"; /** * Configuration of function to buy handle @@ -56,13 +57,13 @@ interface BuyWithAuthConfig { * Buy Handle on marketplace * @param {BuyConfig} config * @param {Network} network - * @returns {Promise>} + * @returns {Promise>} */ const buy = async ( config: BuyConfig, network: Network -): Promise> => { +): Promise> => { const { changeBech32Address, cborUtxos, handleHex, listingUtxo } = config; /// fetch marketplace reference script detail @@ -76,9 +77,10 @@ const buy = async ( : Object.values(deployedScripts[network])[0]; const { cbor, datumCbor, refScriptUtxo } = refScriptDetail; - if (!cbor) return Err(`Deploy script cbor is empty`); - if (!datumCbor) return Err(`Deploy script's datum cbor is empty`); - if (!refScriptUtxo) return Err(`Deployed script UTxO is not defined`); + if (!cbor) return Err(new Error("Deploy script cbor is empty")); + if (!datumCbor) return Err(new Error("Deploy script's datum cbor is empty")); + if (!refScriptUtxo) + return Err(new Error("Deployed script UTxO is not defined")); /// fetch network parameter const networkParams = fetchNetworkParameters(network); @@ -88,7 +90,7 @@ const buy = async ( decodeParametersDatum(datumCbor) ).complete(); if (!parametersResult.ok) - return Err(`Deployed script's datum cbor is invalid`); + return Err(new Error("Deployed script's datum cbor is invalid")); const parameters = parametersResult.data; /// get uplc program @@ -96,12 +98,16 @@ const buy = async ( getUplcProgram(parameters, true) ).complete(); if (!uplcProgramResult.ok) - return Err(`Getting Uplc Program error: ${uplcProgramResult.error}`); + return Err( + new Error("Getting Uplc Program error: ${uplcProgramResult.error}") + ); const uplcProgram = uplcProgramResult.data; /// check deployed script cbor hex if (cbor != helios.bytesToHex(uplcProgram.toCbor())) - return Err(`Deployed script's cbor doesn't match with its parameter`); + return Err( + new Error("Deployed script's cbor doesn't match with its parameter") + ); const changeAddress = helios.Address.fromBech32(changeBech32Address); const utxos = cborUtxos.map((cborUtxo) => @@ -127,12 +133,12 @@ const buy = async ( ); const handleRawDatum = handleUtxo.output.datum; - if (!handleRawDatum) return Err("Handle UTxO datum not found"); + if (!handleRawDatum) return Err(new Error("Handle UTxO datum not found")); const datumResult = await mayFailAsync(() => decodeDatum(handleRawDatum) ).complete(); if (!datumResult.ok) - return Err(`Decoding Datum Cbor error: ${datumResult.error}`); + return Err(new Error(`Decoding Datum Cbor error: ${datumResult.error}`)); const datum = datumResult.data; /// take fund to pay payouts @@ -151,11 +157,13 @@ const buy = async ( /// make redeemer const redeemer = mayFail(() => Buy(0)); - if (!redeemer.ok) return Err(`Making Redeemer error: ${redeemer.error}`); + if (!redeemer.ok) + return Err(new Error(`Making Redeemer error: ${redeemer.error}`)); /// build datum tag const datumTag = mayFail(() => buildDatumTag(handleUtxo.outputId)); - if (!datumTag.ok) return Err(`Building Datum Tag error: ${datumTag.error}`); + if (!datumTag.ok) + return Err(new Error(`Building Datum Tag error: ${datumTag.error}`)); /// marketplace fee output const marketplaceFeeOutput = new helios.TxOutput( @@ -217,23 +225,23 @@ const buy = async ( /// finalize tx const txCompleteResult = await mayFailTransaction( + tx, () => tx.finalize(networkParams, changeAddress, unSelected), refScriptDetail.unoptimizedCbor ).complete(); - if (!txCompleteResult.ok) return Err(txCompleteResult.error); - return Ok(txCompleteResult.data.toCborHex()); + return txCompleteResult; }; /** * Buy Handle on marketplace with one of authorizers * @param {BuyWithAuthConfig} config * @param {Network} network - * @returns {Promise>} + * @returns {Promise>} */ const buyWithAuth = async ( config: BuyWithAuthConfig, network: Network -): Promise> => { +): Promise> => { const { changeBech32Address, cborUtxos, @@ -253,9 +261,10 @@ const buyWithAuth = async ( : Object.values(deployedScripts[network])[0]; const { cbor, datumCbor, refScriptUtxo } = refScriptDetail; - if (!cbor) return Err(`Deploy script cbor is empty`); - if (!datumCbor) return Err(`Deploy script's datum cbor is empty`); - if (!refScriptUtxo) return Err(`Deployed script UTxO is not defined`); + if (!cbor) return Err(new Error("Deploy script cbor is empty")); + if (!datumCbor) return Err(new Error("Deploy script's datum cbor is empty")); + if (!refScriptUtxo) + return Err(new Error("Deployed script UTxO is not defined")); /// fetch network parameter const networkParams = fetchNetworkParameters(network); @@ -265,7 +274,7 @@ const buyWithAuth = async ( decodeParametersDatum(datumCbor) ).complete(); if (!parametersResult.ok) - return Err(`Deployed script's datum cbor is invalid`); + return Err(new Error("Deployed script's datum cbor is invalid")); const parameters = parametersResult.data; /// get uplc program @@ -273,12 +282,16 @@ const buyWithAuth = async ( getUplcProgram(parameters, true) ).complete(); if (!uplcProgramResult.ok) - return Err(`Getting Uplc Program error: ${uplcProgramResult.error}`); + return Err( + new Error(`Getting Uplc Program error: ${uplcProgramResult.error}`) + ); const uplcProgram = uplcProgramResult.data; /// check deployed script cbor hex if (cbor != helios.bytesToHex(uplcProgram.toCbor())) - return Err(`Deployed script's cbor doesn't match with its parameter`); + return Err( + new Error("Deployed script's cbor doesn't match with its parameter") + ); /// check authorizer pub key hash if ( @@ -286,7 +299,7 @@ const buyWithAuth = async ( (authorizer) => authorizer == authorizerPubKeyHash ) ) - return Err(`Authorizer Pub Key Hash is not valid`); + return Err(new Error("Authorizer Pub Key Hash is not valid")); const changeAddress = helios.Address.fromBech32(changeBech32Address); const utxos = cborUtxos.map((cborUtxo) => @@ -312,12 +325,12 @@ const buyWithAuth = async ( ); const handleRawDatum = handleUtxo.output.datum; - if (!handleRawDatum) return Err("Handle UTxO datum not found"); + if (!handleRawDatum) return Err(new Error("Handle UTxO datum not found")); const datumResult = await mayFailAsync(() => decodeDatum(handleRawDatum) ).complete(); if (!datumResult.ok) - return Err(`Decoding Datum Cbor error: ${datumResult.error}`); + return Err(new Error(`Decoding Datum Cbor error: ${datumResult.error}`)); const datum = datumResult.data; /// take fund to pay payouts @@ -333,11 +346,13 @@ const buyWithAuth = async ( /// make redeemer const redeemer = mayFail(() => Buy(0)); - if (!redeemer.ok) return Err(`Making Redeemer error: ${redeemer.error}`); + if (!redeemer.ok) + return Err(new Error(`Making Redeemer error: ${redeemer.error}`)); /// build datum tag const datumTag = mayFail(() => buildDatumTag(handleUtxo.outputId)); - if (!datumTag.ok) return Err(`Building Datum Tag error: ${datumTag.error}`); + if (!datumTag.ok) + return Err(new Error(`Building Datum Tag error: ${datumTag.error}`)); /// payout outputs const payoutOutputs = datum.payouts.map( @@ -392,11 +407,11 @@ const buyWithAuth = async ( /// finalize tx const txCompleteResult = await mayFailTransaction( + tx, () => tx.finalize(networkParams, changeAddress, unSelected), refScriptDetail.unoptimizedCbor ).complete(); - if (!txCompleteResult.ok) return Err(txCompleteResult.error); - return Ok(txCompleteResult.data.toCborHex()); + return txCompleteResult; }; export { buy, buyWithAuth }; diff --git a/src/commands/buy.ts b/src/commands/buy.ts index 72d92d7..4c01e3d 100644 --- a/src/commands/buy.ts +++ b/src/commands/buy.ts @@ -53,7 +53,7 @@ program }; const txResult = await buy(buyConfig, config.network); - if (!txResult.ok) return program.error(txResult.error); + if (!txResult.ok) return program.error(txResult.error.message); console.log("\nTransaction CBOR Hex, copy and paste to wallet\n"); console.log(txResult.data); } @@ -108,8 +108,7 @@ program }; const txResult = await buyWithAuth(buyWithAuthConfig, config.network); - if (!txResult.ok) return program.error(txResult.error); - console.log("\nTransaction CBOR Hex, copy and paste to wallet\n"); - console.log(txResult.data); + if (!txResult.ok) console.log(txResult.error); + else console.log(txResult.data); } ); diff --git a/src/commands/list.ts b/src/commands/list.ts index 90854ba..f5fe35c 100644 --- a/src/commands/list.ts +++ b/src/commands/list.ts @@ -53,9 +53,8 @@ const buyCommand = program }; const txResult = await list(listConfig, config.network); - if (!txResult.ok) return program.error(txResult.error); - console.log("\nTransaction CBOR Hex, copy and paste to wallet\n"); - console.log(txResult.data); + if (!txResult.ok) console.log(txResult.error); + else console.log(txResult.data); } ); diff --git a/src/commands/update.ts b/src/commands/update.ts index 3aef04b..f24ab45 100644 --- a/src/commands/update.ts +++ b/src/commands/update.ts @@ -68,9 +68,8 @@ const updateCommand = program }; const txResult = await update(updateConfig, config.network); - if (!txResult.ok) return program.error(txResult.error); - console.log("\nTransaction CBOR Hex, copy and paste to wallet\n"); - console.log(txResult.data); + if (!txResult.ok) console.log(txResult.error); + else console.log(txResult.data); } ); diff --git a/src/commands/withdraw.ts b/src/commands/withdraw.ts index 092862f..6f43982 100644 --- a/src/commands/withdraw.ts +++ b/src/commands/withdraw.ts @@ -53,9 +53,8 @@ const withdrawCommand = program }; const txResult = await withdraw(withdrawConfig, config.network); - if (!txResult.ok) return program.error(txResult.error); - console.log("\nTransaction CBOR Hex, copy and paste to wallet\n"); - console.log(txResult.data); + if (!txResult.ok) console.log(txResult.error); + else console.log(txResult.data); } ); diff --git a/src/datum.ts b/src/datum.ts index fdad412..41dec17 100644 --- a/src/datum.ts +++ b/src/datum.ts @@ -54,7 +54,6 @@ const decodeDatum = async (datum: helios.Datum): Promise => { }); const owner = helios.PubKeyHash.fromHex(decoded[1].slice(2)).hex; - // eslint-disable-next-line @typescript-eslint/no-explicit-any const payouts: Payout[] = decoded[0].map((rawPayout: any) => { const address = helios.Address.fromHex(rawPayout[0].slice(2)).toBech32(); const amountLovelace = BigInt(rawPayout[1]) as bigint; diff --git a/src/helpers/api.ts b/src/helpers/api.ts index 3883097..326d82c 100644 --- a/src/helpers/api.ts +++ b/src/helpers/api.ts @@ -9,7 +9,6 @@ import { fetch } from "cross-fetch"; const fetchApi = async ( endpoint: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any params: any = {} ): Promise => { const { headers, ...rest } = params; @@ -31,7 +30,6 @@ const fetchApi = async ( const fetchApiJson = async ( endpoint: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any params: any = {} ): Promise => { params.headers = { diff --git a/src/helpers/common/invariant.ts b/src/helpers/common/invariant.ts index 0bfbb57..b9713ba 100644 --- a/src/helpers/common/invariant.ts +++ b/src/helpers/common/invariant.ts @@ -1,7 +1,6 @@ const prefix: string = "Invariant failed"; const invariant: ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any condition: any, message?: string | (() => string) ) => asserts condition = (condition, message?: string | (() => string)) => { diff --git a/src/helpers/error/tx.ts b/src/helpers/error/tx.ts index 114313d..c621271 100644 --- a/src/helpers/error/tx.ts +++ b/src/helpers/error/tx.ts @@ -1,13 +1,13 @@ -import convertError from "./convert"; +import { BuildTxError, SuccessResult } from "../../types"; import * as helios from "@koralabs/helios"; import { Err, Ok, Result } from "ts-res"; type Callback = () => Promise; -type ErrType = string | Error | void | undefined; +type ErrType = string | Error | BuildTxError | void | undefined; type HandleableResult = { handle: (handler: (e: E) => void) => HandleableResult; - complete: () => Promise>; + complete: () => Promise>; }; const buildRefScriptUplcProgram = (cbor: string) => { @@ -28,18 +28,21 @@ const buildRefScriptUplcProgram = (cbor: string) => { }; const mayFailTransaction = ( + tx: helios.Tx, callback: Callback, unoptimzedScriptCbor?: string -): HandleableResult => { +): HandleableResult => { const createHandleable = ( - handler: (e: string) => void - ): HandleableResult => { + handler: (e: Error) => void + ): HandleableResult => { return { handle: (handler) => createHandleable(handler), - complete: async (): Promise> => { + complete: async (): Promise< + Result + > => { try { - return Ok(await callback()); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + const tx = await callback(); + return Ok({ cbor: tx.toCborHex(), dump: tx.dump() }); } catch (error: any) { if (error.context && unoptimzedScriptCbor) { const { context } = error; @@ -60,20 +63,23 @@ const mayFailTransaction = ( (a) => new helios.UplcDataValue(helios.Site.dummy(), a) ) ); - const errorMessage = res.toString(); - handler(errorMessage); - return Err(errorMessage); - // eslint-disable-next-line @typescript-eslint/no-explicit-any + error.message = res.toString(); + + const buildTxError = BuildTxError.fromError(error, tx); + handler(buildTxError); + return Err(buildTxError); } catch (runProgramError: any) { - const errorMessage = `Error running program: ${runProgramError.message} with error ${error.message}`; - handler(errorMessage); - return Err(errorMessage); + runProgramError.message = `Error running program: ${runProgramError.message} with error ${error.message}`; + + const buildTxError = BuildTxError.fromError(runProgramError, tx); + handler(buildTxError); + return Err(buildTxError); } } - const errorMessage = convertError(error); - handler(errorMessage); - return Err(errorMessage); + const buildTxError = BuildTxError.fromError(error, tx); + handler(buildTxError); + return Err(buildTxError); } }, }; diff --git a/src/list.ts b/src/list.ts index 6537e33..0064974 100644 --- a/src/list.ts +++ b/src/list.ts @@ -2,7 +2,7 @@ import { HANDLE_POLICY_ID, MIN_LOVELACE } from "./constants"; import { buildDatum, decodeParametersDatum } from "./datum"; import { deployedScripts } from "./deployed"; import { mayFail, mayFailAsync } from "./helpers"; -import { Payout } from "./types"; +import { Payout, SuccessResult } from "./types"; import { fetchLatestmarketplaceScriptDetail, fetchNetworkParameters, @@ -33,12 +33,12 @@ interface ListConfig { * List Handle to marketplace * @param {ListConfig} config * @param {Network} network - * @returns {Promise>} + * @returns {Promise>} */ const list = async ( config: ListConfig, network: Network -): Promise> => { +): Promise> => { const { changeBech32Address, cborUtxos, handleHex, payouts } = config; /// fetch marketplace reference script detail @@ -51,9 +51,10 @@ const list = async ( ? refScriptDetailResult.data : Object.values(deployedScripts[network])[0]; const { cbor, datumCbor, refScriptUtxo } = refScriptDetail; - if (!cbor) return Err(`Deploy script cbor is empty`); - if (!datumCbor) return Err(`Deploy script's datum cbor is empty`); - if (!refScriptUtxo) return Err(`Deployed script UTxO is not defined`); + if (!cbor) return Err(new Error("Deploy script cbor is empty")); + if (!datumCbor) return Err(new Error("Deploy script's datum cbor is empty")); + if (!refScriptUtxo) + return Err(new Error("Deployed script UTxO is not defined")); /// fetch network parameter const networkParams = fetchNetworkParameters(network); @@ -63,7 +64,7 @@ const list = async ( decodeParametersDatum(datumCbor) ).complete(); if (!parametersResult.ok) - return Err(`Deployed script's datum cbor is invalid`); + return Err(new Error("Deployed script's datum cbor is invalid")); const parameters = parametersResult.data; /// get uplc program @@ -71,12 +72,16 @@ const list = async ( getUplcProgram(parameters, true) ).complete(); if (!uplcProgramResult.ok) - return Err(`Getting Uplc Program error: ${uplcProgramResult.error}`); + return Err( + new Error(`Getting Uplc Program error: ${uplcProgramResult.error}`) + ); const uplcProgram = uplcProgramResult.data; /// check deployed script cbor hex if (cbor != helios.bytesToHex(uplcProgram.toCbor())) - return Err(`Deployed script's cbor doesn't match with its parameter`); + return Err( + new Error("Deployed script's cbor doesn't match with its parameter") + ); const changeAddress = helios.Address.fromBech32(changeBech32Address); const utxos = cborUtxos.map((cborUtxo) => @@ -91,20 +96,24 @@ const list = async ( const selectResult = mayFail(() => helios.CoinSelection.selectLargestFirst(utxos, minValue) ); - if (!selectResult.ok) return Err(selectResult.error); + if (!selectResult.ok) return Err(new Error(selectResult.error)); const [selected, unSelected] = selectResult.data; /// build datum const ownerPubKeyHash = changeAddress.pubKeyHash; - if (!ownerPubKeyHash) return Err(`Change Address doesn't have payment key`); + if (!ownerPubKeyHash) + return Err(new Error("Change Address doesn't have payment key")); const datum = mayFail(() => buildDatum({ payouts, owner: ownerPubKeyHash.hex }) ); - if (!datum.ok) return Err(`Building Datum error: ${datum.error}`); + if (!datum.ok) return Err(new Error(`Building Datum error: ${datum.error}`)); /// ada handle list update const handleListOutput = new helios.TxOutput( - helios.Address.fromHashes(uplcProgram.validatorHash, changeAddress.stakingHash), + helios.Address.fromHashes( + uplcProgram.validatorHash, + changeAddress.stakingHash + ), new helios.Value(0n, handleAsset), datum.data ); @@ -117,9 +126,12 @@ const list = async ( tx.finalize(networkParams, changeAddress, unSelected) ).complete(); if (!txCompleteResult.ok) - return Err(`Finalizing Tx error: ${txCompleteResult.error}`); + return Err(new Error(`Finalizing Tx error: ${txCompleteResult.error}`)); - return Ok(txCompleteResult.data.toCborHex()); + return Ok({ + cbor: txCompleteResult.data.toCborHex(), + dump: txCompleteResult.data.dump(), + }); }; export { list }; diff --git a/src/types.ts b/src/types.ts index 3f09581..59ec8d5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,5 @@ +import * as helios from "@koralabs/helios"; + /** * Parameters for marketplace contract * @interface @@ -34,4 +36,42 @@ interface Datum { owner: string; } -export type { Datum, Parameters, Payout }; +class BuildTxError extends Error { + code: number; + failedTxCbor: string; + failedTxJson: object; + + static fromError(error: Error, failedTx: helios.Tx) { + const err = new BuildTxError( + error.message, + failedTx.toCborHex(), + failedTx.dump() + ); + err.stack = error.stack; + err.cause = error.cause; + return err; + } + + constructor(message: string, failedTxCbor: string, failedTxJson: object) { + super(message); + this.name = "BuildTxError"; + this.code = 500; + this.failedTxCbor = failedTxCbor; + this.failedTxJson = failedTxJson; + } +} + +/** + * SuccessResult - attached to handles listed on marketplace + * @interface + * @typedef {object} SuccessResult + * @property {string} cbor CBOR Hex of transaction, you can sign and submit + * @property {any} dump Transaction's Dump + */ +interface SuccessResult { + cbor: string; + dump: any; +} + +export type { Datum, Parameters, Payout, SuccessResult }; +export { BuildTxError }; diff --git a/src/update.ts b/src/update.ts index 2717511..19ed285 100644 --- a/src/update.ts +++ b/src/update.ts @@ -2,7 +2,7 @@ import { HANDLE_POLICY_ID, MIN_FEE } from "./constants"; import { buildDatum, decodeDatum, decodeParametersDatum } from "./datum"; import { deployedScripts } from "./deployed"; import { mayFail, mayFailAsync, mayFailTransaction } from "./helpers"; -import { Payout } from "./types"; +import { BuildTxError, Payout, SuccessResult } from "./types"; import { fetchLatestmarketplaceScriptDetail, fetchNetworkParameters, @@ -12,7 +12,7 @@ import { import * as helios from "@koralabs/helios"; import { IUTxO, Network } from "@koralabs/kora-labs-common"; import { WithdrawOrUpdate } from "redeemer"; -import { Err, Ok, Result } from "ts-res"; +import { Err, Result } from "ts-res"; /** * Configuration of function to update handle @@ -38,12 +38,12 @@ interface UpdateConfig { * Update Handle on marketplace * @param {UpdateConfig} config * @param {Network} network - * @returns {Promise>} + * @returns {Promise>} */ const update = async ( config: UpdateConfig, network: Network -): Promise> => { +): Promise> => { const { changeBech32Address, cborUtxos, handleHex, listingUtxo, newPayouts } = config; @@ -58,9 +58,10 @@ const update = async ( : Object.values(deployedScripts[network])[0]; const { cbor, datumCbor, refScriptUtxo } = refScriptDetail; - if (!cbor) return Err(`Deploy script cbor is empty`); - if (!datumCbor) return Err(`Deploy script's datum cbor is empty`); - if (!refScriptUtxo) return Err(`Deployed script UTxO is not defined`); + if (!cbor) return Err(new Error("Deploy script cbor is empt")); + if (!datumCbor) return Err(new Error("Deploy script's datum cbor is empty")); + if (!refScriptUtxo) + return Err(new Error("Deployed script UTxO is not defined")); /// fetch network parameter const networkParams = fetchNetworkParameters(network); @@ -70,7 +71,7 @@ const update = async ( decodeParametersDatum(datumCbor) ).complete(); if (!parametersResult.ok) - return Err(`Deployed script's datum cbor is invalid`); + return Err(new Error("Deployed script's datum cbor is invalid")); const parameters = parametersResult.data; /// get uplc program @@ -78,12 +79,16 @@ const update = async ( getUplcProgram(parameters, true) ).complete(); if (!uplcProgramResult.ok) - return Err(`Getting Uplc Program error: ${uplcProgramResult.error}`); + return Err( + new Error(`Getting Uplc Program error: ${uplcProgramResult.error}`) + ); const uplcProgram = uplcProgramResult.data; /// check deployed script cbor hex if (cbor != helios.bytesToHex(uplcProgram.toCbor())) - return Err(`Deployed script's cbor doesn't match with its parameter`); + return Err( + new Error("Deployed script's cbor doesn't match with its parameter") + ); const changeAddress = helios.Address.fromBech32(changeBech32Address); const utxos = cborUtxos.map((cborUtxo) => @@ -109,18 +114,19 @@ const update = async ( ); const handleRawDatum = handleUtxo.output.datum; - if (!handleRawDatum) return Err("Handle UTxO datum not found"); + if (!handleRawDatum) return Err(new Error("Handle UTxO datum not found")); const datumResult = await mayFailAsync(() => decodeDatum(handleRawDatum) ).complete(); if (!datumResult.ok) - return Err(`Decoding Datum Cbor error: ${datumResult.error}`); + return Err(new Error(`Decoding Datum Cbor error: ${datumResult.error}`)); const datum = datumResult.data; const ownerPubKeyHash = changeAddress.pubKeyHash; - if (!ownerPubKeyHash) return Err(`Change Address doesn't have payment key`); + if (!ownerPubKeyHash) + return Err(new Error("Change Address doesn't have payment key")); if (datum.owner != ownerPubKeyHash.hex) - return Err(`You must be owner to update`); + return Err(new Error("You must be owner to update")); /// take fund const [selected, unSelected] = helios.CoinSelection.selectLargestFirst( @@ -130,13 +136,15 @@ const update = async ( /// redeemer const redeemer = mayFail(() => WithdrawOrUpdate()); - if (!redeemer.ok) return Err(`Making Redeemer error: ${redeemer.error}`); + if (!redeemer.ok) + return Err(new Error(`Making Redeemer error: ${redeemer.error}`)); /// build new datum const newDatum = mayFail(() => buildDatum({ payouts: newPayouts, owner: datum.owner }) ); - if (!newDatum.ok) return Err(`Building New Datum error: ${newDatum.error}`); + if (!newDatum.ok) + return Err(new Error(`Building New Datum error: ${newDatum.error}`)); /// add handle update output const handleUpdateOutput = new helios.TxOutput( @@ -178,11 +186,11 @@ const update = async ( /// finalize tx const txCompleteResult = await mayFailTransaction( + tx, () => tx.finalize(networkParams, changeAddress, unSelected), refScriptDetail.unoptimizedCbor ).complete(); - if (!txCompleteResult.ok) return Err(txCompleteResult.error); - return Ok(txCompleteResult.data.toCborHex()); + return txCompleteResult; }; export { update }; diff --git a/src/withdraw.ts b/src/withdraw.ts index 7adbbfd..bf82092 100644 --- a/src/withdraw.ts +++ b/src/withdraw.ts @@ -2,6 +2,7 @@ import { HANDLE_POLICY_ID, MIN_FEE } from "./constants"; import { decodeDatum, decodeParametersDatum } from "./datum"; import { deployedScripts } from "./deployed"; import { mayFail, mayFailAsync, mayFailTransaction } from "./helpers"; +import { BuildTxError, SuccessResult } from "./types"; import { fetchLatestmarketplaceScriptDetail, fetchNetworkParameters, @@ -11,7 +12,7 @@ import { import * as helios from "@koralabs/helios"; import { IUTxO, Network } from "@koralabs/kora-labs-common"; import { WithdrawOrUpdate } from "redeemer"; -import { Err, Ok, Result } from "ts-res"; +import { Err, Result } from "ts-res"; /** * Configuration of function to withdraw handle @@ -35,12 +36,12 @@ interface WithdrawConfig { * Withdraw Handle from marketplace * @param {WithdrawConfig} config * @param {Network} network - * @returns {Promise>} + * @returns {Promise>} */ const withdraw = async ( config: WithdrawConfig, network: Network -): Promise> => { +): Promise> => { const { changeBech32Address, cborUtxos, handleHex, listingUtxo } = config; /// fetch marketplace reference script detail @@ -54,9 +55,10 @@ const withdraw = async ( : Object.values(deployedScripts[network])[0]; const { cbor, datumCbor, refScriptUtxo } = refScriptDetail; - if (!cbor) return Err(`Deploy script cbor is empty`); - if (!datumCbor) return Err(`Deploy script's datum cbor is empty`); - if (!refScriptUtxo) return Err(`Deployed script UTxO is not defined`); + if (!cbor) return Err(new Error("Deploy script cbor is empty")); + if (!datumCbor) return Err(new Error("Deploy script's datum cbor is empty")); + if (!refScriptUtxo) + return Err(new Error("Deployed script UTxO is not defined")); /// fetch network parameter const networkParams = fetchNetworkParameters(network); @@ -66,7 +68,7 @@ const withdraw = async ( decodeParametersDatum(datumCbor) ).complete(); if (!parametersResult.ok) - return Err(`Deployed script's datum cbor is invalid`); + return Err(new Error("Deployed script's datum cbor is invalid")); const parameters = parametersResult.data; /// get uplc program @@ -74,12 +76,16 @@ const withdraw = async ( getUplcProgram(parameters, true) ).complete(); if (!uplcProgramResult.ok) - return Err(`Getting Uplc Program error: ${uplcProgramResult.error}`); + return Err( + new Error(`Getting Uplc Program error: ${uplcProgramResult.error}`) + ); const uplcProgram = uplcProgramResult.data; /// check deployed script cbor hex if (cbor != helios.bytesToHex(uplcProgram.toCbor())) - return Err(`Deployed script's cbor doesn't match with its parameter`); + return Err( + new Error("Deployed script's cbor doesn't match with its parameter") + ); const changeAddress = helios.Address.fromBech32(changeBech32Address); const utxos = cborUtxos.map((cborUtxo) => @@ -105,18 +111,19 @@ const withdraw = async ( ); const handleRawDatum = handleUtxo.output.datum; - if (!handleRawDatum) return Err("Handle UTxO datum not found"); + if (!handleRawDatum) return Err(new Error("Handle UTxO datum not found")); const datumResult = await mayFailAsync(() => decodeDatum(handleRawDatum) ).complete(); if (!datumResult.ok) - return Err(`Decoding Datum Cbor error: ${datumResult.error}`); + return Err(new Error(`Decoding Datum Cbor error: ${datumResult.error}`)); const datum = datumResult.data; const ownerPubKeyHash = changeAddress.pubKeyHash; - if (!ownerPubKeyHash) return Err(`Change Address doesn't have payment key`); + if (!ownerPubKeyHash) + return Err(new Error("Change Address doesn't have payment key")); if (datum.owner != ownerPubKeyHash.hex) - return Err(`You must be owner to withdraw`); + return Err(new Error("You must be owner to withdraw")); /// take fund const [selected, unSelected] = helios.CoinSelection.selectLargestFirst( @@ -126,7 +133,8 @@ const withdraw = async ( /// redeemer const redeemer = mayFail(() => WithdrawOrUpdate()); - if (!redeemer.ok) return Err(`Making Redeemer error: ${redeemer.error}`); + if (!redeemer.ok) + return Err(new Error(`Making Redeemer error: ${redeemer.error}`)); /// add handle withdraw output const handleWithdrawOutput = new helios.TxOutput( @@ -167,11 +175,11 @@ const withdraw = async ( /// finalize tx const txCompleteResult = await mayFailTransaction( + tx, () => tx.finalize(networkParams, changeAddress, unSelected), refScriptDetail.unoptimizedCbor ).complete(); - if (!txCompleteResult.ok) return Err(txCompleteResult.error); - return Ok(txCompleteResult.data.toCborHex()); + return txCompleteResult; }; export { withdraw };