@odatano/core 0.3.1
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/LICENSE +201 -0
- package/README.md +212 -0
- package/cds-plugin.js +5 -0
- package/config/preview/cardano-node/alonzo-genesis.json +196 -0
- package/config/preview/cardano-node/byron-genesis.json +117 -0
- package/config/preview/cardano-node/config.json +118 -0
- package/config/preview/cardano-node/conway-genesis.json +297 -0
- package/config/preview/cardano-node/shelley-genesis.json +68 -0
- package/config/preview/cardano-node/topology.json +19 -0
- package/db/schema.cds +1318 -0
- package/package.json +125 -0
- package/src/index.d.ts.map +1 -0
- package/src/index.js +96 -0
- package/src/index.js.map +1 -0
- package/src/plugin.d.ts.map +1 -0
- package/src/plugin.js +92 -0
- package/src/plugin.js.map +1 -0
- package/srv/blockchain/backends/blockfrost-backend.d.ts.map +1 -0
- package/srv/blockchain/backends/blockfrost-backend.js +398 -0
- package/srv/blockchain/backends/blockfrost-backend.js.map +1 -0
- package/srv/blockchain/backends/cardano-backend.d.ts.map +1 -0
- package/srv/blockchain/backends/cardano-backend.js +12 -0
- package/srv/blockchain/backends/cardano-backend.js.map +1 -0
- package/srv/blockchain/backends/koios-backend.d.ts.map +1 -0
- package/srv/blockchain/backends/koios-backend.js +537 -0
- package/srv/blockchain/backends/koios-backend.js.map +1 -0
- package/srv/blockchain/backends/ogmios-backend.d.ts.map +1 -0
- package/srv/blockchain/backends/ogmios-backend.js +516 -0
- package/srv/blockchain/backends/ogmios-backend.js.map +1 -0
- package/srv/blockchain/cardano-client.d.ts.map +1 -0
- package/srv/blockchain/cardano-client.js +377 -0
- package/srv/blockchain/cardano-client.js.map +1 -0
- package/srv/blockchain/cardano-indexer.d.ts.map +1 -0
- package/srv/blockchain/cardano-indexer.js +542 -0
- package/srv/blockchain/cardano-indexer.js.map +1 -0
- package/srv/blockchain/cardano-tx-builder.d.ts.map +1 -0
- package/srv/blockchain/cardano-tx-builder.js +232 -0
- package/srv/blockchain/cardano-tx-builder.js.map +1 -0
- package/srv/blockchain/circuit-breaker.d.ts.map +1 -0
- package/srv/blockchain/circuit-breaker.js +110 -0
- package/srv/blockchain/circuit-breaker.js.map +1 -0
- package/srv/blockchain/signing/external-signer.d.ts.map +1 -0
- package/srv/blockchain/signing/external-signer.js +302 -0
- package/srv/blockchain/signing/external-signer.js.map +1 -0
- package/srv/blockchain/signing/signature-verifier.d.ts.map +1 -0
- package/srv/blockchain/signing/signature-verifier.js +249 -0
- package/srv/blockchain/signing/signature-verifier.js.map +1 -0
- package/srv/blockchain/transaction-building/buildooor-tx.d.ts.map +1 -0
- package/srv/blockchain/transaction-building/buildooor-tx.js +636 -0
- package/srv/blockchain/transaction-building/buildooor-tx.js.map +1 -0
- package/srv/blockchain/transaction-building/cardano-tx.d.ts.map +1 -0
- package/srv/blockchain/transaction-building/cardano-tx.js +3 -0
- package/srv/blockchain/transaction-building/cardano-tx.js.map +1 -0
- package/srv/blockchain/transaction-building/csl-tx.d.ts.map +1 -0
- package/srv/blockchain/transaction-building/csl-tx.js +766 -0
- package/srv/blockchain/transaction-building/csl-tx.js.map +1 -0
- package/srv/blockchain/transaction-building/tx-builder-registry.d.ts.map +1 -0
- package/srv/blockchain/transaction-building/tx-builder-registry.js +67 -0
- package/srv/blockchain/transaction-building/tx-builder-registry.js.map +1 -0
- package/srv/cardano-service.cds +179 -0
- package/srv/cardano-service.d.ts.map +1 -0
- package/srv/cardano-service.js +227 -0
- package/srv/cardano-service.js.map +1 -0
- package/srv/cardano-tx-service.cds +298 -0
- package/srv/cardano-tx-service.d.ts.map +1 -0
- package/srv/cardano-tx-service.js +646 -0
- package/srv/cardano-tx-service.js.map +1 -0
- package/srv/cardano-ui.cds +2949 -0
- package/srv/server.d.ts.map +1 -0
- package/srv/server.js +212 -0
- package/srv/server.js.map +1 -0
- package/srv/utils/backend-request-handler.d.ts.map +1 -0
- package/srv/utils/backend-request-handler.js +47 -0
- package/srv/utils/backend-request-handler.js.map +1 -0
- package/srv/utils/const.d.ts.map +1 -0
- package/srv/utils/const.js +86 -0
- package/srv/utils/const.js.map +1 -0
- package/srv/utils/error-codes.d.ts.map +1 -0
- package/srv/utils/error-codes.js +49 -0
- package/srv/utils/error-codes.js.map +1 -0
- package/srv/utils/errors.d.ts.map +1 -0
- package/srv/utils/errors.js +389 -0
- package/srv/utils/errors.js.map +1 -0
- package/srv/utils/mappers.d.ts.map +1 -0
- package/srv/utils/mappers.js +723 -0
- package/srv/utils/mappers.js.map +1 -0
- package/srv/utils/signing-helper.d.ts.map +1 -0
- package/srv/utils/signing-helper.js +128 -0
- package/srv/utils/signing-helper.js.map +1 -0
- package/srv/utils/tx-build-helper.d.ts.map +1 -0
- package/srv/utils/tx-build-helper.js +135 -0
- package/srv/utils/tx-build-helper.js.map +1 -0
- package/srv/utils/types.d.ts.map +1 -0
- package/srv/utils/types.js +36 -0
- package/srv/utils/types.js.map +1 -0
- package/srv/utils/validators.d.ts.map +1 -0
- package/srv/utils/validators.js +382 -0
- package/srv/utils/validators.js.map +1 -0
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const cds_1 = __importDefault(require("@sap/cds"));
|
|
7
|
+
const backend_request_handler_1 = require("./utils/backend-request-handler");
|
|
8
|
+
const errors_1 = require("./utils/errors");
|
|
9
|
+
const validators_1 = require("./utils/validators");
|
|
10
|
+
const tx_build_helper_1 = require("./utils/tx-build-helper");
|
|
11
|
+
const server_1 = require("./server");
|
|
12
|
+
const external_signer_1 = require("./blockchain/signing/external-signer");
|
|
13
|
+
const signing_helper_1 = require("./utils/signing-helper");
|
|
14
|
+
const { SELECT, UPDATE } = cds_1.default.ql;
|
|
15
|
+
const logger = cds_1.default.log('CardanoTxService');
|
|
16
|
+
/**
|
|
17
|
+
* Check if a signing request has expired and update its status.
|
|
18
|
+
* @returns true if expired, false otherwise
|
|
19
|
+
*/
|
|
20
|
+
async function checkAndExpireSigningRequest(db, signingRequest, SigningRequests) {
|
|
21
|
+
if (new Date(signingRequest.expiresAt) < new Date()) {
|
|
22
|
+
await db.run(UPDATE.entity(SigningRequests).set({ status: 'expired' }).where({ id: signingRequest.id }));
|
|
23
|
+
signingRequest.status = 'expired';
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Cardano Transaction Service Implementation
|
|
30
|
+
* Handles transaction building and submission operations & some additional data queries.
|
|
31
|
+
*/
|
|
32
|
+
module.exports = (srv) => {
|
|
33
|
+
logger.info('[CardanoTxService] Module loaded - registering handlers');
|
|
34
|
+
const { TransactionBuilds, TransactionBuildInputs, TransactionBuildOutputs, TransactionSubmissions, TransactionSubmissionErrors, SigningRequests, SignatureVerifications, AddressSigningRequests, AddressTransactionBuilds } = require('#cds-models/CardanoTransactionService');
|
|
35
|
+
/**
|
|
36
|
+
* READ handler for TransactionBuilds entity
|
|
37
|
+
* @param req - The incoming request data
|
|
38
|
+
* @returns {TransactionBuilds} The transaction builds fitting the request query
|
|
39
|
+
*/
|
|
40
|
+
srv.on('READ', TransactionBuilds, async (req) => {
|
|
41
|
+
logger.debug('TransactionBuilds READ handler called');
|
|
42
|
+
return (0, backend_request_handler_1.handleRequest)(req, (db) => db.run(req.query));
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* READ handler for TransactionBuildInputs entity
|
|
46
|
+
* @param req - The incoming request data
|
|
47
|
+
* @returns {TransactionBuildInputs} The transaction build inputs fitting the request query
|
|
48
|
+
*/
|
|
49
|
+
srv.on('READ', TransactionBuildInputs, async (req) => {
|
|
50
|
+
logger.debug('TransactionBuildInputs READ handler called');
|
|
51
|
+
return (0, backend_request_handler_1.handleRequest)(req, (db) => db.run(req.query));
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* READ handler for TransactionBuildOutputs entity
|
|
55
|
+
* @param req - The incoming request data
|
|
56
|
+
* @returns {TransactionBuildOutputs} The transaction build outputs fitting the request query
|
|
57
|
+
*/
|
|
58
|
+
srv.on('READ', TransactionBuildOutputs, async (req) => {
|
|
59
|
+
logger.debug('TransactionBuildOutputs READ handler called');
|
|
60
|
+
return (0, backend_request_handler_1.handleRequest)(req, (db) => db.run(req.query));
|
|
61
|
+
});
|
|
62
|
+
/**
|
|
63
|
+
* READ handler for TransactionSubmissions entity
|
|
64
|
+
* @param req - The incoming request data
|
|
65
|
+
* @returns {TransactionSubmissions} The transaction submissions fitting the request query
|
|
66
|
+
*/
|
|
67
|
+
srv.on('READ', TransactionSubmissions, async (req) => {
|
|
68
|
+
logger.debug('TransactionSubmissions READ handler called');
|
|
69
|
+
return (0, backend_request_handler_1.handleRequest)(req, (db) => db.run(req.query));
|
|
70
|
+
});
|
|
71
|
+
/**
|
|
72
|
+
* READ handler for TransactionSubmissionErrors entity
|
|
73
|
+
* @param req - The incoming request data
|
|
74
|
+
* @returns {TransactionSubmissionErrors} The transaction submission errors fitting the request query
|
|
75
|
+
*/
|
|
76
|
+
srv.on('READ', TransactionSubmissionErrors, async (req) => {
|
|
77
|
+
logger.debug('TransactionSubmissionErrors READ handler called');
|
|
78
|
+
return (0, backend_request_handler_1.handleRequest)(req, (db) => db.run(req.query));
|
|
79
|
+
});
|
|
80
|
+
/**
|
|
81
|
+
* Build a simple ADA-only transaction
|
|
82
|
+
* @param req - CDS request object (with senderAddress, recipientAddress, lovelaceAmount, changeAddress)
|
|
83
|
+
* @returns {TransactionBuild} Transaction build details
|
|
84
|
+
*/
|
|
85
|
+
srv.on('BuildSimpleAdaTransaction', async (req) => {
|
|
86
|
+
const { senderAddress, recipientAddress, lovelaceAmount, outputDatumJson } = req.data;
|
|
87
|
+
// validate inputs
|
|
88
|
+
const errors = (0, validators_1.validateTransactionInputs)({ senderAddress, recipientAddress, lovelaceAmount }, ['senderAddress', 'recipientAddress', 'lovelaceAmount']);
|
|
89
|
+
(0, errors_1.throwIfValidationErrors)(req, 'BuildSimpleAdaTransaction', errors);
|
|
90
|
+
// parse optional output datum
|
|
91
|
+
const cleanData = { ...req.data };
|
|
92
|
+
if (outputDatumJson) {
|
|
93
|
+
try {
|
|
94
|
+
cleanData.outputDatum = JSON.parse(outputDatumJson);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return req.reject(400, 'Invalid outputDatumJson: must be valid JSON');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// handle the request / building the transaction / indexing the build result / returning build details
|
|
101
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
102
|
+
logger.info({ senderAddress, recipientAddress, lovelaceAmount, hasDatum: !!outputDatumJson }, 'Building simple ADA transaction');
|
|
103
|
+
return await (0, server_1.getCardanoIndexer)().indexSimpleBuildResult(db, cleanData);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
/**
|
|
107
|
+
* Build a transaction with metadata
|
|
108
|
+
* @param req - CDS request object (with senderAddress, recipientAddress, lovelaceAmount, metadataJson, changeAddress)
|
|
109
|
+
* @returns {TransactionBuild} Transaction build details
|
|
110
|
+
*/
|
|
111
|
+
srv.on('BuildTransactionWithMetadata', async (req) => {
|
|
112
|
+
const { senderAddress, recipientAddress, lovelaceAmount, metadataJson } = req.data;
|
|
113
|
+
// validate inputs (includes JSON parsing validation)
|
|
114
|
+
const errors = (0, validators_1.validateTransactionInputs)({ senderAddress, recipientAddress, lovelaceAmount, metadataJson }, ['senderAddress', 'recipientAddress', 'lovelaceAmount', 'metadataJson']);
|
|
115
|
+
(0, errors_1.throwIfValidationErrors)(req, 'BuildTransactionWithMetadata', errors);
|
|
116
|
+
// Parse metadataJson (already validated as valid JSON)
|
|
117
|
+
const parsedMetadata = JSON.parse(metadataJson);
|
|
118
|
+
// handle the request / building the transaction / indexing the build result / returning build details
|
|
119
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
120
|
+
logger.info({ senderAddress, recipientAddress, lovelaceAmount, metadataJson: parsedMetadata }, 'Building transaction with metadata');
|
|
121
|
+
return await (0, server_1.getCardanoIndexer)().indexMetadataBuildResult(db, { ...req.data, metadataJson: parsedMetadata });
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
/**
|
|
125
|
+
* Build a multi-asset transaction
|
|
126
|
+
* @param req - CDS request object (with senderAddress, recipientAddress, lovelaceAmount, assetsJson, changeAddress)
|
|
127
|
+
* @returns {TransactionBuild} Transaction build details
|
|
128
|
+
*/
|
|
129
|
+
srv.on('BuildMultiAssetTransaction', async (req) => {
|
|
130
|
+
const { senderAddress, recipientAddress, lovelaceAmount, assetsJson } = req.data;
|
|
131
|
+
// validate inputs (includes JSON parsing validation)
|
|
132
|
+
const errors = (0, validators_1.validateTransactionInputs)({ senderAddress, recipientAddress, lovelaceAmount, assetsJson }, ['senderAddress', 'recipientAddress', 'lovelaceAmount', 'assetsJson']);
|
|
133
|
+
(0, errors_1.throwIfValidationErrors)(req, 'BuildMultiAssetTransaction', errors);
|
|
134
|
+
// Parse assetsJson (already validated as valid JSON by validateTransactionInputs)
|
|
135
|
+
const parsedAssets = JSON.parse(assetsJson);
|
|
136
|
+
if (!Array.isArray(parsedAssets)) {
|
|
137
|
+
(0, errors_1.rejectInvalid)(req, 'BuildMultiAssetTransaction', 'assetsJson must be a JSON array', 'assetsJson');
|
|
138
|
+
}
|
|
139
|
+
// handle the request / building the transaction / indexing the build result / returning build details
|
|
140
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
141
|
+
logger.info({ senderAddress, recipientAddress, lovelaceAmount, assets: parsedAssets }, 'Building multi-asset transaction');
|
|
142
|
+
// Create clean request object with parsed assets (remove assetsJson, add assets)
|
|
143
|
+
const cleanData = { ...req.data };
|
|
144
|
+
delete cleanData.assetsJson;
|
|
145
|
+
const result = await (0, server_1.getCardanoIndexer)().indexMultiAssetBuildResult(db, { ...cleanData, assets: parsedAssets });
|
|
146
|
+
logger.info({ result }, 'Multi-asset transaction build result');
|
|
147
|
+
return result;
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
/**
|
|
151
|
+
* Build a minting transaction
|
|
152
|
+
* @param req - CDS request object (with senderAddress, recipientAddress, lovelaceAmount, mintActionsJson, mintingPolicyScript, changeAddress)
|
|
153
|
+
* @returns {TransactionBuild} Transaction build details
|
|
154
|
+
*/
|
|
155
|
+
srv.on('BuildMintTransaction', async (req) => {
|
|
156
|
+
const { senderAddress, recipientAddress, lovelaceAmount, mintActionsJson, mintingPolicyScript } = req.data;
|
|
157
|
+
// validate inputs (includes JSON and CBOR validation)
|
|
158
|
+
const errors = (0, validators_1.validateTransactionInputs)({ senderAddress, recipientAddress, mintActionsJson, mintingPolicyScript }, ['senderAddress', 'recipientAddress', 'mintActionsJson', 'mintingPolicyScript']);
|
|
159
|
+
(0, errors_1.throwIfValidationErrors)(req, 'BuildMintTransaction', errors);
|
|
160
|
+
// Parse mintActionsJson and convert quantity strings to bigint
|
|
161
|
+
const parsedMintActionsRaw = JSON.parse(mintActionsJson);
|
|
162
|
+
if (!Array.isArray(parsedMintActionsRaw)) {
|
|
163
|
+
(0, errors_1.rejectInvalid)(req, 'BuildMintTransaction', 'mintActionsJson must be a JSON array', 'mintActionsJson');
|
|
164
|
+
}
|
|
165
|
+
const parsedMintActions = parsedMintActionsRaw.map((action) => ({
|
|
166
|
+
...action,
|
|
167
|
+
quantity: BigInt(action.quantity)
|
|
168
|
+
}));
|
|
169
|
+
// handle the request / building the transaction / indexing the build result / returning build details
|
|
170
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
171
|
+
logger.info({ senderAddress, recipientAddress, lovelaceAmount, mintActions: parsedMintActions }, 'Building minting transaction');
|
|
172
|
+
// Create clean request object with parsed mintActions (remove mintActionsJson, add mintActions)
|
|
173
|
+
const cleanData = { ...req.data };
|
|
174
|
+
delete cleanData.mintActionsJson;
|
|
175
|
+
return await (0, server_1.getCardanoIndexer)().indexMintBuildResult(db, {
|
|
176
|
+
...cleanData,
|
|
177
|
+
mintActions: parsedMintActions,
|
|
178
|
+
mintingPolicyScript
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
/**
|
|
183
|
+
* Build a Plutus spending transaction (consume UTxO at script address)
|
|
184
|
+
* @param req - CDS request object (with senderAddress, recipientAddress, lovelaceAmount, validatorScript, scriptTxHash, scriptOutputIndex, redeemerJson, datumJson, changeAddress)
|
|
185
|
+
* @returns {TransactionBuild} Transaction build details
|
|
186
|
+
*/
|
|
187
|
+
srv.on('BuildPlutusSpendTransaction', async (req) => {
|
|
188
|
+
const { senderAddress, recipientAddress, lovelaceAmount, validatorScript, scriptTxHash, scriptOutputIndex, redeemerJson, datumJson } = req.data;
|
|
189
|
+
// Validate inputs
|
|
190
|
+
const errors = (0, validators_1.validateTransactionInputs)({ senderAddress, recipientAddress, validatorScript, scriptTxHash, scriptOutputIndex, redeemerJson }, ['senderAddress', 'recipientAddress', 'validatorScript', 'scriptTxHash', 'redeemerJson']);
|
|
191
|
+
(0, errors_1.throwIfValidationErrors)(req, 'BuildPlutusSpendTransaction', errors);
|
|
192
|
+
// Validate scriptOutputIndex separately (it's a number, not caught by required-fields check for empty string)
|
|
193
|
+
if (scriptOutputIndex === undefined || scriptOutputIndex === null) {
|
|
194
|
+
(0, errors_1.rejectMissing)(req, 'BuildPlutusSpendTransaction', 'scriptOutputIndex');
|
|
195
|
+
}
|
|
196
|
+
// Parse redeemer JSON
|
|
197
|
+
const parsedRedeemer = JSON.parse(redeemerJson);
|
|
198
|
+
// Parse optional datum JSON
|
|
199
|
+
const parsedDatum = datumJson ? JSON.parse(datumJson) : undefined;
|
|
200
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
201
|
+
logger.info({ senderAddress, recipientAddress, lovelaceAmount, scriptTxHash, scriptOutputIndex }, 'Building Plutus spending transaction');
|
|
202
|
+
const cleanData = {
|
|
203
|
+
...req.data,
|
|
204
|
+
plutusScriptExecution: {
|
|
205
|
+
validatorScript,
|
|
206
|
+
scriptUtxo: {
|
|
207
|
+
txHash: scriptTxHash,
|
|
208
|
+
outputIndex: scriptOutputIndex,
|
|
209
|
+
},
|
|
210
|
+
redeemer: parsedRedeemer,
|
|
211
|
+
datum: parsedDatum,
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
return await (0, server_1.getCardanoIndexer)().indexPlutusSpendBuildResult(db, cleanData);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
/**
|
|
218
|
+
* Get build details for previously built transaction
|
|
219
|
+
* @param req - CDS request object (with buildId)
|
|
220
|
+
* @returns {TransactionBuild} Transaction build details
|
|
221
|
+
*/
|
|
222
|
+
srv.on('GetBuildDetails', async (req) => {
|
|
223
|
+
const { buildId } = req.data;
|
|
224
|
+
// Validate inputs
|
|
225
|
+
const errors = (0, validators_1.validateTransactionInputs)({ buildId }, ['buildId']);
|
|
226
|
+
(0, errors_1.throwIfValidationErrors)(req, 'GetBuildDetails', errors);
|
|
227
|
+
// handle the request / fetching the build details
|
|
228
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
229
|
+
const existing = await db.run(SELECT.one.from(TransactionBuilds).where({ id: buildId }));
|
|
230
|
+
if (!existing)
|
|
231
|
+
(0, errors_1.rejectInvalid)(req, 'GetBuildDetails', 'Build not found', 'buildId');
|
|
232
|
+
return existing;
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
/**
|
|
236
|
+
* Set up a collateral UTxO for Plutus transactions.
|
|
237
|
+
* Checks if the address already has >= 2 UTxOs with >= 5 ADA each.
|
|
238
|
+
* If not, builds a self-send transaction to create a 5 ADA collateral UTxO.
|
|
239
|
+
* @param req - CDS request object (with address)
|
|
240
|
+
* @returns {TransactionBuild} Transaction build details for the collateral setup
|
|
241
|
+
*/
|
|
242
|
+
srv.on('SetCollateral', async (req) => {
|
|
243
|
+
const { address } = req.data;
|
|
244
|
+
if (!address || !(0, validators_1.isValidBech32Address)(address)) {
|
|
245
|
+
return req.reject(400, 'SetCollateral: Invalid or missing Bech32 address');
|
|
246
|
+
}
|
|
247
|
+
const COLLATERAL_LOVELACE = 5000000n;
|
|
248
|
+
const FEE_BUFFER_LOVELACE = 1000000n;
|
|
249
|
+
// Fetch UTxOs and validate before entering handleRequest (req.reject inside handleRequest gets wrapped as 500)
|
|
250
|
+
const utxos = await (0, server_1.getCardanoClient)().getAddressUtxos(address);
|
|
251
|
+
if (utxos.length === 0) {
|
|
252
|
+
return req.reject(400, 'SetCollateral: No UTxOs found at address');
|
|
253
|
+
}
|
|
254
|
+
const qualifyingUtxos = utxos.filter(u => (0, tx_build_helper_1.getLovelace)(u) >= COLLATERAL_LOVELACE);
|
|
255
|
+
if (qualifyingUtxos.length >= 2) {
|
|
256
|
+
return req.reject(409, `SetCollateral: Collateral already available — found ${qualifyingUtxos.length} UTxOs with >= 5 ADA`);
|
|
257
|
+
}
|
|
258
|
+
const totalLovelace = utxos.reduce((sum, u) => sum + (0, tx_build_helper_1.getLovelace)(u), 0n);
|
|
259
|
+
if (totalLovelace < COLLATERAL_LOVELACE + FEE_BUFFER_LOVELACE) {
|
|
260
|
+
return req.reject(400, `SetCollateral: Insufficient funds — need at least ${Number(COLLATERAL_LOVELACE + FEE_BUFFER_LOVELACE) / 1_000_000} ADA, have ${Number(totalLovelace) / 1_000_000} ADA`);
|
|
261
|
+
}
|
|
262
|
+
// Build self-send transaction: address → address, 5 ADA
|
|
263
|
+
logger.info({ address, existingQualifying: qualifyingUtxos.length }, 'Building collateral setup transaction');
|
|
264
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
265
|
+
return await (0, server_1.getCardanoIndexer)().indexSimpleBuildResult(db, {
|
|
266
|
+
network: (0, server_1.getCardanoClient)().network,
|
|
267
|
+
senderAddress: address,
|
|
268
|
+
recipientAddress: address,
|
|
269
|
+
lovelaceAmount: Number(COLLATERAL_LOVELACE),
|
|
270
|
+
changeAddress: address,
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
/**
|
|
275
|
+
* Submit signed transaction built previously
|
|
276
|
+
* Handler validates, checks build exists, submits to blockchain, delegates persistence to indexer
|
|
277
|
+
* @param req - CDS request object (with buildId, signedTxCbor)
|
|
278
|
+
* @returns {TransactionSubmission} Transaction submission details
|
|
279
|
+
*/
|
|
280
|
+
srv.on('SubmitTransaction', async (req) => {
|
|
281
|
+
logger.debug('SubmitTransaction Action handler called');
|
|
282
|
+
const { buildId, signedTxCbor } = req.data;
|
|
283
|
+
// Validate inputs (includes CBOR format validation)
|
|
284
|
+
const errors = (0, validators_1.validateTransactionInputs)({ buildId, signedTxCbor }, ['buildId', 'signedTxCbor']);
|
|
285
|
+
(0, errors_1.throwIfValidationErrors)(req, 'SubmitTransaction', errors);
|
|
286
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
287
|
+
logger.debug({ buildId }, 'Submitting signed transaction');
|
|
288
|
+
// Validate build exists
|
|
289
|
+
const existing = await db.run(SELECT.one.from(TransactionBuilds).where({ id: buildId }));
|
|
290
|
+
if (!existing)
|
|
291
|
+
(0, errors_1.rejectInvalid)(req, 'SubmitTransaction', 'Build not found', 'buildId');
|
|
292
|
+
// Use txBodyHash from build
|
|
293
|
+
const txHash = existing.txBodyHash;
|
|
294
|
+
// Two-phase submit: persist with 'pending', then submit to blockchain
|
|
295
|
+
const submissionRecord = await (0, server_1.getCardanoIndexer)().persistTransactionSubmission(db, {
|
|
296
|
+
signedTxCbor,
|
|
297
|
+
txHash,
|
|
298
|
+
buildId,
|
|
299
|
+
});
|
|
300
|
+
try {
|
|
301
|
+
await (0, server_1.getCardanoClient)().submitTransaction(signedTxCbor);
|
|
302
|
+
logger.info({ txHash }, 'Transaction submitted to blockchain');
|
|
303
|
+
await (0, server_1.getCardanoIndexer)().updateSubmissionStatus(db, submissionRecord.id, 'submitted');
|
|
304
|
+
submissionRecord.status = 'submitted';
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
logger.error({ txHash, error: err.message }, 'Transaction submission failed');
|
|
308
|
+
await (0, server_1.getCardanoIndexer)().updateSubmissionStatus(db, submissionRecord.id, 'failed', err.message);
|
|
309
|
+
throw err;
|
|
310
|
+
}
|
|
311
|
+
return submissionRecord;
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
/**
|
|
315
|
+
* Submit signed transaction without prior build
|
|
316
|
+
* Handler validates, submits to blockchain, delegates persistence to indexer
|
|
317
|
+
* Two-phase: persist with 'pending' first, then update to 'submitted' or 'failed'
|
|
318
|
+
* @param req - CDS request object (with signedTxCbor, network)
|
|
319
|
+
* @returns {TransactionSubmission} Transaction submission details
|
|
320
|
+
*/
|
|
321
|
+
srv.on('SubmitSignedTransaction', async (req) => {
|
|
322
|
+
logger.info('SubmitSignedTransaction Action handler called');
|
|
323
|
+
const { signedTxCbor } = req.data;
|
|
324
|
+
// Validate inputs (includes CBOR format validation)
|
|
325
|
+
const errors = (0, validators_1.validateTransactionInputs)({ signedTxCbor }, ['signedTxCbor']);
|
|
326
|
+
(0, errors_1.throwIfValidationErrors)(req, 'SubmitSignedTransaction', errors);
|
|
327
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
328
|
+
// Extract txHash from signed CBOR
|
|
329
|
+
const txHash = (0, tx_build_helper_1.getTxHashFromCbor)(signedTxCbor);
|
|
330
|
+
// Two-phase submit: persist with 'pending', then submit to blockchain
|
|
331
|
+
const submissionRecord = await (0, server_1.getCardanoIndexer)().persistTransactionSubmission(db, {
|
|
332
|
+
signedTxCbor,
|
|
333
|
+
txHash,
|
|
334
|
+
buildId: null,
|
|
335
|
+
});
|
|
336
|
+
try {
|
|
337
|
+
await (0, server_1.getCardanoClient)().submitTransaction(signedTxCbor);
|
|
338
|
+
logger.info({ txHash }, 'External transaction submitted');
|
|
339
|
+
await (0, server_1.getCardanoIndexer)().updateSubmissionStatus(db, submissionRecord.id, 'submitted');
|
|
340
|
+
submissionRecord.status = 'submitted';
|
|
341
|
+
}
|
|
342
|
+
catch (err) {
|
|
343
|
+
logger.error({ txHash, error: err.message }, 'External transaction submission failed');
|
|
344
|
+
await (0, server_1.getCardanoIndexer)().updateSubmissionStatus(db, submissionRecord.id, 'failed', err.message);
|
|
345
|
+
throw err;
|
|
346
|
+
}
|
|
347
|
+
return submissionRecord;
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
/**
|
|
351
|
+
* Check submission status (bound action on TransactionSubmissions)
|
|
352
|
+
* @flow.status validates @from: [#submitted] automatically (409 if wrong state)
|
|
353
|
+
* Queries blockchain for transaction confirmation and updates status accordingly
|
|
354
|
+
* @param req - CDS request with entity key in params
|
|
355
|
+
* @returns {TransactionSubmission} The updated transaction submission status
|
|
356
|
+
*/
|
|
357
|
+
srv.on('CheckSubmissionStatus', async (req) => {
|
|
358
|
+
logger.debug('CheckSubmissionStatus Action handler called');
|
|
359
|
+
const { id: submissionId } = req.params[0];
|
|
360
|
+
// @from: [#submitted] validated by framework — no manual status check needed
|
|
361
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
362
|
+
const submission = await db.run(SELECT.one.from(TransactionSubmissions).where({ id: submissionId }));
|
|
363
|
+
if (!submission)
|
|
364
|
+
(0, errors_1.rejectInvalid)(req, 'CheckSubmissionStatus', 'Submission not found', 'submissionId');
|
|
365
|
+
// Query blockchain for confirmation
|
|
366
|
+
try {
|
|
367
|
+
const txDetails = await (0, server_1.getCardanoClient)().getTransaction(submission.txHash);
|
|
368
|
+
if (txDetails) {
|
|
369
|
+
await db.run(UPDATE.entity(TransactionSubmissions)
|
|
370
|
+
.set({ status: 'confirmed' })
|
|
371
|
+
.where({ id: submissionId }));
|
|
372
|
+
submission.status = 'confirmed';
|
|
373
|
+
logger.info({ submissionId, txHash: submission.txHash }, 'Transaction confirmed on chain');
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
// Transaction not yet confirmed on chain — status stays 'submitted'
|
|
378
|
+
logger.debug({ submissionId, txHash: submission.txHash }, 'Transaction not yet confirmed on chain');
|
|
379
|
+
}
|
|
380
|
+
return submission;
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
// ---------------------------------------------------------------------------
|
|
384
|
+
// M3 - External Signing Workflow Actions
|
|
385
|
+
// ---------------------------------------------------------------------------
|
|
386
|
+
/**
|
|
387
|
+
* before-READ handler for SigningRequests: lazy expiration check
|
|
388
|
+
* Updates status to 'expired' if expiresAt has passed (for pending requests)
|
|
389
|
+
*/
|
|
390
|
+
srv.before('READ', SigningRequests, async (req) => {
|
|
391
|
+
// Expiration check runs on single-entity reads (where ID is provided)
|
|
392
|
+
if (req.params && req.params.length > 0) {
|
|
393
|
+
const { id } = req.params[0];
|
|
394
|
+
if (id) {
|
|
395
|
+
const db = await cds_1.default.connect.to('db');
|
|
396
|
+
const signingRequest = await db.run(SELECT.one.from(SigningRequests).where({ id }));
|
|
397
|
+
if (signingRequest && signingRequest.status === 'pending') {
|
|
398
|
+
await checkAndExpireSigningRequest(db, signingRequest, SigningRequests);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
/**
|
|
404
|
+
* READ handler for SigningRequests entity
|
|
405
|
+
* @param req - The incoming request data
|
|
406
|
+
* @returns {SigningRequest} The signing requests fitting the request query
|
|
407
|
+
*/
|
|
408
|
+
srv.on('READ', SigningRequests, async (req) => {
|
|
409
|
+
logger.debug('SigningRequests READ handler called');
|
|
410
|
+
return (0, backend_request_handler_1.handleRequest)(req, (db) => db.run(req.query));
|
|
411
|
+
});
|
|
412
|
+
/**
|
|
413
|
+
* READ handler for SignatureVerifications entity
|
|
414
|
+
* @param req - The incoming request data
|
|
415
|
+
* @returns {SignatureVerification} The signature verifications fitting the request query
|
|
416
|
+
*/
|
|
417
|
+
srv.on('READ', SignatureVerifications, async (req) => {
|
|
418
|
+
logger.debug('SignatureVerifications READ handler called');
|
|
419
|
+
return (0, backend_request_handler_1.handleRequest)(req, (db) => db.run(req.query));
|
|
420
|
+
});
|
|
421
|
+
/**
|
|
422
|
+
* READ handler for AddressSigningRequests entity
|
|
423
|
+
* @param req - The incoming request data
|
|
424
|
+
* @returns {AddressSigningRequest} The address signing requests fitting the request query
|
|
425
|
+
*/
|
|
426
|
+
srv.on('READ', AddressSigningRequests, async (req) => {
|
|
427
|
+
logger.debug('AddressSigningRequests READ handler called');
|
|
428
|
+
return (0, backend_request_handler_1.handleRequest)(req, (db) => db.run(req.query));
|
|
429
|
+
});
|
|
430
|
+
/**
|
|
431
|
+
* READ handler for AddressTransactionBuilds entity
|
|
432
|
+
* @param req - The incoming request data
|
|
433
|
+
* @returns {AddressTransactionBuild} The address transaction builds fitting the request query
|
|
434
|
+
*/
|
|
435
|
+
srv.on('READ', AddressTransactionBuilds, async (req) => {
|
|
436
|
+
logger.debug('AddressTransactionBuilds READ handler called');
|
|
437
|
+
return (0, backend_request_handler_1.handleRequest)(req, (db) => db.run(req.query));
|
|
438
|
+
});
|
|
439
|
+
/**
|
|
440
|
+
* Create a new signing request for external signing
|
|
441
|
+
* Persists the request for audit trail and workflow tracking
|
|
442
|
+
* @param req - CDS request object (with buildId)
|
|
443
|
+
* @returns {SigningRequest} Signing request entity
|
|
444
|
+
*/
|
|
445
|
+
srv.on('CreateSigningRequest', async (req) => {
|
|
446
|
+
logger.debug('CreateSigningRequest Action handler called');
|
|
447
|
+
const { buildId, message } = req.data;
|
|
448
|
+
// Validate inputs
|
|
449
|
+
const errors = (0, validators_1.validateTransactionInputs)({ buildId }, ['buildId']);
|
|
450
|
+
(0, errors_1.throwIfValidationErrors)(req, 'CreateSigningRequest', errors);
|
|
451
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
452
|
+
// Fetch the build
|
|
453
|
+
const build = await db.run(SELECT.one.from(TransactionBuilds).where({ id: buildId }));
|
|
454
|
+
if (!build)
|
|
455
|
+
(0, errors_1.rejectInvalid)(req, 'CreateSigningRequest', 'Build not found', 'buildId');
|
|
456
|
+
// Check if signing request already exists for this build
|
|
457
|
+
const existingRequest = await db.run(SELECT.one.from(SigningRequests).where({ build_id: buildId, status: 'pending' }));
|
|
458
|
+
if (existingRequest) {
|
|
459
|
+
logger.info({ buildId, signingRequestId: existingRequest.id }, 'Returning existing signing request');
|
|
460
|
+
return existingRequest;
|
|
461
|
+
}
|
|
462
|
+
// Create signing request using external signer module
|
|
463
|
+
const signerModule = (0, external_signer_1.getExternalSignerModule)();
|
|
464
|
+
const signingPayload = signerModule.createSigningRequest(build.id, build.unsignedTxCbor, build.txBodyHash, build.network, message);
|
|
465
|
+
// Delegate persistence to indexer
|
|
466
|
+
const signingRequestRecord = await (0, server_1.getCardanoIndexer)().persistSigningRequest(db, {
|
|
467
|
+
buildId,
|
|
468
|
+
signingPayload,
|
|
469
|
+
});
|
|
470
|
+
logger.info({ buildId, signingRequestId: signingRequestRecord.id }, 'Created signing request');
|
|
471
|
+
return signingRequestRecord;
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
/**
|
|
475
|
+
* Get an existing signing request by ID
|
|
476
|
+
* @param req - CDS request object (with signingRequestId)
|
|
477
|
+
* @returns {SigningRequest} signing request entity
|
|
478
|
+
*/
|
|
479
|
+
srv.on('GetSigningRequest', async (req) => {
|
|
480
|
+
logger.debug('GetSigningRequest Action handler called');
|
|
481
|
+
const { signingRequestId } = req.data;
|
|
482
|
+
// Validate inputs
|
|
483
|
+
const errors = (0, validators_1.validateTransactionInputs)({ signingRequestId }, ['signingRequestId']);
|
|
484
|
+
(0, errors_1.throwIfValidationErrors)(req, 'GetSigningRequest', errors);
|
|
485
|
+
// Fetch the signing request within transaction context
|
|
486
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
487
|
+
const signingRequest = await db.run(SELECT.one.from(SigningRequests).where({ id: signingRequestId }));
|
|
488
|
+
if (!signingRequest)
|
|
489
|
+
(0, errors_1.rejectInvalid)(req, 'GetSigningRequest', 'Signing request not found', 'signingRequestId');
|
|
490
|
+
// Check if expired and update status if needed
|
|
491
|
+
if (signingRequest.status === 'pending') {
|
|
492
|
+
await checkAndExpireSigningRequest(db, signingRequest, SigningRequests);
|
|
493
|
+
}
|
|
494
|
+
return signingRequest;
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
/**
|
|
498
|
+
* Verify signature of a signed transaction (bound action on SigningRequests)
|
|
499
|
+
* @flow.status validates @from: [#pending] automatically (409 if wrong state)
|
|
500
|
+
* @param req - CDS request with entity key in params, action data in data
|
|
501
|
+
* @returns {SignatureVerification} Persisted signature verification entity
|
|
502
|
+
*/
|
|
503
|
+
srv.on('VerifySignature', async (req) => {
|
|
504
|
+
logger.debug('VerifySignature Action handler called');
|
|
505
|
+
const { id: signingRequestId } = req.params[0];
|
|
506
|
+
const { signedTxCbor, signerType, signerInfo } = req.data;
|
|
507
|
+
// Validate inputs (signingRequestId no longer needed — comes from URL)
|
|
508
|
+
const errors = (0, validators_1.validateTransactionInputs)({ signedTxCbor }, ['signedTxCbor']);
|
|
509
|
+
(0, errors_1.throwIfValidationErrors)(req, 'VerifySignature', errors);
|
|
510
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
511
|
+
// Fetch the signing request (@from already validated by framework)
|
|
512
|
+
const signingRequest = await db.run(SELECT.one.from(SigningRequests).where({ id: signingRequestId }));
|
|
513
|
+
if (!signingRequest)
|
|
514
|
+
(0, errors_1.rejectInvalid)(req, 'VerifySignature', 'Signing request not found', 'signingRequestId');
|
|
515
|
+
// Check if expired (time-based, not covered by @flow.status)
|
|
516
|
+
if (await checkAndExpireSigningRequest(db, signingRequest, SigningRequests)) {
|
|
517
|
+
(0, errors_1.rejectInvalid)(req, 'VerifySignature', 'Signing request has expired', 'signingRequestId');
|
|
518
|
+
}
|
|
519
|
+
// Verify the signature
|
|
520
|
+
const signerModule = (0, external_signer_1.getExternalSignerModule)();
|
|
521
|
+
const result = signerModule.verifySignedTransaction(signedTxCbor, signingRequest.txBodyHash);
|
|
522
|
+
// Delegate persistence to indexer
|
|
523
|
+
const verificationRecord = await (0, server_1.getCardanoIndexer)().persistSignatureVerification(db, {
|
|
524
|
+
signingRequestId,
|
|
525
|
+
signedTxCbor,
|
|
526
|
+
verificationResult: result,
|
|
527
|
+
signerType,
|
|
528
|
+
signerInfo,
|
|
529
|
+
});
|
|
530
|
+
logger.info({
|
|
531
|
+
signingRequestId,
|
|
532
|
+
verificationId: verificationRecord.id,
|
|
533
|
+
isValid: result.isValid,
|
|
534
|
+
witnessCount: result.witnessCount,
|
|
535
|
+
}, 'Signature verification completed');
|
|
536
|
+
return verificationRecord;
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
/**
|
|
540
|
+
* Verify and submit a signed transaction in one step (bound action on SigningRequests)
|
|
541
|
+
* @flow.status validates @from: [#pending, #verified] and sets @to: #submitted automatically
|
|
542
|
+
* @param req - CDS request with entity key in params, action data in data
|
|
543
|
+
* @returns {TransactionSubmission} Transaction submission details
|
|
544
|
+
*/
|
|
545
|
+
srv.on('SubmitVerifiedTransaction', async (req) => {
|
|
546
|
+
logger.debug('SubmitVerifiedTransaction Action handler called');
|
|
547
|
+
const { id: signingRequestId } = req.params[0];
|
|
548
|
+
const { signedTxCbor, signerType, signerInfo } = req.data;
|
|
549
|
+
// Validate inputs (signingRequestId no longer needed — comes from URL)
|
|
550
|
+
const errors = (0, validators_1.validateTransactionInputs)({ signedTxCbor }, ['signedTxCbor']);
|
|
551
|
+
(0, errors_1.throwIfValidationErrors)(req, 'SubmitVerifiedTransaction', errors);
|
|
552
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
553
|
+
// Fetch the signing request (@from already validated by framework)
|
|
554
|
+
const signingRequest = await db.run(SELECT.one.from(SigningRequests).where({ id: signingRequestId }));
|
|
555
|
+
if (!signingRequest)
|
|
556
|
+
(0, errors_1.rejectInvalid)(req, 'SubmitVerifiedTransaction', 'Signing request not found', 'signingRequestId');
|
|
557
|
+
// Check if expired (time-based, not covered by @flow.status)
|
|
558
|
+
if (await checkAndExpireSigningRequest(db, signingRequest, SigningRequests)) {
|
|
559
|
+
(0, errors_1.rejectInvalid)(req, 'SubmitVerifiedTransaction', 'Signing request has expired', 'signingRequestId');
|
|
560
|
+
}
|
|
561
|
+
// STATUS CHECKS REMOVED — @flow.status handles @from: [#pending, #verified]
|
|
562
|
+
// Previously: manual checks for 'submitted', 'expired', 'failed' states
|
|
563
|
+
// Check if build association exists
|
|
564
|
+
if (!signingRequest.build_id) {
|
|
565
|
+
(0, errors_1.rejectInvalid)(req, 'SubmitVerifiedTransaction', 'Signing request has no associated build', 'signingRequestId');
|
|
566
|
+
}
|
|
567
|
+
// Detect if signedTxCbor is a witness set (CIP-30) or a full signed transaction (cardano-cli)
|
|
568
|
+
let fullSignedTxCbor;
|
|
569
|
+
if ((0, signing_helper_1.isWitnessSetCbor)(signedTxCbor)) {
|
|
570
|
+
// CIP-30 wallet returns only witness set - combine with unsigned tx
|
|
571
|
+
fullSignedTxCbor = (0, signing_helper_1.combineTransactionWithWitnesses)(signingRequest.unsignedTxCbor, signedTxCbor);
|
|
572
|
+
logger.debug({ signingRequestId }, 'Combined witness set with unsigned transaction');
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
// Full signed transaction provided (e.g., from cardano-cli)
|
|
576
|
+
fullSignedTxCbor = signedTxCbor;
|
|
577
|
+
logger.debug({ signingRequestId }, 'Using full signed transaction directly');
|
|
578
|
+
}
|
|
579
|
+
// Verify signature (throws on failure)
|
|
580
|
+
const signerModule = (0, external_signer_1.getExternalSignerModule)();
|
|
581
|
+
const verificationResult = signerModule.verifyOrThrow(fullSignedTxCbor, signingRequest.txBodyHash);
|
|
582
|
+
logger.info({
|
|
583
|
+
signingRequestId,
|
|
584
|
+
witnessCount: verificationResult.witnessCount,
|
|
585
|
+
signers: verificationResult.signerKeyHashes,
|
|
586
|
+
}, 'Signature verified, proceeding with submission');
|
|
587
|
+
// Submit to blockchain
|
|
588
|
+
const txHash = signingRequest.txBodyHash;
|
|
589
|
+
await (0, server_1.getCardanoClient)().submitTransaction(fullSignedTxCbor);
|
|
590
|
+
logger.info({ txHash }, 'Verified transaction submitted to blockchain');
|
|
591
|
+
// Delegate all persistence to indexer
|
|
592
|
+
const submissionRecord = await (0, server_1.getCardanoIndexer)().indexVerifiedTransactionSubmission(db, {
|
|
593
|
+
signingRequestId,
|
|
594
|
+
buildId: signingRequest.build_id,
|
|
595
|
+
fullSignedTxCbor,
|
|
596
|
+
txHash,
|
|
597
|
+
verificationResult,
|
|
598
|
+
signerType,
|
|
599
|
+
signerInfo,
|
|
600
|
+
});
|
|
601
|
+
logger.info({
|
|
602
|
+
signingRequestId,
|
|
603
|
+
submissionId: submissionRecord.id,
|
|
604
|
+
txHash,
|
|
605
|
+
}, 'Transaction submitted and all records updated');
|
|
606
|
+
return submissionRecord;
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
/**
|
|
610
|
+
* Get all existing signing requests by address
|
|
611
|
+
* @param req - CDS request object (with address)
|
|
612
|
+
* @returns {AddressSigningRequests} Address signing request associations
|
|
613
|
+
*/
|
|
614
|
+
srv.on('GetSigningRequestsByAddress', async (req) => {
|
|
615
|
+
logger.debug('GetSigningRequestsByAddress Action handler called');
|
|
616
|
+
const { address } = req.data;
|
|
617
|
+
// Validate input before business logic
|
|
618
|
+
if (!address)
|
|
619
|
+
(0, errors_1.rejectMissing)(req, 'GetSigningRequestsByAddress', 'address');
|
|
620
|
+
if (!(0, validators_1.isValidBech32Address)(address))
|
|
621
|
+
(0, errors_1.rejectInvalid)(req, 'GetSigningRequestsByAddress', 'Invalid bech32 address format', 'address');
|
|
622
|
+
// Fetch the address-signing request associations
|
|
623
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
624
|
+
return db.run(SELECT.from(AddressSigningRequests).where({ address_address: address }));
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
/**
|
|
628
|
+
* Get all existing transaction builds by address
|
|
629
|
+
* @param req - CDS request object (with address)
|
|
630
|
+
* @returns {AddressTransactionBuilds} Address transaction build associations
|
|
631
|
+
*/
|
|
632
|
+
srv.on('GetTransactionBuildsByAddress', async (req) => {
|
|
633
|
+
logger.debug('GetTransactionBuildsByAddress Action handler called');
|
|
634
|
+
const { address } = req.data;
|
|
635
|
+
// Validate inputs
|
|
636
|
+
if (!address)
|
|
637
|
+
(0, errors_1.rejectMissing)(req, 'GetTransactionBuildsByAddress', 'address');
|
|
638
|
+
if (!(0, validators_1.isValidBech32Address)(address))
|
|
639
|
+
(0, errors_1.rejectInvalid)(req, 'GetTransactionBuildsByAddress', 'Invalid bech32 address format', 'address');
|
|
640
|
+
// Fetch the address-build associations
|
|
641
|
+
return (0, backend_request_handler_1.handleRequest)(req, async (db) => {
|
|
642
|
+
return db.run(SELECT.from(AddressTransactionBuilds).where({ address_address: address }));
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
};
|
|
646
|
+
//# sourceMappingURL=cardano-tx-service.js.map
|