@@ -44,6 +44,15 @@ export class ChainportMemoMetadata {
4444 return String . fromCharCode ( num - 10 + 'a' . charCodeAt ( 0 ) )
4545 }
4646
47+ public static convertHexToBinary ( encodedHex : string ) : string {
48+ const buffer = Buffer . from ( encodedHex , 'hex' )
49+ let binaryString = ''
50+ for ( let i = 0 ; i < buffer . length ; i ++ ) {
51+ binaryString += buffer [ i ] . toString ( 2 ) . padStart ( 8 , '0' )
52+ }
53+ return binaryString
54+ }
55+
4756 public static encode ( networkId : number , address : string , toIronfish : boolean ) {
4857 if ( address . startsWith ( '0x' ) ) {
4958 address = address . slice ( 2 )
@@ -63,7 +72,86 @@ export class ChainportMemoMetadata {
6372 return hexString . padStart ( 64 , '0' )
6473 }
6574
66- public static decode ( encodedHex : string ) : [ number , string , boolean ] {
75+ public static encodeV2 (
76+ networkId : number ,
77+ address : string ,
78+ toIronfish : boolean ,
79+ timestamp : number ,
80+ version : number ,
81+ ) {
82+ if ( networkId >= 1 << 6 ) {
83+ throw new Error ( 'networkId exceeds 6-bit capacity' )
84+ }
85+ if ( version >= 1 << 2 ) {
86+ throw new Error ( 'version exceeds 2-bit capacity' )
87+ }
88+ if ( BigInt ( timestamp ) >= 1n << 31n ) {
89+ throw new Error ( 'timestamp exceeds 31-bit capacity' )
90+ }
91+
92+ let addressClean = address
93+ if ( addressClean . startsWith ( '0x' ) ) {
94+ addressClean = addressClean . slice ( 2 )
95+ }
96+
97+ if ( addressClean . length !== 40 ) {
98+ throw new Error ( 'address must be 40 hexadecimal characters' )
99+ }
100+
101+ const addrBytes = Buffer . from ( addressClean , 'hex' )
102+
103+ if ( addrBytes . length !== 20 ) {
104+ throw new Error ( 'address must decode to 20 bytes' )
105+ }
106+
107+ const bitArray : number [ ] = new Array ( 256 ) . fill ( 0 ) as number [ ]
108+ let pos = 0
109+
110+ pos += 6
111+
112+ bitArray [ pos ] = toIronfish ? 1 : 0
113+ pos += 1
114+
115+ pos += 1
116+
117+ bitArray [ pos ] = ( version >> 1 ) & 1
118+ bitArray [ pos + 1 ] = version & 1
119+ pos += 2
120+
121+ for ( let i = 0 ; i < 6 ; i ++ ) {
122+ bitArray [ pos + i ] = ( networkId >> ( 5 - i ) ) & 1
123+ }
124+ pos += 6
125+
126+ for ( const byte of addrBytes ) {
127+ for ( let i = 0 ; i < 8 ; i ++ ) {
128+ bitArray [ pos ] = ( byte >> ( 7 - i ) ) & 1
129+ pos += 1
130+ }
131+ }
132+
133+ for ( let i = 0 ; i < 31 ; i ++ ) {
134+ bitArray [ pos + i ] = ( timestamp >> ( 30 - i ) ) & 1
135+ }
136+ pos += 31
137+
138+ pos += 49
139+
140+ const result = new Uint8Array ( 32 )
141+ for ( let i = 0 ; i < 32 ; i ++ ) {
142+ let byte = 0
143+ for ( let j = 0 ; j < 8 ; j ++ ) {
144+ byte = ( byte << 1 ) | bitArray [ i * 8 + j ]
145+ }
146+ result [ i ] = byte
147+ }
148+
149+ return Array . from ( result )
150+ . map ( ( byte ) => byte . toString ( 16 ) . padStart ( 2 , '0' ) )
151+ . join ( '' )
152+ }
153+
154+ public static decodeV1 ( encodedHex : string ) : [ number , string , boolean ] {
67155 const hexInteger = BigInt ( '0x' + encodedHex )
68156 const encodedString = hexInteger . toString ( 2 )
69157 const padded = encodedString . padStart ( 250 , '0' )
@@ -82,4 +170,32 @@ export class ChainportMemoMetadata {
82170
83171 return [ networkId , address . toLowerCase ( ) , toIronfish ]
84172 }
173+
174+ public static decodeV2 ( encodedHex : string ) : [ number , string , boolean ] {
175+ const bits = this . convertHexToBinary ( encodedHex )
176+ const toIronfish = bits [ 6 ] === '1'
177+ const memoHexVersion = bits . slice ( 8 , 10 )
178+ if ( memoHexVersion !== '01' ) {
179+ throw new Error ( `Unexpected memoHex version: ${ memoHexVersion } ` )
180+ }
181+
182+ const networkIdBits = bits . slice ( 10 , 16 )
183+ const networkId = parseInt ( networkIdBits , 2 )
184+ const addressBits = bits . slice ( 16 , 176 )
185+ let address = '0x'
186+ for ( let i = 0 ; i < addressBits . length ; i += 4 ) {
187+ address += parseInt ( addressBits . slice ( i , i + 4 ) , 2 ) . toString ( 16 )
188+ }
189+
190+ return [ networkId , address . toLowerCase ( ) , toIronfish ]
191+ }
192+
193+ public static decode ( encodedHex : string ) : [ number , string , boolean ] {
194+ const bits = this . convertHexToBinary ( encodedHex )
195+ const memoHexVersion = bits . slice ( 8 , 10 )
196+ if ( memoHexVersion === '01' ) {
197+ return this . decodeV2 ( encodedHex )
198+ }
199+ return this . decodeV1 ( encodedHex )
200+ }
85201}
0 commit comments