@@ -17,7 +17,8 @@ import {
17
17
makeStandardFungiblePostCondition , makeStandardNonFungiblePostCondition , makeStandardSTXPostCondition , makeSTXTokenTransfer ,
18
18
makeUnsignedContractCall , makeUnsignedContractDeploy , makeUnsignedSTXTokenTransfer , SignedTokenTransferOptions , sponsorTransaction , TxBroadcastResult ,
19
19
TxBroadcastResultOk ,
20
- TxBroadcastResultRejected
20
+ TxBroadcastResultRejected ,
21
+ estimateTransactionByteLength
21
22
} from '../src/builders' ;
22
23
import { bufferCV , bufferCVFromString , serializeCV , standardPrincipalCV } from '../src/clarity' ;
23
24
import { createMessageSignature } from '../src/common' ;
@@ -1061,6 +1062,118 @@ test('Estimate transaction transfer fee', async () => {
1061
1062
expect ( resultEstimateFee2 . map ( f => f . fee ) ) . toEqual ( [ 140 , 17 , 125 ] ) ;
1062
1063
} ) ;
1063
1064
1065
+ test ( 'Single-sig transaction byte length must include signature' , async ( ) => {
1066
+ /*
1067
+ * *** Context ***
1068
+ * 1) Single-sig transaction byte length remain same due to empty message signature which allocates the space for signature
1069
+ * 2) estimateTransactionByteLength should correctly estimate the byte length of single-sig transaction
1070
+ */
1071
+
1072
+ const recipient = standardPrincipalCV ( 'SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159' ) ;
1073
+ const amount = 2500000 ;
1074
+ const fee = 100 ;
1075
+ const nonce = 10 ;
1076
+ const memo = 'test memo...' ;
1077
+
1078
+ const privateKey = 'a5c61c6ca7b3e7e55edee68566aeab22e4da26baa285c7bd10e8d2218aa3b229' ;
1079
+ const publicKey = '027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69' ;
1080
+
1081
+ // Create a unsigned single-sig transaction
1082
+ const unsignedTransaction = await makeUnsignedSTXTokenTransfer ( {
1083
+ recipient,
1084
+ amount,
1085
+ fee,
1086
+ nonce,
1087
+ memo : memo ,
1088
+ publicKey : publicKey ,
1089
+ anchorMode : AnchorMode . Any
1090
+ } ) ;
1091
+
1092
+ // Due to empty message signature space will be allocated for signature
1093
+ expect ( unsignedTransaction . serialize ( ) . byteLength ) . toEqual ( estimateTransactionByteLength ( unsignedTransaction ) ) ;
1094
+
1095
+ const signer = new TransactionSigner ( unsignedTransaction ) ;
1096
+ // Now sign the transaction and verify the byteLength after adding signature
1097
+ signer . signOrigin ( createStacksPrivateKey ( privateKey ) ) ;
1098
+
1099
+ const finalSerializedTx = signer . transaction . serialize ( ) ;
1100
+
1101
+ // Byte length will remains the same after signing due to pre allocated space by empty message signature
1102
+ expect ( finalSerializedTx . byteLength ) . toEqual ( estimateTransactionByteLength ( signer . transaction ) ) ;
1103
+ } ) ;
1104
+
1105
+ test ( 'Multi-sig transaction byte length must include the required signatures' , async ( ) => {
1106
+ /*
1107
+ * *** Context ***
1108
+ * 1) Multi-sig transaction byte length increases by adding signatures
1109
+ * which causes the incorrect fee estimation because the fee value is set while creating unsigned transaction
1110
+ * 2) estimateTransactionByteLength should correctly estimate the byte length of multi-sig transaction
1111
+ */
1112
+
1113
+ const recipient = standardPrincipalCV ( 'SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159' ) ;
1114
+ const amount = 2500000 ;
1115
+ const fee = 100 ;
1116
+ const nonce = 10 ;
1117
+ const memo = 'test memo...' ;
1118
+
1119
+ const privKeyStrings = [
1120
+ '6d430bb91222408e7706c9001cfaeb91b08c2be6d5ac95779ab52c6b431950e001' ,
1121
+ '2a584d899fed1d24e26b524f202763c8ab30260167429f157f1c119f550fa6af01' ,
1122
+ 'd5200dee706ee53ae98a03fba6cf4fdcc5084c30cfa9e1b3462dcdeaa3e0f1d201' ,
1123
+ ] ;
1124
+ const privKeys = privKeyStrings . map ( createStacksPrivateKey ) ;
1125
+
1126
+ const pubKeys = privKeyStrings . map ( pubKeyfromPrivKey ) ;
1127
+ const pubKeyStrings = pubKeys . map ( publicKeyToString ) ;
1128
+
1129
+ // Create a unsigned multi-sig transaction
1130
+ const transaction = await makeUnsignedSTXTokenTransfer ( {
1131
+ recipient,
1132
+ amount,
1133
+ fee,
1134
+ nonce,
1135
+ memo : memo ,
1136
+ numSignatures : 3 ,
1137
+ publicKeys : pubKeyStrings ,
1138
+ anchorMode : AnchorMode . Any
1139
+ } ) ;
1140
+
1141
+ // Total length without signatures
1142
+ const unsignedByteLength = 120 ;
1143
+
1144
+ const serializedTx = transaction . serialize ( ) ;
1145
+ // Unsigned transaction byte length without signatures
1146
+ expect ( serializedTx . byteLength ) . toEqual ( unsignedByteLength ) ;
1147
+
1148
+ // Expected final byte length after adding the required signatures
1149
+ const expectedFinalLength = 318 ;
1150
+
1151
+ // estimatedLen includes the space required for signatures in case of multi-sig transaction
1152
+ expect ( estimateTransactionByteLength ( transaction ) ) . toEqual ( expectedFinalLength ) ;
1153
+
1154
+ // Now add the required signatures one by one and test it against the value returned by estimateTransactionByteLength
1155
+ const signer = new TransactionSigner ( transaction ) ;
1156
+
1157
+ signer . signOrigin ( privKeys [ 0 ] ) ;
1158
+ // Should calculate correct length if transaction is partially signed
1159
+ expect ( estimateTransactionByteLength ( signer . transaction ) ) . toEqual ( expectedFinalLength ) ;
1160
+
1161
+ signer . signOrigin ( privKeys [ 1 ] ) ;
1162
+ // Should calculate correct length if transaction is partially signed
1163
+ expect ( estimateTransactionByteLength ( signer . transaction ) ) . toEqual ( expectedFinalLength ) ;
1164
+
1165
+ signer . signOrigin ( privKeys [ 2 ] ) ;
1166
+ // Should calculate correct length if transaction is completely signed
1167
+ expect ( estimateTransactionByteLength ( signer . transaction ) ) . toEqual ( expectedFinalLength ) ;
1168
+
1169
+ const finalSerializedTx = signer . transaction . serialize ( ) ;
1170
+ // Validate expectedFinalLength is correct
1171
+ expect ( finalSerializedTx . byteLength ) . toEqual ( expectedFinalLength ) ;
1172
+
1173
+ // Final byte length should match as estimated by estimateTransactionByteLength
1174
+ expect ( finalSerializedTx . byteLength ) . toEqual ( estimateTransactionByteLength ( signer . transaction ) ) ;
1175
+ } ) ;
1176
+
1064
1177
test ( 'Make STX token transfer with fetch account nonce' , async ( ) => {
1065
1178
const nonce = 123 ;
1066
1179
const recipient = standardPrincipalCV ( 'SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159' ) ;
0 commit comments