@sundaeswap/sprinkles 0.1.1 → 0.2.0
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/dist/cjs/Sprinkle/__tests__/tx-dialog.test.js +451 -210
- package/dist/cjs/Sprinkle/__tests__/tx-dialog.test.js.map +1 -1
- package/dist/cjs/Sprinkle/index.js +333 -86
- package/dist/cjs/Sprinkle/index.js.map +1 -1
- package/dist/esm/Sprinkle/__tests__/tx-dialog.test.js +451 -210
- package/dist/esm/Sprinkle/__tests__/tx-dialog.test.js.map +1 -1
- package/dist/esm/Sprinkle/index.js +334 -87
- package/dist/esm/Sprinkle/index.js.map +1 -1
- package/dist/types/Sprinkle/index.d.ts +40 -3
- package/dist/types/Sprinkle/index.d.ts.map +1 -1
- package/dist/types/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -3
- package/src/Sprinkle/__tests__/tx-dialog.test.ts +517 -234
- package/src/Sprinkle/index.ts +347 -109
package/src/Sprinkle/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
HotWallet,
|
|
7
7
|
type Wallet,
|
|
8
8
|
} from "@blaze-cardano/sdk";
|
|
9
|
-
import { CborSet, VkeyWitness } from "@blaze-cardano/core";
|
|
9
|
+
import { CborSet, VkeyWitness, blake2b_256, TxCBOR } from "@blaze-cardano/core";
|
|
10
10
|
import { confirm, input, password, search, select } from "@inquirer/prompts";
|
|
11
11
|
import {
|
|
12
12
|
Kind,
|
|
@@ -72,6 +72,16 @@ export interface IProfileEntry {
|
|
|
72
72
|
meta: IProfileMeta;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
export interface TxDialogResult {
|
|
76
|
+
action: "submitted" | "signed" | "cancelled";
|
|
77
|
+
txId?: string; // present only if action === 'submitted'
|
|
78
|
+
tx: Core.Transaction; // the (potentially signed) transaction
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface TxDialogOptions {
|
|
82
|
+
beforeSign?: () => Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
|
|
75
85
|
export const NetworkSchema = Type.Union([
|
|
76
86
|
Type.Literal("mainnet"),
|
|
77
87
|
Type.Literal("preview"),
|
|
@@ -803,152 +813,380 @@ export class Sprinkle<S extends TSchema> {
|
|
|
803
813
|
this.saveProfile();
|
|
804
814
|
}
|
|
805
815
|
|
|
816
|
+
// --- TxDialog Helpers ---
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* Get the payment key hash from a HotWallet's first address
|
|
820
|
+
*/
|
|
821
|
+
private async getWalletPaymentKeyHash(
|
|
822
|
+
wallet: HotWallet,
|
|
823
|
+
): Promise<string | null> {
|
|
824
|
+
try {
|
|
825
|
+
const addresses = await wallet.getUsedAddresses();
|
|
826
|
+
const address = addresses[0];
|
|
827
|
+
if (!address) return null;
|
|
828
|
+
const paymentCredential = address.asBase()?.getPaymentCredential();
|
|
829
|
+
return paymentCredential?.hash?.toString() ?? null;
|
|
830
|
+
} catch {
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Count the number of vkey signatures in a transaction's witness set
|
|
837
|
+
*/
|
|
838
|
+
private countSignatures(tx: Core.Transaction): number {
|
|
839
|
+
const vkeys = tx.witnessSet().vkeys();
|
|
840
|
+
return vkeys ? vkeys.size() : 0;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Check if a specific public key has already signed the transaction
|
|
845
|
+
* Compares by vkey (public key bytes)
|
|
846
|
+
*/
|
|
847
|
+
private hasVkeySigned(tx: Core.Transaction, vkeyHex: string): boolean {
|
|
848
|
+
const vkeys = tx.witnessSet().vkeys();
|
|
849
|
+
if (!vkeys) return false;
|
|
850
|
+
const vkeyArray = vkeys.toCore();
|
|
851
|
+
return vkeyArray.some(([vkey]) => vkey === vkeyHex);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Get the list of required signer key hashes from the transaction body
|
|
856
|
+
*/
|
|
857
|
+
private getRequiredSigners(tx: Core.Transaction): string[] {
|
|
858
|
+
const requiredSigners = tx.body().requiredSigners();
|
|
859
|
+
if (!requiredSigners) return [];
|
|
860
|
+
return Array.from(requiredSigners.values()).map((s) => s.toString());
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Compute the transaction body hash for display
|
|
865
|
+
*/
|
|
866
|
+
private getTxBodyHash(tx: Core.Transaction): string {
|
|
867
|
+
const bodyCbor = tx.body().toCbor();
|
|
868
|
+
return blake2b_256(bodyCbor);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Format a hash for display: first 8 chars + ... + last 8 chars
|
|
873
|
+
*/
|
|
874
|
+
private formatHash(hash: string): string {
|
|
875
|
+
if (hash.length <= 20) return hash;
|
|
876
|
+
return `${hash.slice(0, 8)}...${hash.slice(-8)}`;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/**
|
|
880
|
+
* Merge signatures from source transaction into target transaction.
|
|
881
|
+
* Prevents duplicate signatures by comparing vkey (public key).
|
|
882
|
+
* Returns the count of newly added signatures.
|
|
883
|
+
*/
|
|
884
|
+
private mergeSignatures(
|
|
885
|
+
target: Core.Transaction,
|
|
886
|
+
source: Core.Transaction,
|
|
887
|
+
): number {
|
|
888
|
+
const targetWs = target.witnessSet();
|
|
889
|
+
const sourceWs = source.witnessSet();
|
|
890
|
+
|
|
891
|
+
const targetVkeys = targetWs.vkeys()?.toCore() ?? [];
|
|
892
|
+
const sourceVkeys = sourceWs.vkeys()?.toCore() ?? [];
|
|
893
|
+
|
|
894
|
+
// Find vkeys in source that aren't in target (by comparing public key)
|
|
895
|
+
const existingPubKeys = new Set(targetVkeys.map(([vkey]) => vkey));
|
|
896
|
+
const newVkeys = sourceVkeys.filter(
|
|
897
|
+
([vkey]) => !existingPubKeys.has(vkey),
|
|
898
|
+
);
|
|
899
|
+
|
|
900
|
+
if (newVkeys.length === 0) {
|
|
901
|
+
return 0;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// Merge the new vkeys into target
|
|
905
|
+
targetWs.setVkeys(
|
|
906
|
+
CborSet.fromCore([...targetVkeys, ...newVkeys], VkeyWitness.fromCore),
|
|
907
|
+
);
|
|
908
|
+
target.setWitnessSet(targetWs);
|
|
909
|
+
|
|
910
|
+
return newVkeys.length;
|
|
911
|
+
}
|
|
912
|
+
|
|
806
913
|
async TxDialog<P extends Provider, W extends Wallet>(
|
|
807
914
|
blaze: Blaze<P, W>,
|
|
808
915
|
tx: Core.Transaction,
|
|
809
|
-
opts?:
|
|
810
|
-
): Promise<
|
|
811
|
-
|
|
916
|
+
opts?: TxDialogOptions,
|
|
917
|
+
): Promise<TxDialogResult> {
|
|
918
|
+
let currentTx = tx;
|
|
812
919
|
let expanded = false;
|
|
920
|
+
let hasSignedThisSession = false;
|
|
921
|
+
|
|
922
|
+
// Check if wallet can sign (is HotWallet)
|
|
923
|
+
const isHotWallet = blaze.wallet instanceof HotWallet;
|
|
813
924
|
|
|
814
|
-
|
|
925
|
+
// Get wallet's vkeys for detecting if already signed
|
|
926
|
+
let walletVkeys: string[] = [];
|
|
927
|
+
if (isHotWallet) {
|
|
928
|
+
try {
|
|
929
|
+
// Sign a dummy to get the wallet's public keys
|
|
930
|
+
// We'll use this to check if wallet has already signed
|
|
931
|
+
const wallet = blaze.wallet as unknown as HotWallet;
|
|
932
|
+
const addresses = await wallet.getUsedAddresses();
|
|
933
|
+
const address = addresses[0];
|
|
934
|
+
if (address) {
|
|
935
|
+
// We can't easily get vkeys without signing, so we'll track after first sign
|
|
936
|
+
}
|
|
937
|
+
} catch {
|
|
938
|
+
// Ignore errors in setup
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
while (true) {
|
|
943
|
+
// Display transaction status
|
|
944
|
+
const txHash = this.getTxBodyHash(currentTx);
|
|
945
|
+
const sigCount = this.countSignatures(currentTx);
|
|
946
|
+
const requiredSigners = this.getRequiredSigners(currentTx);
|
|
947
|
+
|
|
948
|
+
console.log("");
|
|
949
|
+
console.log(`Transaction: ${this.formatHash(txHash)}`);
|
|
950
|
+
if (requiredSigners.length > 0) {
|
|
951
|
+
console.log(`Signatures: ${sigCount} of ${requiredSigners.length} required`);
|
|
952
|
+
console.log("Required signers:");
|
|
953
|
+
for (const signer of requiredSigners) {
|
|
954
|
+
console.log(` - ${this.formatHash(signer)}`);
|
|
955
|
+
}
|
|
956
|
+
} else {
|
|
957
|
+
console.log(`Signatures: ${sigCount}`);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// Display CBOR
|
|
961
|
+
const txCbor = currentTx.toCbor();
|
|
815
962
|
if (expanded) {
|
|
816
963
|
console.log("Transaction CBOR:", txCbor);
|
|
817
964
|
} else {
|
|
818
965
|
console.log("Transaction CBOR:", `${String(txCbor).slice(0, 50)}...`);
|
|
819
966
|
}
|
|
820
967
|
|
|
821
|
-
|
|
968
|
+
// Build dynamic menu choices
|
|
969
|
+
const choices: { name: string; value: string }[] = [];
|
|
822
970
|
|
|
971
|
+
// "Sign with this wallet" - only if HotWallet and hasn't signed this session
|
|
972
|
+
if (isHotWallet && !hasSignedThisSession) {
|
|
973
|
+
choices.push({ name: "Sign with this wallet", value: "sign" });
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// CBOR options
|
|
823
977
|
if (!expanded) {
|
|
824
|
-
|
|
825
|
-
title: "Expand CBOR",
|
|
826
|
-
action: async () => {
|
|
827
|
-
expanded = true;
|
|
828
|
-
await showDialog();
|
|
829
|
-
},
|
|
830
|
-
});
|
|
978
|
+
choices.push({ name: "Expand CBOR", value: "expand" });
|
|
831
979
|
}
|
|
980
|
+
choices.push({ name: "Copy CBOR to clipboard", value: "copy" });
|
|
981
|
+
choices.push({ name: "Import signatures from CBOR", value: "import" });
|
|
832
982
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
} catch (e) {
|
|
841
|
-
console.log("Failed to copy to clipboard, expanding instead.");
|
|
842
|
-
expanded = true;
|
|
843
|
-
await showDialog();
|
|
844
|
-
}
|
|
845
|
-
},
|
|
983
|
+
// Submit and cancel
|
|
984
|
+
choices.push({ name: "Submit transaction", value: "submit" });
|
|
985
|
+
choices.push({ name: "Cancel", value: "cancel" });
|
|
986
|
+
|
|
987
|
+
const selection = await select({
|
|
988
|
+
message: "Select an option:",
|
|
989
|
+
choices,
|
|
846
990
|
});
|
|
847
991
|
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
await opts.beforeSign();
|
|
854
|
-
}
|
|
992
|
+
// Handle selection
|
|
993
|
+
if (selection === "sign") {
|
|
994
|
+
if (opts?.beforeSign) {
|
|
995
|
+
await opts.beforeSign();
|
|
996
|
+
}
|
|
855
997
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
(signer) => signer.toString() === stakeKeyHash,
|
|
874
|
-
);
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
const certs = tx.body().certs();
|
|
878
|
-
const hasCertificates = certs && certs.size() > 0;
|
|
879
|
-
const withdrawals = tx.body().withdrawals();
|
|
880
|
-
const hasWithdrawals = withdrawals && withdrawals.size > 0;
|
|
881
|
-
|
|
882
|
-
if (hasCertificates || hasWithdrawals) {
|
|
883
|
-
needsStakeKey = true;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
998
|
+
// Detect if stake key signature is required
|
|
999
|
+
let needsStakeKey = false;
|
|
1000
|
+
try {
|
|
1001
|
+
const wallet = blaze.wallet as unknown as HotWallet;
|
|
1002
|
+
const addresses = await wallet.getUsedAddresses();
|
|
1003
|
+
const userAddress = addresses[0];
|
|
1004
|
+
if (userAddress) {
|
|
1005
|
+
const stakeCredential = userAddress.asBase()?.getStakeCredential();
|
|
1006
|
+
const stakeKeyHash = stakeCredential?.hash?.toString();
|
|
1007
|
+
|
|
1008
|
+
if (stakeKeyHash) {
|
|
1009
|
+
const reqSigners = currentTx.body().requiredSigners();
|
|
1010
|
+
if (reqSigners) {
|
|
1011
|
+
const signerArray = Array.from(reqSigners.values());
|
|
1012
|
+
needsStakeKey = signerArray.some(
|
|
1013
|
+
(signer) => signer.toString() === stakeKeyHash,
|
|
1014
|
+
);
|
|
886
1015
|
}
|
|
887
1016
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1017
|
+
const certs = currentTx.body().certs();
|
|
1018
|
+
const hasCertificates = certs && certs.size() > 0;
|
|
1019
|
+
const withdrawals = currentTx.body().withdrawals();
|
|
1020
|
+
const hasWithdrawals = withdrawals && withdrawals.size > 0;
|
|
1021
|
+
|
|
1022
|
+
if (hasCertificates || hasWithdrawals) {
|
|
1023
|
+
needsStakeKey = true;
|
|
892
1024
|
}
|
|
893
|
-
} catch (error) {
|
|
894
|
-
console.warn(
|
|
895
|
-
"Could not determine stake key requirement, signing with payment key only.",
|
|
896
|
-
);
|
|
897
|
-
console.warn(`Error: ${(error as Error).message}`);
|
|
898
1025
|
}
|
|
1026
|
+
}
|
|
899
1027
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1028
|
+
if (needsStakeKey) {
|
|
1029
|
+
console.log("Transaction requires stake key signature.");
|
|
1030
|
+
} else {
|
|
1031
|
+
console.log("Transaction requires payment key signature only.");
|
|
1032
|
+
}
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
console.warn(
|
|
1035
|
+
"Could not determine stake key requirement, signing with payment key only.",
|
|
1036
|
+
);
|
|
1037
|
+
console.warn(`Error: ${(error as Error).message}`);
|
|
1038
|
+
}
|
|
908
1039
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1040
|
+
try {
|
|
1041
|
+
if (needsStakeKey) {
|
|
1042
|
+
const wallet = blaze.wallet as unknown as HotWallet;
|
|
1043
|
+
const signed = await wallet.signTransaction(currentTx, true, true);
|
|
1044
|
+
const ws = currentTx.witnessSet();
|
|
1045
|
+
const existingVkeys = ws.vkeys()?.toCore() ?? [];
|
|
1046
|
+
|
|
1047
|
+
const signedKeys = signed.vkeys();
|
|
1048
|
+
if (!signedKeys) {
|
|
1049
|
+
throw new Error(
|
|
1050
|
+
"signTransaction: no signed keys in wallet witness response",
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
915
1053
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
)
|
|
921
|
-
|
|
922
|
-
"signTransaction: some keys were already signed",
|
|
923
|
-
);
|
|
924
|
-
}
|
|
1054
|
+
// Check for duplicates before adding
|
|
1055
|
+
const newSignedKeys = signedKeys.toCore();
|
|
1056
|
+
const existingPubKeys = new Set(existingVkeys.map(([vkey]) => vkey));
|
|
1057
|
+
const uniqueNewKeys = newSignedKeys.filter(
|
|
1058
|
+
([vkey]) => !existingPubKeys.has(vkey),
|
|
1059
|
+
);
|
|
925
1060
|
|
|
1061
|
+
if (uniqueNewKeys.length === 0) {
|
|
1062
|
+
console.log("Wallet has already signed this transaction.");
|
|
1063
|
+
} else {
|
|
926
1064
|
ws.setVkeys(
|
|
927
1065
|
CborSet.fromCore(
|
|
928
|
-
[...
|
|
1066
|
+
[...existingVkeys, ...uniqueNewKeys],
|
|
929
1067
|
VkeyWitness.fromCore,
|
|
930
1068
|
),
|
|
931
1069
|
);
|
|
932
|
-
|
|
933
|
-
|
|
1070
|
+
currentTx.setWitnessSet(ws);
|
|
1071
|
+
console.log(`Added ${uniqueNewKeys.length} signature(s).`);
|
|
1072
|
+
hasSignedThisSession = true;
|
|
1073
|
+
}
|
|
1074
|
+
} else {
|
|
1075
|
+
const signedTx = await blaze.signTransaction(currentTx);
|
|
1076
|
+
// Merge signatures from signed tx into current tx
|
|
1077
|
+
const added = this.mergeSignatures(currentTx, signedTx);
|
|
1078
|
+
if (added > 0) {
|
|
1079
|
+
console.log(`Added ${added} signature(s).`);
|
|
1080
|
+
hasSignedThisSession = true;
|
|
934
1081
|
} else {
|
|
935
|
-
|
|
1082
|
+
console.log("Wallet has already signed this transaction.");
|
|
936
1083
|
}
|
|
1084
|
+
}
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
console.error(`Signing failed: ${(error as Error).message}`);
|
|
1087
|
+
}
|
|
1088
|
+
// Continue loop after signing
|
|
1089
|
+
continue;
|
|
1090
|
+
}
|
|
937
1091
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1092
|
+
if (selection === "expand") {
|
|
1093
|
+
expanded = true;
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
if (selection === "copy") {
|
|
1098
|
+
try {
|
|
1099
|
+
const { default: clipboard } = await import("clipboardy");
|
|
1100
|
+
clipboard.writeSync(String(currentTx.toCbor()));
|
|
1101
|
+
console.log("Transaction CBOR copied to clipboard.");
|
|
1102
|
+
} catch {
|
|
1103
|
+
console.log("Failed to copy to clipboard, expanding instead.");
|
|
1104
|
+
expanded = true;
|
|
1105
|
+
}
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
if (selection === "import") {
|
|
1110
|
+
const cborInput = await input({
|
|
1111
|
+
message: "Paste transaction CBOR (hex):",
|
|
941
1112
|
});
|
|
1113
|
+
|
|
1114
|
+
if (!cborInput || cborInput.trim() === "") {
|
|
1115
|
+
console.log("No CBOR provided.");
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
try {
|
|
1120
|
+
const importedTx = Core.Transaction.fromCbor(
|
|
1121
|
+
TxCBOR(cborInput.trim()),
|
|
1122
|
+
);
|
|
1123
|
+
|
|
1124
|
+
// Validate body hash matches
|
|
1125
|
+
const currentHash = this.getTxBodyHash(currentTx);
|
|
1126
|
+
const importedHash = this.getTxBodyHash(importedTx);
|
|
1127
|
+
|
|
1128
|
+
if (currentHash !== importedHash) {
|
|
1129
|
+
const proceed = await confirm({
|
|
1130
|
+
message: `Warning: Imported transaction has different body hash.\nCurrent: ${this.formatHash(currentHash)}\nImported: ${this.formatHash(importedHash)}\nProceed anyway?`,
|
|
1131
|
+
default: false,
|
|
1132
|
+
});
|
|
1133
|
+
if (!proceed) {
|
|
1134
|
+
console.log("Import cancelled.");
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// Merge signatures
|
|
1140
|
+
const added = this.mergeSignatures(currentTx, importedTx);
|
|
1141
|
+
const sourceVkeys = importedTx.witnessSet().vkeys();
|
|
1142
|
+
const sourceCount = sourceVkeys ? sourceVkeys.size() : 0;
|
|
1143
|
+
const skipped = sourceCount - added;
|
|
1144
|
+
|
|
1145
|
+
if (added > 0) {
|
|
1146
|
+
console.log(`Added ${added} new signature(s).`);
|
|
1147
|
+
}
|
|
1148
|
+
if (skipped > 0) {
|
|
1149
|
+
console.log(`Skipped ${skipped} duplicate signature(s).`);
|
|
1150
|
+
}
|
|
1151
|
+
if (added === 0 && skipped === 0) {
|
|
1152
|
+
console.log("No signatures found in imported transaction.");
|
|
1153
|
+
}
|
|
1154
|
+
} catch (error) {
|
|
1155
|
+
console.error(`Failed to import CBOR: ${(error as Error).message}`);
|
|
1156
|
+
}
|
|
1157
|
+
continue;
|
|
942
1158
|
}
|
|
943
1159
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1160
|
+
if (selection === "submit") {
|
|
1161
|
+
const sigCount = this.countSignatures(currentTx);
|
|
1162
|
+
if (sigCount === 0) {
|
|
1163
|
+
const proceed = await confirm({
|
|
1164
|
+
message: "Warning: Transaction has no signatures. Submit anyway?",
|
|
1165
|
+
default: false,
|
|
1166
|
+
});
|
|
1167
|
+
if (!proceed) {
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
950
1171
|
|
|
951
|
-
|
|
1172
|
+
try {
|
|
1173
|
+
const txId = await blaze.submitTransaction(currentTx);
|
|
1174
|
+
console.log(`Transaction submitted: ${txId}`);
|
|
1175
|
+
return { action: "submitted", txId, tx: currentTx };
|
|
1176
|
+
} catch (error) {
|
|
1177
|
+
console.error(`Submit failed: ${(error as Error).message}`);
|
|
1178
|
+
// Continue loop to allow retry or other actions
|
|
1179
|
+
continue;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
if (selection === "cancel") {
|
|
1184
|
+
if (hasSignedThisSession) {
|
|
1185
|
+
return { action: "signed", tx: currentTx };
|
|
1186
|
+
}
|
|
1187
|
+
return { action: "cancelled", tx: currentTx };
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
952
1190
|
}
|
|
953
1191
|
|
|
954
1192
|
async EditStruct<U extends TSchema>(
|