@@ -6,10 +6,10 @@ import {
6
6
Signature ,
7
7
TransactionType as BitGoTransactionType ,
8
8
} from '@bitgo/sdk-core' ;
9
- import { SuiProgrammableTransaction , SuiTransaction , SuiTransactionType , TxData } from './iface' ;
9
+ import { SuiProgrammableTransaction , SuiTransaction , SuiTransactionType , TxData , GasData } from './iface' ;
10
10
import { BaseCoin as CoinConfig } from '@bitgo/statics' ;
11
11
import utils , { AppId , Intent , IntentScope , IntentVersion , isImmOrOwnedObj } from './utils' ;
12
- import { GasData , normalizeSuiAddress , normalizeSuiObjectId , SuiObjectRef } from './mystenlab/types' ;
12
+ import { normalizeSuiAddress , normalizeSuiObjectId , SuiObjectRef } from './mystenlab/types' ;
13
13
import { SIGNATURE_SCHEME_BYTES } from './constants' ;
14
14
import { Buffer } from 'buffer' ;
15
15
import { fromB64 , toB64 } from '@mysten/bcs' ;
@@ -20,10 +20,12 @@ import { builder, MergeCoinsTransaction, TransactionType } from './mystenlab/bui
20
20
import blake2b from '@bitgo/blake2b' ;
21
21
import { hashTypedData } from './mystenlab/cryptography/hash' ;
22
22
23
- export abstract class Transaction < T > extends BaseTransaction {
23
+ export abstract class Transaction < T = SuiProgrammableTransaction > extends BaseTransaction {
24
24
protected _suiTransaction : SuiTransaction < T > ;
25
25
protected _signature : Signature ;
26
+ protected _feePayerSignature : Signature ;
26
27
private _serializedSig : Uint8Array ;
28
+ private _serializedFeePayerSig : Uint8Array ;
27
29
28
30
protected constructor ( _coinConfig : Readonly < CoinConfig > ) {
29
31
super ( _coinConfig ) ;
@@ -48,17 +50,31 @@ export abstract class Transaction<T> extends BaseTransaction {
48
50
addSignature ( publicKey : BasePublicKey , signature : Buffer ) : void {
49
51
this . _signatures . push ( signature . toString ( 'hex' ) ) ;
50
52
this . _signature = { publicKey, signature } ;
53
+ this . setSerializedSig ( publicKey , signature ) ;
51
54
this . serialize ( ) ;
52
55
}
53
56
57
+ addFeePayerSignature ( publicKey : BasePublicKey , signature : Buffer ) : void {
58
+ this . _feePayerSignature = { publicKey, signature } ;
59
+ this . setSerializedFeePayerSig ( publicKey , signature ) ;
60
+ }
61
+
54
62
get suiSignature ( ) : Signature {
55
63
return this . _signature ;
56
64
}
57
65
66
+ get feePayerSignature ( ) : Signature {
67
+ return this . _feePayerSignature ;
68
+ }
69
+
58
70
get serializedSig ( ) : Uint8Array {
59
71
return this . _serializedSig ;
60
72
}
61
73
74
+ get serializedFeePayerSig ( ) : Uint8Array {
75
+ return this . _serializedFeePayerSig ;
76
+ }
77
+
62
78
setSerializedSig ( publicKey : BasePublicKey , signature : Buffer ) : void {
63
79
const pubKey = Buffer . from ( publicKey . pub , 'hex' ) ;
64
80
const serialized_sig = new Uint8Array ( 1 + signature . length + pubKey . length ) ;
@@ -68,6 +84,15 @@ export abstract class Transaction<T> extends BaseTransaction {
68
84
this . _serializedSig = serialized_sig ;
69
85
}
70
86
87
+ setSerializedFeePayerSig ( publicKey : BasePublicKey , signature : Buffer ) : void {
88
+ const pubKey = Buffer . from ( publicKey . pub , 'hex' ) ;
89
+ const serialized_sig = new Uint8Array ( 1 + signature . length + pubKey . length ) ;
90
+ serialized_sig . set ( SIGNATURE_SCHEME_BYTES ) ;
91
+ serialized_sig . set ( signature , 1 ) ;
92
+ serialized_sig . set ( pubKey , 1 + signature . length ) ;
93
+ this . _serializedFeePayerSig = serialized_sig ;
94
+ }
95
+
71
96
/** @inheritdoc */
72
97
canSign ( key : BaseKey ) : boolean {
73
98
return true ;
@@ -78,7 +103,6 @@ export abstract class Transaction<T> extends BaseTransaction {
78
103
*
79
104
* @param {KeyPair } signer key
80
105
*/
81
-
82
106
sign ( signer : KeyPair ) : void {
83
107
if ( ! this . _suiTransaction ) {
84
108
throw new InvalidTransactionError ( 'empty transaction to sign' ) ;
@@ -87,16 +111,56 @@ export abstract class Transaction<T> extends BaseTransaction {
87
111
const intentMessage = this . signablePayload ;
88
112
const signature = signer . signMessageinUint8Array ( intentMessage ) ;
89
113
90
- this . setSerializedSig ( { pub : signer . getKeys ( ) . pub } , Buffer . from ( signature ) ) ;
91
114
this . addSignature ( { pub : signer . getKeys ( ) . pub } , Buffer . from ( signature ) ) ;
92
115
}
93
116
117
+ /**
118
+ * Sign this transaction as a fee payer
119
+ *
120
+ * @param {KeyPair } signer key
121
+ */
122
+ signFeePayer ( signer : KeyPair ) : void {
123
+ if ( ! this . _suiTransaction ) {
124
+ throw new InvalidTransactionError ( 'empty transaction to sign' ) ;
125
+ }
126
+
127
+ if (
128
+ ! this . _suiTransaction . gasData ||
129
+ ! ( 'sponsor' in this . _suiTransaction . gasData ) ||
130
+ ! this . _suiTransaction . gasData . sponsor
131
+ ) {
132
+ throw new InvalidTransactionError ( 'transaction does not have a fee payer' ) ;
133
+ }
134
+
135
+ const intentMessage = this . signablePayload ;
136
+ const signature = signer . signMessageinUint8Array ( intentMessage ) ;
137
+
138
+ this . addFeePayerSignature ( { pub : signer . getKeys ( ) . pub } , Buffer . from ( signature ) ) ;
139
+ }
140
+
94
141
/** @inheritdoc */
95
142
toBroadcastFormat ( ) : string {
96
143
if ( ! this . _suiTransaction ) {
97
144
throw new InvalidTransactionError ( 'Empty transaction' ) ;
98
145
}
99
- return this . serialize ( ) ;
146
+
147
+ if ( ! this . _serializedSig ) {
148
+ throw new InvalidTransactionError ( 'Transaction must be signed' ) ;
149
+ }
150
+
151
+ const result = {
152
+ txBytes : this . serialize ( ) ,
153
+ senderSignature : toB64 ( this . _serializedSig ) ,
154
+ } ;
155
+
156
+ if ( this . _suiTransaction . gasData ?. sponsor ) {
157
+ if ( ! this . _serializedFeePayerSig ) {
158
+ throw new InvalidTransactionError ( 'Sponsored transaction must have fee payer signature' ) ;
159
+ }
160
+ result [ 'sponsorSignature' ] = toB64 ( this . _serializedFeePayerSig ) ;
161
+ }
162
+
163
+ return JSON . stringify ( result ) ;
100
164
}
101
165
102
166
/** @inheritdoc */
@@ -165,6 +229,19 @@ export abstract class Transaction<T> extends BaseTransaction {
165
229
const inputs = transactionBlock . inputs . map ( ( txInput ) => txInput . value ) ;
166
230
const transactions = transactionBlock . transactions ;
167
231
const txType = this . getSuiTransactionType ( transactions ) ;
232
+
233
+ const gasData : GasData = {
234
+ payment : this . normalizeCoins ( transactionBlock . gasConfig . payment ! ) ,
235
+ owner : normalizeSuiAddress ( transactionBlock . gasConfig . owner ! ) ,
236
+ price : Number ( transactionBlock . gasConfig . price as string ) ,
237
+ budget : Number ( transactionBlock . gasConfig . budget as string ) ,
238
+ } ;
239
+
240
+ // Only add sponsor if it exists
241
+ if ( transactionBlock . gasConfig . sponsor ) {
242
+ gasData . sponsor = normalizeSuiAddress ( transactionBlock . gasConfig . sponsor ) ;
243
+ }
244
+
168
245
return {
169
246
id : transactionBlock . getDigest ( ) ,
170
247
type : txType ,
@@ -173,12 +250,7 @@ export abstract class Transaction<T> extends BaseTransaction {
173
250
inputs : inputs ,
174
251
transactions : transactions ,
175
252
} ,
176
- gasData : {
177
- payment : this . normalizeCoins ( transactionBlock . gasConfig . payment ! ) ,
178
- owner : normalizeSuiAddress ( transactionBlock . gasConfig . owner ! ) ,
179
- price : Number ( transactionBlock . gasConfig . price as string ) ,
180
- budget : Number ( transactionBlock . gasConfig . budget as string ) ,
181
- } ,
253
+ gasData : gasData ,
182
254
} ;
183
255
}
184
256
@@ -213,12 +285,20 @@ export abstract class Transaction<T> extends BaseTransaction {
213
285
}
214
286
215
287
static getProperGasData ( k : any ) : GasData {
216
- return {
217
- payment : [ this . normalizeSuiObjectRef ( k . gasData . payment ) ] ,
288
+ const gasData : GasData = {
289
+ payment : Array . isArray ( k . gasData . payment )
290
+ ? k . gasData . payment . map ( ( p : any ) => this . normalizeSuiObjectRef ( p ) )
291
+ : [ this . normalizeSuiObjectRef ( k . gasData . payment ) ] ,
218
292
owner : utils . normalizeHexId ( k . gasData . owner ) ,
219
293
price : Number ( k . gasData . price ) ,
220
294
budget : Number ( k . gasData . budget ) ,
221
295
} ;
296
+
297
+ if ( k . gasData . sponsor ) {
298
+ gasData . sponsor = utils . normalizeHexId ( k . gasData . sponsor ) ;
299
+ }
300
+
301
+ return gasData ;
222
302
}
223
303
224
304
private static normalizeCoins ( coins : any [ ] ) : SuiObjectRef [ ] {
@@ -267,4 +347,15 @@ export abstract class Transaction<T> extends BaseTransaction {
267
347
268
348
return inputGasPaymentObjects ;
269
349
}
350
+
351
+ hasFeePayerSig ( ) : boolean {
352
+ return this . _feePayerSignature !== undefined ;
353
+ }
354
+
355
+ getFeePayerPubKey ( ) : string | undefined {
356
+ if ( ! this . _feePayerSignature || ! this . _feePayerSignature . publicKey ) {
357
+ return undefined ;
358
+ }
359
+ return this . _feePayerSignature . publicKey . pub ;
360
+ }
270
361
}
0 commit comments