@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.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # mesh-transaction
2
+
3
+ Transactions - [meshjs.dev/apis/transaction](https://meshjs.dev/apis/transaction)
4
+
5
+ [meshjs.dev](https://meshjs.dev/)
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@meshsdk/transaction",
3
+ "version": "1.6.0-alpha.11",
4
+ "description": "",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build:mesh": "tsup src/index.ts --format esm,cjs --dts",
16
+ "dev": "tsup src/index.ts --format esm,cjs --watch --dts",
17
+ "lint": "eslint",
18
+ "clean": "rm -rf .turbo && rm -rf dist && rm -rf node_modules",
19
+ "format": "prettier --check . --ignore-path ../../.gitignore",
20
+ "build:docs": "typedoc src/index.ts --json ../../apps/docs/src/data/mesh-transactions.json"
21
+ },
22
+ "devDependencies": {
23
+ "@meshsdk/typescript-config": "*",
24
+ "@types/json-bigint": "^1.0.4",
25
+ "eslint": "^8.57.0",
26
+ "tsup": "^8.0.2",
27
+ "typescript": "^5.3.3"
28
+ },
29
+ "dependencies": {
30
+ "@meshsdk/common": "*",
31
+ "@meshsdk/core-csl": "*",
32
+ "@meshsdk/core-cst": "*",
33
+ "json-bigint": "^1.0.0"
34
+ }
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./mesh-tx-builder";
2
+ export * from "./scripts";
3
+ export * from "./transaction";
4
+ export * from "./utxo-selection";
@@ -0,0 +1,259 @@
1
+ import { MeshTxBuilderCore } from "./tx-builder-core";
2
+ import {
3
+ IMeshTxSerializer,
4
+ IFetcher,
5
+ ISubmitter,
6
+ IEvaluator,
7
+ MeshTxBuilderBody,
8
+ TxIn,
9
+ ScriptTxIn,
10
+ UTxO,
11
+ Protocol,
12
+ } from "@meshsdk/common";
13
+ // import { CardanoSDKSerializer } from "@meshsdk/core-cst";
14
+ import { CSLSerializer } from "@meshsdk/core-csl";
15
+ import { CardanoSDKSerializer } from "@meshsdk/core-cst";
16
+
17
+ export interface MeshTxBuilderOptions {
18
+ fetcher?: IFetcher;
19
+ submitter?: ISubmitter;
20
+ evaluator?: IEvaluator;
21
+ serializer?: IMeshTxSerializer;
22
+ isHydra?: boolean;
23
+ params?: Partial<Protocol>;
24
+ }
25
+
26
+ export class MeshTxBuilder extends MeshTxBuilderCore {
27
+ serializer: IMeshTxSerializer;
28
+ fetcher?: IFetcher;
29
+ submitter?: ISubmitter;
30
+ evaluator?: IEvaluator;
31
+ txHex: string = "";
32
+ private queriedTxHashes: Set<string> = new Set();
33
+ private queriedUTxOs: { [x: string]: UTxO[] } = {};
34
+
35
+ constructor({
36
+ serializer,
37
+ fetcher,
38
+ submitter,
39
+ evaluator,
40
+ params,
41
+ isHydra = false,
42
+ }: MeshTxBuilderOptions = {}) {
43
+ super();
44
+ if (serializer) {
45
+ this.serializer = serializer;
46
+ } else {
47
+ // this.serializer = new CardanoSDKSerializer();
48
+ this.serializer = new CSLSerializer();
49
+ }
50
+ if (fetcher) this.fetcher = fetcher;
51
+ if (submitter) this.submitter = submitter;
52
+ if (evaluator) this.evaluator = evaluator;
53
+ if (params) this.protocolParams(params);
54
+ if (isHydra)
55
+ this.protocolParams({
56
+ minFeeA: 0,
57
+ minFeeB: 0,
58
+ priceMem: 0,
59
+ priceStep: 0,
60
+ collateralPercent: 0,
61
+ coinsPerUtxoSize: 0,
62
+ });
63
+ }
64
+
65
+ /**
66
+ * It builds the transaction and query the blockchain for missing information
67
+ * @param customizedTx The optional customized transaction body
68
+ * @returns The signed transaction in hex ready to submit / signed by client
69
+ */
70
+ complete = async (customizedTx?: MeshTxBuilderBody) => {
71
+ if (customizedTx) {
72
+ this.meshTxBuilderBody = customizedTx;
73
+ } else {
74
+ this.queueAllLastItem();
75
+ }
76
+ this.removeDuplicateInputs();
77
+
78
+ // Checking if all inputs are complete
79
+ const { inputs, collaterals } = this.meshTxBuilderBody;
80
+ const incompleteTxIns = [...inputs, ...collaterals].filter(
81
+ (txIn) => !this.isInputComplete(txIn)
82
+ );
83
+ // Getting all missing utxo information
84
+ await this.queryAllTxInfo(incompleteTxIns);
85
+ // Completing all inputs
86
+ incompleteTxIns.forEach((txIn) => {
87
+ this.completeTxInformation(txIn);
88
+ });
89
+ this.addUtxosFromSelection();
90
+
91
+ let txHex = this.serializer.serializeTxBody(
92
+ this.meshTxBuilderBody,
93
+ this._protocolParams
94
+ );
95
+
96
+ // Evaluating the transaction
97
+ if (this.evaluator) {
98
+ const txEvaluation = await this.evaluator
99
+ .evaluateTx(txHex)
100
+ .catch((error) => {
101
+ throw Error(`Tx evaluation failed: ${error}`);
102
+ });
103
+ this.updateRedeemer(this.meshTxBuilderBody, txEvaluation);
104
+ txHex = this.serializer.serializeTxBody(
105
+ this.meshTxBuilderBody,
106
+ this._protocolParams
107
+ );
108
+ }
109
+ console.log(txHex);
110
+
111
+ this.txHex = txHex;
112
+ return txHex;
113
+ };
114
+
115
+ /**
116
+ * It builds the transaction without dependencies
117
+ * @param customizedTx The optional customized transaction body
118
+ * @returns The signed transaction in hex ready to submit / signed by client
119
+ */
120
+ completeSync = (customizedTx?: MeshTxBuilderBody) => {
121
+ if (customizedTx) {
122
+ this.meshTxBuilderBody = customizedTx;
123
+ } else {
124
+ this.queueAllLastItem();
125
+ }
126
+ return this.serializer.serializeTxBody(
127
+ this.meshTxBuilderBody,
128
+ this._protocolParams
129
+ );
130
+ };
131
+
132
+ /**
133
+ * Complete the signing process
134
+ * @returns The signed transaction in hex
135
+ */
136
+ completeSigning = () => {
137
+ const signedTxHex = this.serializer.addSigningKeys(
138
+ this.txHex,
139
+ this.meshTxBuilderBody.signingKey
140
+ );
141
+ this.txHex = signedTxHex;
142
+ return signedTxHex;
143
+ };
144
+
145
+ /**
146
+ * Submit transactions to the blockchain using the fetcher instance
147
+ * @param txHex The signed transaction in hex
148
+ * @returns
149
+ */
150
+ submitTx = async (txHex: string): Promise<string | undefined> => {
151
+ const txHash = await this.submitter?.submitTx(txHex);
152
+ return txHash;
153
+ };
154
+
155
+ /**
156
+ * Get the UTxO information from the blockchain
157
+ * @param TxHash The TxIn object that contains the txHash and txIndex, while missing amount and address information
158
+ */
159
+ private getUTxOInfo = async (txHash: string): Promise<void> => {
160
+ let utxos: UTxO[] = [];
161
+ if (!this.queriedTxHashes.has(txHash)) {
162
+ this.queriedTxHashes.add(txHash);
163
+ utxos = (await this.fetcher?.fetchUTxOs(txHash)) || [];
164
+ this.queriedUTxOs[txHash] = utxos;
165
+ }
166
+ };
167
+
168
+ private queryAllTxInfo = (incompleteTxIns: TxIn[]) => {
169
+ const queryUTxOPromises: Promise<void>[] = [];
170
+ if (incompleteTxIns.length > 0 && !this.fetcher)
171
+ throw Error(
172
+ "Transaction information is incomplete while no fetcher instance is provided"
173
+ );
174
+ for (let i = 0; i < incompleteTxIns.length; i++) {
175
+ const currentTxIn = incompleteTxIns[i]!;
176
+ if (!this.isInputInfoComplete(currentTxIn)) {
177
+ queryUTxOPromises.push(this.getUTxOInfo(currentTxIn.txIn.txHash));
178
+ }
179
+ if (
180
+ currentTxIn.type === "Script" &&
181
+ currentTxIn.scriptTxIn.scriptSource?.type === "Inline" &&
182
+ !this.isRefScriptInfoComplete(currentTxIn)
183
+ ) {
184
+ queryUTxOPromises.push(
185
+ this.getUTxOInfo(currentTxIn.scriptTxIn.scriptSource.txHash)
186
+ );
187
+ }
188
+ }
189
+ return Promise.all(queryUTxOPromises);
190
+ };
191
+
192
+ private completeTxInformation = (input: TxIn) => {
193
+ // Adding value and address information for inputs if missing
194
+ if (!this.isInputInfoComplete(input)) {
195
+ const utxos: UTxO[] = this.queriedUTxOs[input.txIn.txHash]!;
196
+ const utxo = utxos?.find(
197
+ (utxo) => utxo.input.outputIndex === input.txIn.txIndex
198
+ );
199
+ const amount = utxo?.output.amount;
200
+ const address = utxo?.output.address;
201
+ if (!amount || amount.length === 0)
202
+ throw Error(
203
+ `Couldn't find value information for ${input.txIn.txHash}#${input.txIn.txIndex}`
204
+ );
205
+ input.txIn.amount = amount;
206
+ if (input.type === "PubKey") {
207
+ if (!address || address === "")
208
+ throw Error(
209
+ `Couldn't find address information for ${input.txIn.txHash}#${input.txIn.txIndex}`
210
+ );
211
+ input.txIn.address = address;
212
+ }
213
+ }
214
+ // Adding spendingScriptHash for script inputs' scriptSource if missing
215
+ if (
216
+ input.type === "Script" &&
217
+ input.scriptTxIn.scriptSource?.type == "Inline" &&
218
+ !this.isRefScriptInfoComplete(input)
219
+ ) {
220
+ const scriptSource = input.scriptTxIn.scriptSource;
221
+ const refUtxos = this.queriedUTxOs[scriptSource.txHash]!;
222
+ const scriptRefUtxo = refUtxos.find(
223
+ (utxo) => utxo.input.outputIndex === scriptSource.txIndex
224
+ );
225
+ if (!scriptRefUtxo)
226
+ throw Error(
227
+ `Couldn't find script reference utxo for ${scriptSource.txHash}#${scriptSource.txIndex}`
228
+ );
229
+ scriptSource.scriptHash = scriptRefUtxo?.output.scriptHash;
230
+ // TODO: Calculate script size
231
+ }
232
+ };
233
+
234
+ private isInputComplete = (txIn: TxIn): boolean => {
235
+ if (txIn.type === "PubKey") return this.isInputInfoComplete(txIn);
236
+ if (txIn.type === "Script") {
237
+ return (
238
+ this.isInputInfoComplete(txIn) && this.isRefScriptInfoComplete(txIn)
239
+ );
240
+ }
241
+ return true;
242
+ };
243
+
244
+ private isInputInfoComplete = (txIn: TxIn): boolean => {
245
+ const { amount, address } = txIn.txIn;
246
+ if (txIn.type === "PubKey" && (!amount || !address)) return false;
247
+ if (txIn.type === "Script") {
248
+ if (!amount) return false;
249
+ }
250
+ return true;
251
+ };
252
+
253
+ private isRefScriptInfoComplete = (scriptTxIn: ScriptTxIn): boolean => {
254
+ const { scriptSource } = scriptTxIn.scriptTxIn;
255
+ if (scriptSource?.type === "Inline" && !scriptSource?.scriptHash)
256
+ return false;
257
+ return true;
258
+ };
259
+ }