From 31acbde9089984535cd192924a620656cfa21d0a Mon Sep 17 00:00:00 2001 From: Richard Nguyen Date: Sat, 29 Jun 2024 10:58:15 +0700 Subject: [PATCH 1/3] Support Script receiver on V1 swap transaction --- src/dex.ts | 101 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 90 insertions(+), 11 deletions(-) diff --git a/src/dex.ts b/src/dex.ts index f1831df..aa254c4 100644 --- a/src/dex.ts +++ b/src/dex.ts @@ -6,6 +6,7 @@ import { Data, Lucid, Network, + OutputData, SpendingValidator, TxComplete, UTxO, @@ -50,7 +51,6 @@ export type BuildCancelOrderOptions = { * @minimumLPReceived Minimum Received Amount you can accept after order is executed */ export type BuildDepositTxOptions = CommonOptions & { - // sender: Address; assetA: Asset; assetB: Asset; amountA: bigint; @@ -81,7 +81,6 @@ export type BuildZapInTxOptions = CommonOptions & { * @minimumAssetBReceived Minimum Received of Asset A in the Pool you can accept after order is executed */ export type BuildWithdrawTxOptions = CommonOptions & { - sender: Address; lpAsset: Asset; lpAmount: bigint; minimumAssetAReceived: bigint; @@ -96,7 +95,13 @@ export type BuildWithdrawTxOptions = CommonOptions & { * @expectedAmountOut The expected Amount of Asset Out you want to receive after order is executed */ export type BuildSwapExactOutTxOptions = CommonOptions & { - sender: Address; + customReceiver?: { + receiver: Address; + receiverDatum?: { + hash: string; + datum: string; + }; + }; assetIn: Asset; assetOut: Asset; maximumAmountIn: bigint; @@ -112,7 +117,13 @@ export type BuildSwapExactOutTxOptions = CommonOptions & { * @isLimitOrder Define this order is Limit Order or not */ export type BuildSwapExactInTxOptions = CommonOptions & { - sender: Address; + customReceiver?: { + receiver: Address; + receiverDatum?: { + hash: string; + datum: string; + }; + }; assetIn: Asset; amountIn: bigint; assetOut: Asset; @@ -137,6 +148,7 @@ export class Dex { ): Promise { const { sender, + customReceiver, assetIn, amountIn, assetOut, @@ -158,8 +170,8 @@ export class Dex { } const datum: OrderDatum = { sender: sender, - receiver: sender, - receiverDatumHash: undefined, + receiver: customReceiver ? customReceiver.receiver : sender, + receiverDatumHash: customReceiver?.receiverDatum?.hash, step: { type: OrderStepType.SWAP_EXACT_IN, desiredAsset: assetOut, @@ -184,6 +196,21 @@ export class Dex { } else { tx.attachMetadata(674, { msg: [MetadataMessage.SWAP_EXACT_IN_ORDER] }); } + if (customReceiver && customReceiver.receiverDatum) { + const utxoForStoringDatum = buildUtxoToStoreDatum( + this.lucid, + sender, + customReceiver.receiver, + customReceiver.receiverDatum.datum + ); + if (utxoForStoringDatum) { + tx.payToAddressWithData( + utxoForStoringDatum.address, + utxoForStoringDatum.outputData, + utxoForStoringDatum.assets + ); + } + } return await tx.complete(); } @@ -192,6 +219,7 @@ export class Dex { ): Promise { const { sender, + customReceiver, assetIn, assetOut, maximumAmountIn, @@ -214,8 +242,8 @@ export class Dex { } const datum: OrderDatum = { sender: sender, - receiver: sender, - receiverDatumHash: undefined, + receiver: customReceiver ? customReceiver.receiver : sender, + receiverDatumHash: customReceiver?.receiverDatum?.hash, step: { type: OrderStepType.SWAP_EXACT_OUT, desiredAsset: assetOut, @@ -225,7 +253,7 @@ export class Dex { depositADA: FIXED_DEPOSIT_ADA, }; - return await this.lucid + const tx = this.lucid .newTx() .payToContract( ORDER_BASE_ADDRESS[this.networkId], @@ -234,8 +262,25 @@ export class Dex { ) .payToAddress(sender, reductionAssets) .addSigner(sender) - .attachMetadata(674, { msg: [MetadataMessage.SWAP_EXACT_OUT_ORDER] }) - .complete(); + .attachMetadata(674, { msg: [MetadataMessage.SWAP_EXACT_OUT_ORDER] }); + + if (customReceiver && customReceiver.receiverDatum) { + const utxoForStoringDatum = buildUtxoToStoreDatum( + this.lucid, + sender, + customReceiver.receiver, + customReceiver.receiverDatum.datum + ); + if (utxoForStoringDatum) { + tx.payToAddressWithData( + utxoForStoringDatum.address, + utxoForStoringDatum.outputData, + utxoForStoringDatum.assets + ); + } + } + + return await tx.complete(); } async buildWithdrawTx(options: BuildWithdrawTxOptions): Promise { @@ -444,3 +489,37 @@ export class Dex { }; } } + +/** + * Return a Output that pay back to @sender and include @datum + * This function is used for @receiver of an order can be a script + * @param lucid + * @param sender + * @param receiver + * @param datum + */ +export function buildUtxoToStoreDatum( + lucid: Lucid, + sender: Address, + receiver: Address, + datum: string +): { + address: Address; + outputData: OutputData; + assets: Assets; +} | null { + const receivePaymentCred = + lucid.utils.getAddressDetails(receiver).paymentCredential; + // If receiver is not a script address, we no need to store this datum On-chain because it's useless + if (!receivePaymentCred || receivePaymentCred.type === "Key") { + return null; + } + + return { + address: sender, + assets: {}, + outputData: { + inline: datum, + }, + }; +} From f24a4b9c884dc8935172274c603df6f4e1f9cc37 Mon Sep 17 00:00:00 2001 From: h2physics Date: Mon, 9 Sep 2024 18:09:59 +0700 Subject: [PATCH 2/3] support V2 custom receiver --- src/dex-v2.ts | 104 ++++++++++++++++++++++++++++++++++++--- src/dex.ts | 59 +++++----------------- src/utils/tx.internal.ts | 35 +++++++++++++ 3 files changed, 142 insertions(+), 56 deletions(-) create mode 100644 src/utils/tx.internal.ts diff --git a/src/dex-v2.ts b/src/dex-v2.ts index e8e0198..2890fe7 100644 --- a/src/dex-v2.ts +++ b/src/dex-v2.ts @@ -27,6 +27,24 @@ import { DexVersion } from "./batcher-fee-reduction/types.internal"; import { FactoryV2 } from "./types/factory"; import { NetworkEnvironment, NetworkId } from "./types/network"; import { lucidToNetworkEnv } from "./utils/network.internal"; +import { buildUtxoToStoreDatum } from "./utils/tx.internal"; + +export type V2CustomReceiver = { + refundReceiver: Address; + refundReceiverDatum?: { + type: + | OrderV2.ExtraDatumType.DATUM_HASH + | OrderV2.ExtraDatumType.INLINE_DATUM; + datum: string; + }; + successReceiver: Address; + successReceiverDatum?: { + type: + | OrderV2.ExtraDatumType.DATUM_HASH + | OrderV2.ExtraDatumType.INLINE_DATUM; + datum: string; + }; +}; /** * Options for building Pool V2 Creation transaction @@ -50,6 +68,7 @@ export type CreatePoolV2Options = { export type BulkOrdersOption = { sender: Address; + customReceiver?: V2CustomReceiver; orderOptions: OrderOptions[]; expiredOptions?: OrderV2.ExpirySetting; availableUtxos: UTxO[]; @@ -733,6 +752,7 @@ export class DexV2 { async createBulkOrdersTx({ sender, + customReceiver, orderOptions, expiredOptions, availableUtxos, @@ -760,6 +780,10 @@ export class DexV2 { }); const limitOrders: string[] = []; const lucidTx = this.lucid.newTx(); + const necessaryExtraDatums: { + receiver: Address; + datum: string; + }[] = []; for (let i = 0; i < orderOptions.length; i++) { const option = orderOptions[i]; const { type, lpAsset } = option; @@ -796,16 +820,65 @@ export class DexV2 { type: OrderV2.AuthorizationMethodType.SIGNATURE, hash: senderPaymentCred.hash, }; + + let successReceiver: Address = sender; + let successReceiverDatum: OrderV2.ExtraDatum = { + type: OrderV2.ExtraDatumType.NO_DATUM, + }; + let refundReceiver: Address = sender; + let refundReceiverDatum: OrderV2.ExtraDatum = { + type: OrderV2.ExtraDatumType.NO_DATUM, + }; + if (customReceiver) { + const { + successReceiver: customSuccessReceiver, + successReceiverDatum: customSuccessReceiverDatum, + refundReceiver: customRefundReceiver, + refundReceiverDatum: customRefundReceiverDatum, + } = customReceiver; + successReceiver = customSuccessReceiver; + refundReceiver = customRefundReceiver; + if (!customSuccessReceiverDatum) { + successReceiverDatum = { + type: OrderV2.ExtraDatumType.NO_DATUM, + }; + } else { + const datumHash = this.lucid.utils.datumToHash( + customSuccessReceiverDatum.datum + ); + successReceiverDatum = { + type: customSuccessReceiverDatum.type, + hash: datumHash, + }; + necessaryExtraDatums.push({ + receiver: successReceiver, + datum: customSuccessReceiverDatum.datum, + }); + } + if (!customRefundReceiverDatum) { + refundReceiverDatum = { + type: OrderV2.ExtraDatumType.NO_DATUM, + }; + } else { + const datumHash = this.lucid.utils.datumToHash( + customRefundReceiverDatum.datum + ); + refundReceiverDatum = { + type: customRefundReceiverDatum.type, + hash: datumHash, + }; + necessaryExtraDatums.push({ + receiver: refundReceiver, + datum: customRefundReceiverDatum.datum, + }); + } + } const orderDatum: OrderV2.Datum = { canceller: canceller, - refundReceiver: sender, - refundReceiverDatum: { - type: OrderV2.ExtraDatumType.NO_DATUM, - }, - successReceiver: sender, - successReceiverDatum: { - type: OrderV2.ExtraDatumType.NO_DATUM, - }, + refundReceiver: refundReceiver, + refundReceiverDatum: refundReceiverDatum, + successReceiver: successReceiver, + successReceiverDatum: successReceiverDatum, step: orderStep, lpAsset: lpAsset, maxBatcherFee: totalBatcherFee, @@ -843,6 +916,21 @@ export class DexV2 { if (composeTx) { lucidTx.compose(composeTx); } + for (const necessaryExtraDatum of necessaryExtraDatums) { + const utxoForStoringDatum = buildUtxoToStoreDatum( + this.lucid, + sender, + necessaryExtraDatum.receiver, + necessaryExtraDatum.datum + ); + if (utxoForStoringDatum) { + lucidTx.payToAddressWithData( + utxoForStoringDatum.address, + utxoForStoringDatum.outputData, + utxoForStoringDatum.assets + ); + } + } return lucidTx.complete(); } diff --git a/src/dex.ts b/src/dex.ts index fcb48c8..dd7a769 100644 --- a/src/dex.ts +++ b/src/dex.ts @@ -22,6 +22,15 @@ import { import { NetworkEnvironment, NetworkId } from "./types/network"; import { OrderV1 } from "./types/order"; import { lucidToNetworkEnv } from "./utils/network.internal"; +import { buildUtxoToStoreDatum } from "./utils/tx.internal"; + +export type V1AndStableswapCustomReceiver = { + receiver: Address; + receiverDatum?: { + hash: string; + datum: string; + }; +}; /** * Common options for build Minswap transaction @@ -94,13 +103,7 @@ export type BuildWithdrawTxOptions = CommonOptions & { * @expectedAmountOut The expected Amount of Asset Out you want to receive after order is executed */ export type BuildSwapExactOutTxOptions = CommonOptions & { - customReceiver?: { - receiver: Address; - receiverDatum?: { - hash: string; - datum: string; - }; - }; + customReceiver?: V1AndStableswapCustomReceiver; assetIn: Asset; assetOut: Asset; maximumAmountIn: bigint; @@ -116,13 +119,7 @@ export type BuildSwapExactOutTxOptions = CommonOptions & { * @isLimitOrder Define this order is Limit Order or not */ export type BuildSwapExactInTxOptions = CommonOptions & { - customReceiver?: { - receiver: Address; - receiverDatum?: { - hash: string; - datum: string; - }; - }; + customReceiver?: V1AndStableswapCustomReceiver; assetIn: Asset; amountIn: bigint; assetOut: Asset; @@ -461,37 +458,3 @@ export class Dex { .complete(); } } - -/** - * Return a Output that pay back to @sender and include @datum - * This function is used for @receiver of an order can be a script - * @param lucid - * @param sender - * @param receiver - * @param datum - */ -export function buildUtxoToStoreDatum( - lucid: Lucid, - sender: Address, - receiver: Address, - datum: string -): { - address: Address; - outputData: OutputData; - assets: Assets; -} | null { - const receivePaymentCred = - lucid.utils.getAddressDetails(receiver).paymentCredential; - // If receiver is not a script address, we no need to store this datum On-chain because it's useless - if (!receivePaymentCred || receivePaymentCred.type === "Key") { - return null; - } - - return { - address: sender, - assets: {}, - outputData: { - inline: datum, - }, - }; -} diff --git a/src/utils/tx.internal.ts b/src/utils/tx.internal.ts new file mode 100644 index 0000000..e8456c8 --- /dev/null +++ b/src/utils/tx.internal.ts @@ -0,0 +1,35 @@ +import { Address, Assets, Lucid, OutputData } from "lucid-cardano"; + +/** + * Return a Output that pay back to @sender and include @datum + * This function is used for @receiver of an order can be a script + * @param lucid + * @param sender + * @param receiver + * @param datum + */ +export function buildUtxoToStoreDatum( + lucid: Lucid, + sender: Address, + receiver: Address, + datum: string +): { + address: Address; + outputData: OutputData; + assets: Assets; +} | null { + const receivePaymentCred = + lucid.utils.getAddressDetails(receiver).paymentCredential; + // If receiver is not a script address, we no need to store this datum On-chain because it's useless + if (!receivePaymentCred || receivePaymentCred.type === "Key") { + return null; + } + + return { + address: sender, + assets: {}, + outputData: { + inline: datum, + }, + }; +} \ No newline at end of file From 814929bde07d706cd511ae222d1edcf63e8b328e Mon Sep 17 00:00:00 2001 From: h2physics Date: Wed, 11 Sep 2024 16:17:12 +0700 Subject: [PATCH 3/3] fix format --- src/dex.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dex.ts b/src/dex.ts index dd7a769..54512cf 100644 --- a/src/dex.ts +++ b/src/dex.ts @@ -5,7 +5,6 @@ import { Constr, Data, Lucid, - OutputData, SpendingValidator, TxComplete, UTxO,