@meshsdk/transaction 1.6.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,81 @@
1
+ import { Asset, Data, UTxO } from "@meshsdk/common";
2
+
3
+ export interface TransactionV2 {
4
+ sendAssets(
5
+ receiver: string,
6
+ assets: Asset[],
7
+ datum?: Data,
8
+ inlineScript?: string,
9
+ isInline?: boolean,
10
+ ): this;
11
+ spendUtxo(utxo: UTxO, redeemer?: Data): this;
12
+ }
13
+
14
+ // export type TxBuilder = {
15
+ // readFrom: (utxos: UTxO[]) => TxBuilder;
16
+ // collectFrom: (utxos: UTxO[], redeemer?: string | undefined) => TxBuilder;
17
+ // pay: {
18
+ // ToAddress: (address: string, assets: Assets) => TxBuilder;
19
+ // ToAddressWithData: (
20
+ // address: string,
21
+ // outputDatum: OutputDatum,
22
+ // assets?: Assets | undefined,
23
+ // scriptRef?: Script | undefined
24
+ // ) => TxBuilder;
25
+ // ToContract: (
26
+ // address: string,
27
+ // outputDatum: OutputDatum,
28
+ // assets?: Assets | undefined,
29
+ // scriptRef?: Script | undefined
30
+ // ) => TxBuilder;
31
+ // };
32
+ // addSigner: (address: Address) => TxBuilder;
33
+ // registerStake: (rewardAddress: RewardAddress) => TxBuilder;
34
+ // deRegisterStake: (
35
+ // rewardAddress: RewardAddress,
36
+ // redeemer?: string
37
+ // ) => TxBuilder;
38
+ // withdraw: (
39
+ // rewardAddress: RewardAddress,
40
+ // amount: Lovelace,
41
+ // redeemer?: string
42
+ // ) => TxBuilder;
43
+ // mintAssets: (assets: Assets, redeemer?: string | undefined) => TxBuilder;
44
+ // validFrom: (unixTime: number) => TxBuilder;
45
+ // validTo: (unixTime: number) => TxBuilder;
46
+ // delegateTo: (
47
+ // rewardAddress: RewardAddress,
48
+ // poolId: PoolId,
49
+ // redeemer?: Redeemer
50
+ // ) => TxBuilder;
51
+ // attachMetadata: (
52
+ // label: Label,
53
+ // metadata: Metadata.TransactionMetadata
54
+ // ) => TxBuilder;
55
+ // attach: {
56
+ // Script: (script: Script) => TxBuilder;
57
+ // SpendingValidator: (spendingValidator: Script) => TxBuilder;
58
+ // MintingPolicy: (mintingPolicy: Script) => TxBuilder;
59
+ // CertificateValidator: (certValidator: Script) => TxBuilder;
60
+ // WithdrawalValidator: (withdrawalValidator: Script) => TxBuilder;
61
+ // };
62
+ // complete: (
63
+ // options?: CompleteTxBuilder.CompleteOptions
64
+ // ) => Promise<TxSignBuilder.TxSignBuilder>;
65
+ // completeProgram: (
66
+ // options?: CompleteTxBuilder.CompleteOptions
67
+ // ) => Effect.Effect<TxSignBuilder.TxSignBuilder, TransactionError>;
68
+ // completeSafe: (
69
+ // options?: CompleteTxBuilder.CompleteOptions
70
+ // ) => Promise<Either<TxSignBuilder.TxSignBuilder, TransactionError>>;
71
+ // chainProgram: () => Effect.Effect<
72
+ // [UTxO[], UTxO[], TxSignBuilder.TxSignBuilder],
73
+ // TransactionError,
74
+ // never
75
+ // >;
76
+ // chain: () => Promise<[UTxO[], UTxO[], TxSignBuilder.TxSignBuilder]>;
77
+ // chainSafe: () => Promise<
78
+ // Either<[UTxO[], UTxO[], TxSignBuilder.TxSignBuilder], TransactionError>
79
+ // >;
80
+ // config: () => TxBuilderConfig;
81
+ // };
@@ -0,0 +1,113 @@
1
+ import { BigNum } from "@meshsdk/common";
2
+ import { resolveTxFees } from "@meshsdk/common";
3
+ import type { Protocol, Quantity, Unit, UTxO } from "@meshsdk/common";
4
+
5
+ export const enoughValueHasBeenSelected = (
6
+ selection: UTxO[],
7
+ assets: Map<Unit, Quantity>
8
+ ): boolean => {
9
+ return Array.from(assets, (asset) => ({
10
+ unit: asset[0],
11
+ quantity: BigNum.new(asset[1]),
12
+ })).every((asset) => {
13
+ return (
14
+ selection
15
+ .filter((utxo) => {
16
+ return (
17
+ utxo.output.amount.find((a) => a.unit === asset.unit) !== undefined
18
+ );
19
+ })
20
+ .reduce((selectedQuantity, utxo) => {
21
+ const utxoQuantity = utxo.output.amount.reduce(
22
+ (quantity, a) =>
23
+ quantity.checkedAdd(
24
+ BigNum.new(asset.unit === a.unit ? a.quantity : "0")
25
+ ),
26
+ BigNum.new("0")
27
+ );
28
+
29
+ return selectedQuantity.checkedAdd(utxoQuantity);
30
+ }, BigNum.new("0"))
31
+ .lessThan(asset.quantity) === false
32
+ );
33
+ });
34
+ };
35
+
36
+ export const largestLovelaceQuantity = (utxoA: UTxO, utxoB: UTxO): number => {
37
+ const aLovelaceQuantity = BigNum.new(
38
+ utxoA.output.amount.find((asset) => asset.unit === "lovelace")?.quantity ??
39
+ "0"
40
+ );
41
+
42
+ const bLovelaceQuantity = BigNum.new(
43
+ utxoB.output.amount.find((asset) => asset.unit === "lovelace")?.quantity ??
44
+ "0"
45
+ );
46
+
47
+ return bLovelaceQuantity.compare(aLovelaceQuantity);
48
+ };
49
+
50
+ export const maxTxFees = (parameters: Protocol) => {
51
+ const { maxTxSize, minFeeA, minFeeB } = parameters;
52
+
53
+ return BigNum.new(resolveTxFees(maxTxSize, minFeeA, minFeeB));
54
+ };
55
+
56
+ export const multiAssetUTxO = (utxo: UTxO): boolean =>
57
+ utxo.output.amount.length > 1;
58
+
59
+ export const selectedLovelaceQuantity = (multiAsset: UTxO[]) => {
60
+ return multiAsset.reduce((sum, utxo) => {
61
+ const lovelace =
62
+ utxo.output.amount.find((asset) => asset.unit === "lovelace")?.quantity ??
63
+ "0";
64
+
65
+ return sum.checkedAdd(BigNum.new(lovelace));
66
+ }, BigNum.new("0"));
67
+ };
68
+
69
+ export const remainingLovelace = (
70
+ quantity: Quantity,
71
+ initialUTxOSet: UTxO[]
72
+ ) => {
73
+ const sortedUTxOs = initialUTxOSet.sort(largestLovelaceQuantity);
74
+
75
+ const requestedOutputSet = new Map<Unit, Quantity>([["lovelace", quantity]]);
76
+
77
+ const selection = selectValue(sortedUTxOs, requestedOutputSet);
78
+
79
+ return selection;
80
+ };
81
+
82
+ export const selectValue = (
83
+ inputUTxO: UTxO[],
84
+ outputSet: Map<Unit, Quantity>,
85
+ selection: UTxO[] = []
86
+ ): UTxO[] => {
87
+ if (
88
+ inputUTxO.length === 0 ||
89
+ enoughValueHasBeenSelected(selection, outputSet)
90
+ ) {
91
+ return selection;
92
+ }
93
+
94
+ if (valueCanBeSelected(inputUTxO[0]!, outputSet)) {
95
+ return selectValue(inputUTxO.slice(1), outputSet, [
96
+ ...selection,
97
+ inputUTxO[0]!,
98
+ ]);
99
+ }
100
+
101
+ return selectValue(inputUTxO.slice(1), outputSet, selection);
102
+ };
103
+
104
+ export const valueCanBeSelected = (
105
+ utxo: UTxO,
106
+ assets: Map<Unit, Quantity>
107
+ ): boolean => {
108
+ return Array.from(assets.keys()).some((unit) => {
109
+ return (
110
+ utxo.output.amount.find((asset) => asset.unit === unit) !== undefined
111
+ );
112
+ });
113
+ };
@@ -0,0 +1,137 @@
1
+ import type { Quantity, UTxO, Unit } from "@meshsdk/common";
2
+
3
+ export const experimentalSelectUtxos = (
4
+ requiredAssets: Map<Unit, Quantity>,
5
+ inputs: UTxO[],
6
+ threshold: Quantity
7
+ ): UTxO[] => {
8
+ const totalRequiredAssets = new Map<Unit, Quantity>(requiredAssets);
9
+ totalRequiredAssets.set(
10
+ "lovelace",
11
+ String(Number(totalRequiredAssets.get("lovelace") || 0) + Number(threshold))
12
+ );
13
+ const utxoMap = new Map<number, UTxO>();
14
+ for (let i = 0; i < inputs.length; i++) {
15
+ utxoMap.set(i, inputs[i]!);
16
+ }
17
+ const selectedInputs = new Set<number>();
18
+ const onlyLovelace = new Set<number>();
19
+ const singletons = new Set<number>();
20
+ const pairs = new Set<number>();
21
+ const rest = new Set<number>();
22
+ for (let i = 0; i < inputs.length; i++) {
23
+ switch (inputs[i]!.output.amount.length) {
24
+ case 1: {
25
+ onlyLovelace.add(i);
26
+ break;
27
+ }
28
+ case 2: {
29
+ singletons.add(i);
30
+ break;
31
+ }
32
+ case 3: {
33
+ pairs.add(i);
34
+ break;
35
+ }
36
+ default: {
37
+ rest.add(i);
38
+ break;
39
+ }
40
+ }
41
+ }
42
+
43
+ const addUtxoWithAssetAmount = (
44
+ inputIndex: number,
45
+ assetUnit: string,
46
+ set: Set<number>
47
+ ) => {
48
+ const utxo = utxoMap.get(inputIndex);
49
+ if (!utxo) return;
50
+
51
+ const amount = getAssetAmount(utxo, assetUnit);
52
+ if (Number(amount) > 0) {
53
+ selectedInputs.add(inputIndex);
54
+ set.delete(inputIndex);
55
+ for (const asset of utxo.output.amount) {
56
+ totalRequiredAssets.set(
57
+ asset.unit,
58
+ String(
59
+ Number(totalRequiredAssets.get(asset.unit)) - Number(asset.quantity)
60
+ )
61
+ );
62
+ }
63
+ }
64
+ };
65
+
66
+ for (const assetUnit of totalRequiredAssets.keys()) {
67
+ if (assetUnit == "lovelace") continue;
68
+ for (const inputIndex of singletons) {
69
+ const assetRequired = totalRequiredAssets.get(assetUnit);
70
+ if (!assetRequired || Number(assetRequired) <= 0) break;
71
+ addUtxoWithAssetAmount(inputIndex, assetUnit, singletons);
72
+ }
73
+
74
+ for (const inputIndex of pairs) {
75
+ const assetRequired = totalRequiredAssets.get(assetUnit);
76
+ if (!assetRequired || Number(assetRequired) <= 0) break;
77
+ addUtxoWithAssetAmount(inputIndex, assetUnit, pairs);
78
+ }
79
+
80
+ for (const inputIndex of rest) {
81
+ const assetRequired = totalRequiredAssets.get(assetUnit);
82
+ if (!assetRequired || Number(assetRequired) <= 0) break;
83
+ addUtxoWithAssetAmount(inputIndex, assetUnit, rest);
84
+ }
85
+ }
86
+
87
+ for (const inputIndex of onlyLovelace) {
88
+ const assetRequired = totalRequiredAssets.get("lovelace");
89
+ if (!assetRequired || Number(assetRequired) <= 0) break;
90
+ addUtxoWithAssetAmount(inputIndex, "lovelace", onlyLovelace);
91
+ }
92
+
93
+ for (const inputIndex of singletons) {
94
+ const assetRequired = totalRequiredAssets.get("lovelace");
95
+ if (!assetRequired || Number(assetRequired) <= 0) break;
96
+ addUtxoWithAssetAmount(inputIndex, "lovelace", singletons);
97
+ }
98
+
99
+ for (const inputIndex of pairs) {
100
+ const assetRequired = totalRequiredAssets.get("lovelace");
101
+ if (!assetRequired || Number(assetRequired) <= 0) break;
102
+ addUtxoWithAssetAmount(inputIndex, "lovelace", pairs);
103
+ }
104
+
105
+ for (const inputIndex of rest) {
106
+ const assetRequired = totalRequiredAssets.get("lovelace");
107
+ if (!assetRequired || Number(assetRequired) <= 0) break;
108
+ addUtxoWithAssetAmount(inputIndex, "lovelace", rest);
109
+ }
110
+
111
+ for (const assetUnit of totalRequiredAssets.keys()) {
112
+ if (Number(totalRequiredAssets.get(assetUnit)) > 0) {
113
+ console.log("Insufficient funds for", assetUnit);
114
+ console.log(
115
+ "Remaining quantity",
116
+ Number(totalRequiredAssets.get(assetUnit))
117
+ );
118
+ return [];
119
+ }
120
+ }
121
+
122
+ const selectedUtxos: UTxO[] = [];
123
+ for (const inputIndex of selectedInputs) {
124
+ const utxo = utxoMap.get(inputIndex);
125
+ if (utxo) {
126
+ selectedUtxos.push(utxo);
127
+ }
128
+ }
129
+ return selectedUtxos;
130
+ };
131
+
132
+ const getAssetAmount = (utxo: UTxO, assetUnit: string): string => {
133
+ for (const utxoAsset of utxo.output.amount) {
134
+ if (utxoAsset.unit == assetUnit) return utxoAsset.quantity;
135
+ }
136
+ return "0";
137
+ };
@@ -0,0 +1,69 @@
1
+ import { Unit, Quantity, UTxO } from "@meshsdk/common";
2
+ import { keepRelevant } from "./keepRelevant";
3
+ import { largestFirst } from "./largestFirst";
4
+ import { largestFirstMultiAsset } from "./largestFirstMultiAsset";
5
+ import { experimentalSelectUtxos } from "./experimental";
6
+
7
+ export {
8
+ keepRelevant,
9
+ largestFirst,
10
+ largestFirstMultiAsset,
11
+ experimentalSelectUtxos,
12
+ };
13
+
14
+ /**
15
+ * All UTxO selection algorithms follows below's interface
16
+ *
17
+ * Supported algorithms:
18
+ * - largestFirst - CIP2 suggested algorithm
19
+ * - largestFirstMultiAsset - CIP2 suggested algorithm
20
+ * - keepRelevant - CIP2 suggested algorithm
21
+ * - experimental - The always evolving algorithm according to the latest research
22
+ *
23
+ * @param requestedOutputSet
24
+ * @param initialUTxOSet
25
+ * @returns
26
+ */
27
+ export class UtxoSelection {
28
+ constructor() {}
29
+
30
+ largestFirst(
31
+ requiredAssets: Map<Unit, Quantity>,
32
+ inputs: UTxO[],
33
+ threshold = "5000000"
34
+ ) {
35
+ const lovelaceAmount = requiredAssets.get("lovelace") ?? "0";
36
+ const requiredAssetWithThreshold =
37
+ BigInt(lovelaceAmount) + BigInt(threshold);
38
+ return largestFirst(requiredAssetWithThreshold.toString(), inputs, false);
39
+ }
40
+
41
+ keepRelevant(
42
+ requiredAssets: Map<Unit, Quantity>,
43
+ inputs: UTxO[],
44
+ threshold = "5000000"
45
+ ) {
46
+ return keepRelevant(requiredAssets, inputs, threshold);
47
+ }
48
+
49
+ largestFirstMultiAsset(
50
+ requiredAssets: Map<Unit, Quantity>,
51
+ inputs: UTxO[],
52
+ threshold: Quantity
53
+ ) {
54
+ const lovelaceAmount = requiredAssets.get("lovelace") ?? "0";
55
+ requiredAssets.set(
56
+ "lovelace",
57
+ (BigInt(lovelaceAmount) + BigInt(threshold)).toString()
58
+ );
59
+ return largestFirstMultiAsset(requiredAssets, inputs, false);
60
+ }
61
+
62
+ experimental(
63
+ requiredAssets: Map<Unit, Quantity>,
64
+ inputs: UTxO[],
65
+ threshold: Quantity
66
+ ) {
67
+ return experimentalSelectUtxos(requiredAssets, inputs, threshold);
68
+ }
69
+ }
@@ -0,0 +1,35 @@
1
+ import { BigNum, Quantity, UTxO, Unit } from "@meshsdk/common";
2
+ import { remainingLovelace, selectedLovelaceQuantity } from "./common";
3
+
4
+ export const keepRelevant = (
5
+ requiredAssets: Map<Unit, Quantity>,
6
+ inputs: UTxO[],
7
+ threshold = "5000000"
8
+ ) => {
9
+ const requestedLovelace = BigNum.new(
10
+ requiredAssets.get("lovelace") ?? "0"
11
+ ).checkedAdd(BigNum.new(threshold));
12
+
13
+ const multiAsset = inputs.filter((utxo) =>
14
+ utxo.output.amount
15
+ .filter((asset) => asset.unit !== "lovelace")
16
+ .some((asset) => requiredAssets.has(asset.unit))
17
+ );
18
+
19
+ const selectedLovelace = selectedLovelaceQuantity(multiAsset);
20
+
21
+ const lovelace = selectedLovelace.lessThan(requestedLovelace)
22
+ ? remainingLovelace(
23
+ requestedLovelace.clampedSub(selectedLovelace).toString(),
24
+ inputs.filter((input) => {
25
+ return !multiAsset.some(
26
+ (selectedUtxo) =>
27
+ selectedUtxo.input.txHash === input.input.txHash &&
28
+ selectedUtxo.input.outputIndex === input.input.outputIndex
29
+ );
30
+ })
31
+ )
32
+ : [];
33
+
34
+ return [...lovelace, ...multiAsset];
35
+ };
@@ -0,0 +1,31 @@
1
+ import {
2
+ Quantity,
3
+ UTxO,
4
+ DEFAULT_PROTOCOL_PARAMETERS,
5
+ BigNum,
6
+ resolveTxFees,
7
+ Unit,
8
+ } from "@meshsdk/common";
9
+ import { multiAssetUTxO, largestLovelaceQuantity, selectValue } from "./common";
10
+
11
+ export const largestFirst = (
12
+ lovelace: Quantity,
13
+ initialUTxOSet: UTxO[],
14
+ includeTxFees = false,
15
+ { maxTxSize, minFeeA, minFeeB } = DEFAULT_PROTOCOL_PARAMETERS
16
+ ): UTxO[] => {
17
+ const sortedUTxOs = initialUTxOSet
18
+ .filter((utxo) => multiAssetUTxO(utxo) === false)
19
+ .sort(largestLovelaceQuantity);
20
+
21
+ const maxTxFees = BigNum.new(resolveTxFees(maxTxSize, minFeeA, minFeeB));
22
+
23
+ const quantity = includeTxFees
24
+ ? BigNum.new(lovelace).checkedAdd(maxTxFees).toString()
25
+ : lovelace;
26
+
27
+ const requestedOutputSet = new Map<Unit, Quantity>([["lovelace", quantity]]);
28
+
29
+ const selection = selectValue(sortedUTxOs, requestedOutputSet);
30
+ return selection;
31
+ };
@@ -0,0 +1,37 @@
1
+ import {
2
+ BigNum,
3
+ DEFAULT_PROTOCOL_PARAMETERS,
4
+ Quantity,
5
+ UTxO,
6
+ Unit,
7
+ } from "@meshsdk/common";
8
+ import {
9
+ multiAssetUTxO,
10
+ largestLovelaceQuantity,
11
+ maxTxFees,
12
+ selectValue,
13
+ } from "./common";
14
+
15
+ export const largestFirstMultiAsset = (
16
+ requestedOutputSet: Map<Unit, Quantity>,
17
+ initialUTxOSet: UTxO[],
18
+ includeTxFees = false,
19
+ parameters = DEFAULT_PROTOCOL_PARAMETERS
20
+ ): UTxO[] => {
21
+ const sortedMultiAssetUTxOs = initialUTxOSet
22
+ .filter(multiAssetUTxO)
23
+ .sort(largestLovelaceQuantity);
24
+
25
+ const txFees = maxTxFees(parameters);
26
+ const lovelace = requestedOutputSet.get("lovelace") ?? "0";
27
+
28
+ const quantity = includeTxFees
29
+ ? BigNum.new(lovelace).checkedAdd(txFees).toString()
30
+ : lovelace;
31
+
32
+ requestedOutputSet.set("lovelace", quantity);
33
+
34
+ const selection = selectValue(sortedMultiAssetUTxOs, requestedOutputSet);
35
+
36
+ return selection;
37
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "@meshsdk/typescript-config/base.json",
3
+ "include": ["src/**/*"],
4
+ "exclude": ["dist", "node_modules"]
5
+ }
package/types/index.ts ADDED
File without changes