@@ -109,6 +109,11 @@ export interface TransactionLike<A = string> {
109109 */
110110 accessList ?: null | AccessListish ;
111111
112+ /**
113+ * The blob version (see [[link-eip-7594]]).
114+ */
115+ blobVersion ?: null | number ;
116+
112117 /**
113118 * The maximum fee per blob gas (see [[link-eip-4844]]).
114119 */
@@ -147,7 +152,7 @@ export interface TransactionLike<A = string> {
147152 */
148153export interface Blob {
149154 data : string ;
150- proof : string ;
155+ proof : string | string [ ] ;
151156 commitment : string ;
152157}
153158
@@ -160,7 +165,7 @@ export interface Blob {
160165 */
161166export type BlobLike = BytesLike | {
162167 data : BytesLike ;
163- proof : BytesLike ;
168+ proof : BytesLike | BytesLike [ ] ;
164169 commitment : BytesLike ;
165170} ;
166171
@@ -170,6 +175,7 @@ export type BlobLike = BytesLike | {
170175 */
171176export interface KzgLibrary {
172177 blobToKzgCommitment : ( blob : Uint8Array ) => Uint8Array ;
178+ computeCellsAndKzgProofs ( blob : Uint8Array ) : [ Uint8Array [ ] , Uint8Array [ ] ] ;
173179 computeBlobKzgProof : ( blob : Uint8Array , commitment : Uint8Array ) => Uint8Array ;
174180}
175181
@@ -238,7 +244,15 @@ function getKzgLibrary(kzg: KzgLibraryLike): KzgLibrary {
238244 assertArgument ( false , "unsupported KZG library" , "kzg" , kzg ) ;
239245 } ;
240246
241- return { blobToKzgCommitment, computeBlobKzgProof } ;
247+ const computeCellsAndKzgProofs = ( blob : Uint8Array ) : [ Uint8Array [ ] , Uint8Array [ ] ] => {
248+ if ( "computeCellsAndKzgProofs" in kzg && typeof kzg . computeCellsAndKzgProofs === "function" ) {
249+ return kzg . computeCellsAndKzgProofs ( blob ) ;
250+ }
251+
252+ assertArgument ( false , "unsupported KZG library" , "kzg" , kzg ) ;
253+ } ;
254+
255+ return { blobToKzgCommitment, computeBlobKzgProof, computeCellsAndKzgProofs } ;
242256}
243257
244258function getVersionedHash ( version : number , hash : BytesLike ) : string {
@@ -562,23 +576,53 @@ function _parseEip4844(data: Uint8Array): TransactionLike {
562576 let typeName = "3" ;
563577
564578 let blobs : null | Array < Blob > = null ;
579+ let blobVersion : null | number = null ;
565580
566581 // Parse the network format
567- if ( fields . length === 4 && Array . isArray ( fields [ 0 ] ) ) {
582+ if ( ( fields . length === 4 || fields . length === 5 ) && Array . isArray ( fields [ 0 ] ) ) {
568583 typeName = "3 (network format)" ;
569- const fBlobs = fields [ 1 ] , fCommits = fields [ 2 ] , fProofs = fields [ 3 ] ;
570- assertArgument ( Array . isArray ( fBlobs ) , "invalid network format: blobs not an array" , "fields[1]" , fBlobs ) ;
571- assertArgument ( Array . isArray ( fCommits ) , "invalid network format: commitments not an array" , "fields[2]" , fCommits ) ;
572- assertArgument ( Array . isArray ( fProofs ) , "invalid network format: proofs not an array" , "fields[3]" , fProofs ) ;
573- assertArgument ( fBlobs . length === fCommits . length , "invalid network format: blobs/commitments length mismatch" , "fields" , fields ) ;
574- assertArgument ( fBlobs . length === fProofs . length , "invalid network format: blobs/proofs length mismatch" , "fields" , fields ) ;
575-
584+ blobVersion = fields . length === 5 ? handleNumber ( fields [ 1 ] , "blobVersion" ) : null ;
585+
586+ const fBlobs = fields . length === 5 ? fields [ 2 ] : fields [ 1 ] ,
587+ fCommits = fields . length === 5 ? fields [ 3 ] : fields [ 2 ] ,
588+ fProofs = fields . length === 5 ? fields [ 4 ] : fields [ 3 ] ;
589+
590+ assertArgument (
591+ blobVersion === null || blobVersion === 1 ,
592+ "invalid network format: unsupported blobVersion" ,
593+ "fields[1]" ,
594+ blobVersion
595+ ) ;
596+ assertArgument (
597+ Array . isArray ( fBlobs ) ,
598+ "invalid network format: blobs not an array" ,
599+ fields . length === 5 ? "fields[2]" : "fields[1]" ,
600+ fBlobs
601+ ) ;
602+ assertArgument (
603+ Array . isArray ( fCommits ) ,
604+ "invalid network format: commitments not an array" ,
605+ fields . length === 5 ? "fields[3]" : "fields[2]" ,
606+ fCommits
607+ ) ;
608+ assertArgument (
609+ Array . isArray ( fProofs ) ,
610+ "invalid network format: proofs not an array" ,
611+ fields . length === 5 ? "fields[4]" : "fields[3]" ,
612+ fProofs
613+ ) ;
614+ assertArgument (
615+ ( blobVersion === null && fBlobs . length === fProofs . length ) || ( blobVersion === 1 && fBlobs . length * 128 === fProofs . length ) ,
616+ "invalid network format: blobs/proofs length mismatch" ,
617+ "fields" ,
618+ fields
619+ ) ;
576620 blobs = [ ] ;
577- for ( let i = 0 ; i < fields [ 1 ] . length ; i ++ ) {
621+ for ( let i = 0 ; i < fBlobs . length ; i ++ ) {
578622 blobs . push ( {
579623 data : fBlobs [ i ] ,
580624 commitment : fCommits [ i ] ,
581- proof : fProofs [ i ] ,
625+ proof : blobVersion === null ? fProofs [ i ] : fProofs . slice ( i * 128 , ( i + 1 ) * 128 ) ,
582626 } ) ;
583627 }
584628
@@ -601,7 +645,8 @@ function _parseEip4844(data: Uint8Array): TransactionLike {
601645 data : hexlify ( fields [ 7 ] ) ,
602646 accessList : handleAccessList ( fields [ 8 ] , "accessList" ) ,
603647 maxFeePerBlobGas : handleUint ( fields [ 9 ] , "maxFeePerBlobGas" ) ,
604- blobVersionedHashes : fields [ 10 ]
648+ blobVersionedHashes : fields [ 10 ] ,
649+ blobVersion,
605650 } ;
606651
607652 if ( blobs ) { tx . blobs = blobs ; }
@@ -647,14 +692,21 @@ function _serializeEip4844(tx: Transaction, sig: null | Signature, blobs: null |
647692
648693 // We have blobs; return the network wrapped format
649694 if ( blobs ) {
695+ const withBlob : Array < any > = [ fields ]
696+
697+ // Add blobVersion if it's explicitly set
698+ if ( tx . blobVersion != null && tx . blobVersion != 0 ) {
699+ withBlob . push ( formatNumber ( tx . blobVersion , "blobVersion" ) ) ;
700+ }
701+
702+ withBlob . push (
703+ blobs . map ( ( b ) => b . data ) ,
704+ blobs . map ( ( b ) => b . commitment ) ,
705+ blobs . flatMap ( ( b ) => Array . isArray ( b . proof ) ? b . proof : [ b . proof ] )
706+ ) ;
650707 return concat ( [
651708 "0x03" ,
652- encodeRlp ( [
653- fields ,
654- blobs . map ( ( b ) => b . data ) ,
655- blobs . map ( ( b ) => b . commitment ) ,
656- blobs . map ( ( b ) => b . proof ) ,
657- ] )
709+ encodeRlp ( withBlob ) ,
658710 ] ) ;
659711 }
660712
@@ -741,6 +793,7 @@ export class Transaction implements TransactionLike<string> {
741793 #chainId: bigint ;
742794 #sig: null | Signature ;
743795 #accessList: null | AccessList ;
796+ #blobVersion: null | number ;
744797 #maxFeePerBlobGas: null | bigint ;
745798 #blobVersionedHashes: null | Array < string > ;
746799 #kzg: null | KzgLibrary ;
@@ -933,6 +986,30 @@ export class Transaction implements TransactionLike<string> {
933986 authorizationify ( a ) ) ;
934987 }
935988
989+ /**
990+ * The blob version for Cancun transactions.
991+ */
992+ get blobVersion ( ) : null | number {
993+ const value = this . #blobVersion;
994+ if ( value == null && this . type === 3 ) {
995+ return 0 ;
996+ }
997+ return value ;
998+ }
999+ set blobVersion ( value : null | number ) {
1000+ if ( value == null ) {
1001+ this . #blobVersion = null ;
1002+ return ;
1003+ }
1004+ assertArgument (
1005+ Number . isInteger ( value ) && value >= 0 && value <= 1 ,
1006+ "blobVersion must be an integer between 0 and 1" ,
1007+ "blobVersion" ,
1008+ value
1009+ ) ;
1010+ this . #blobVersion = value ;
1011+ }
1012+
9361013 /**
9371014 * The max fee per blob gas for Cancun transactions.
9381015 */
@@ -1025,8 +1102,14 @@ export class Transaction implements TransactionLike<string> {
10251102 }
10261103
10271104 const commit = this . #kzg. blobToKzgCommitment ( data ) ;
1028- const proof = hexlify ( this . #kzg. computeBlobKzgProof ( data , commit ) ) ;
1029-
1105+ let proof : string | string [ ] ;
1106+ // Encode proof based on version
1107+ if ( this . #blobVersion === 1 ) {
1108+ const [ , _proof ] = this . #kzg. computeCellsAndKzgProofs ( data ) ;
1109+ proof = _proof . map ( ( p ) => hexlify ( p ) ) ;
1110+ } else {
1111+ proof = hexlify ( this . #kzg. computeBlobKzgProof ( data , commit ) ) ;
1112+ }
10301113 blobs . push ( {
10311114 data : hexlify ( data ) ,
10321115 commitment : hexlify ( commit ) ,
@@ -1039,7 +1122,9 @@ export class Transaction implements TransactionLike<string> {
10391122 blobs . push ( {
10401123 data : hexlify ( blob . data ) ,
10411124 commitment : commit ,
1042- proof : hexlify ( blob . proof )
1125+ proof : Array . isArray ( blob . proof )
1126+ ? blob . proof . map ( ( p ) => hexlify ( p ) )
1127+ : hexlify ( blob . proof ) ,
10431128 } ) ;
10441129 versionedHashes . push ( getVersionedHash ( 1 , commit ) ) ;
10451130 }
@@ -1074,6 +1159,7 @@ export class Transaction implements TransactionLike<string> {
10741159 this . #chainId = BN_0 ;
10751160 this . #sig = null ;
10761161 this . #accessList = null ;
1162+ this . #blobVersion = null ;
10771163 this . #maxFeePerBlobGas = null ;
10781164 this . #blobVersionedHashes = null ;
10791165 this . #kzg = null ;
@@ -1359,6 +1445,10 @@ export class Transaction implements TransactionLike<string> {
13591445 // This will get overwritten by blobs, if present
13601446 if ( tx . blobVersionedHashes != null ) { result . blobVersionedHashes = tx . blobVersionedHashes ; }
13611447
1448+ if ( tx . blobVersion != null ) {
1449+ result . blobVersion = tx . blobVersion ;
1450+ }
1451+
13621452 // Make sure we assign the kzg before assigning blobs, which
13631453 // require the library in the event raw blob data is provided.
13641454 if ( tx . kzg != null ) { result . kzg = tx . kzg ; }
0 commit comments