@@ -121,4 +121,77 @@ export const getSupply = (asset, t) => {
121121 return totalSupply
122122}
123123
124- export const strTruncate = ( str ) => str . substr ( 0 , 10 ) + '...' + str . substr ( str . length - 4 , str . length ) ;
124+ export const strTruncate = ( str ) => str . substr ( 0 , 10 ) + '...' + str . substr ( str . length - 4 , str . length ) ;
125+
126+
127+ // Simplicity helpers (Elements only)
128+
129+ // Helper to check if witness has an annex (per BIP 341)
130+ const hasAnnex = ( witness ) => witness && witness . length >= 2 && witness [ witness . length - 1 ] . startsWith ( '50' )
131+
132+ // Get the control block element from a witness stack (accounting for optional annex)
133+ const getControlBlock = witness => {
134+ if ( ! witness || witness . length < 3 ) return null
135+ const hasAnnexBlock = hasAnnex ( witness )
136+ if ( hasAnnexBlock ) {
137+ return witness [ witness . length - 2 ]
138+ }
139+ return witness [ witness . length - 1 ]
140+ }
141+
142+ // Check if a vin is a P2TR Simplicity spend
143+ // A P2TR Simplicity spend has:
144+ // - 4 witness elements (or 5 with optional annex)
145+ // - Control block starts with 'be' or 'bf' (leaf version for Simplicity)
146+ export const isSimplicitySpend = vin => {
147+ if ( ! process . env . IS_ELEMENTS || ! vin . witness ) return false
148+
149+ const witnessLen = vin . witness . length
150+ const hasAnnexBlock = hasAnnex ( vin . witness )
151+
152+ // Must be 4 elements without annex, or 5 elements with annex
153+ if ( witnessLen !== 4 && ! ( witnessLen === 5 && hasAnnexBlock ) ) return false
154+
155+ const controlBlock = getControlBlock ( vin . witness )
156+ return controlBlock && ( controlBlock . startsWith ( 'be' ) || controlBlock . startsWith ( 'bf' ) )
157+ }
158+
159+ // Extract Simplicity witness components from a vin
160+ // Returns: { witnessData, program, cmr, controlBlock, annex }
161+ export const getSimplicityWitness = vin => {
162+ if ( ! isSimplicitySpend ( vin ) ) return null
163+
164+ const witness = vin . witness
165+ const hasAnnexBlock = hasAnnex ( witness )
166+
167+ if ( hasAnnexBlock && witness . length === 5 ) {
168+ return {
169+ witnessData : witness [ 0 ] ,
170+ program : witness [ 1 ] ,
171+ cmr : witness [ 2 ] ,
172+ controlBlock : witness [ 3 ] ,
173+ annex : witness [ 4 ]
174+ }
175+ }
176+
177+ return {
178+ witnessData : witness [ 0 ] ,
179+ program : witness [ 1 ] ,
180+ cmr : witness [ 2 ] ,
181+ controlBlock : witness [ 3 ] ,
182+ annex : null
183+ }
184+ }
185+
186+ // Convert hex string to base64
187+ export const hexToBase64 = hex => {
188+ if ( process . browser ) {
189+ // Browser environment
190+ const bytes = hex . match ( / .{ 1 , 2 } / g) . map ( byte => parseInt ( byte , 16 ) )
191+ const binary = String . fromCharCode ( ...bytes )
192+ return btoa ( binary )
193+ } else {
194+ // Node environment (for pre-rendering)
195+ return Buffer . from ( hex , 'hex' ) . toString ( 'base64' )
196+ }
197+ }
0 commit comments