3
3
using System . Security . Cryptography ;
4
4
using System . Text ;
5
5
using System . Text . RegularExpressions ;
6
+ using ADRaffy . ENSNormalize ;
6
7
using Nethereum . ABI . EIP712 ;
8
+ using Nethereum . ABI . FunctionEncoding ;
9
+ using Nethereum . ABI . FunctionEncoding . Attributes ;
10
+ using Nethereum . ABI . Model ;
7
11
using Nethereum . Contracts ;
8
12
using Nethereum . Hex . HexConvertors . Extensions ;
9
13
using Nethereum . Signer ;
@@ -20,6 +24,7 @@ public static partial class Utils
20
24
{
21
25
private static readonly Dictionary < BigInteger , bool > _eip155EnforcedCache = new ( ) ;
22
26
private static readonly Dictionary < BigInteger , ThirdwebChainData > _chainDataCache = new ( ) ;
27
+ private static readonly Dictionary < string , string > _ensCache = new ( ) ;
23
28
24
29
/// <summary>
25
30
/// Computes the client ID from the given secret key.
@@ -101,7 +106,7 @@ public static byte[] HashMessage(this byte[] messageBytes)
101
106
/// <returns>The hashed message.</returns>
102
107
public static string HashMessage ( this string message )
103
108
{
104
- return Sha3Keccack . Current . CalculateHash ( Encoding . UTF8 . GetBytes ( message ) ) . ToHex ( true ) ;
109
+ return Sha3Keccack . Current . CalculateHash ( message ) ;
105
110
}
106
111
107
112
/// <summary>
@@ -682,4 +687,152 @@ public static bool IsEip1559Supported(string chainId)
682
687
return true ;
683
688
}
684
689
}
690
+
691
+ #if NET8_0_OR_GREATER
692
+ [ GeneratedRegex ( "^\\ .|\\ .$" ) ]
693
+ private static partial Regex PacketRegex ( ) ;
694
+ #endif
695
+
696
+ public static byte [ ] PacketToBytes ( string packet )
697
+ {
698
+ #if ! NET8_0_OR_GREATER
699
+ var value = new Regex ( "^\\ .|\\ .$" ) . Replace ( packet , "" ) ;
700
+ #else
701
+ var value = PacketRegex ( ) . Replace ( packet , "" ) ;
702
+ #endif
703
+ if ( string . IsNullOrEmpty ( value ) )
704
+ {
705
+ return new byte [ ] { 0 } ;
706
+ }
707
+
708
+ var labels = value . Split ( "." ) ;
709
+ using var memoryStream = new MemoryStream ( ) ;
710
+ foreach ( var label in labels )
711
+ {
712
+ byte [ ] labelBytes ;
713
+
714
+ if ( label . Length > 63 )
715
+ {
716
+ labelBytes = label . HashMessage ( ) . HexToBytes ( ) ;
717
+ }
718
+ else
719
+ {
720
+ labelBytes = Encoding . UTF8 . GetBytes ( label ) ;
721
+ }
722
+
723
+ if ( labelBytes . Length > 255 )
724
+ {
725
+ throw new ArgumentException ( "Label is too long after encoding." ) ;
726
+ }
727
+
728
+ memoryStream . WriteByte ( ( byte ) labelBytes . Length ) ;
729
+ memoryStream . Write ( labelBytes , 0 , labelBytes . Length ) ;
730
+ }
731
+
732
+ memoryStream . WriteByte ( 0 ) ;
733
+ return memoryStream . ToArray ( ) ;
734
+ }
735
+
736
+ public static async Task < string > GetENSFromAddress ( ThirdwebClient client , string address )
737
+ {
738
+ if ( string . IsNullOrEmpty ( address ) )
739
+ {
740
+ throw new ArgumentNullException ( nameof ( address ) ) ;
741
+ }
742
+
743
+ if ( ! address . IsValidAddress ( ) )
744
+ {
745
+ throw new ArgumentException ( "Invalid address." ) ;
746
+ }
747
+
748
+ if ( _ensCache . TryGetValue ( address , out var value ) )
749
+ {
750
+ return value ;
751
+ }
752
+
753
+ var contract = await ThirdwebContract . Create ( client : client , address : Constants . ENS_REGISTRY_ADDRESS , chain : 1 ) . ConfigureAwait ( false ) ;
754
+ var reverseName = address . ToLower ( ) [ 2 ..] + ".addr.reverse" ;
755
+ var reverseNameBytes = PacketToBytes ( reverseName ) ;
756
+ var ensName = await contract . Read < string > ( "reverse" , reverseNameBytes ) . ConfigureAwait ( false ) ;
757
+ _ensCache [ address ] = ensName ;
758
+ return ensName ;
759
+ }
760
+
761
+ public static async Task < string > GetAddressFromENS ( ThirdwebClient client , string ensName )
762
+ {
763
+ if ( string . IsNullOrEmpty ( ensName ) )
764
+ {
765
+ throw new ArgumentNullException ( nameof ( ensName ) ) ;
766
+ }
767
+
768
+ if ( ensName . IsValidAddress ( ) )
769
+ {
770
+ return ensName . ToChecksumAddress ( ) ;
771
+ }
772
+
773
+ if ( ! ensName . Contains ( '.' ) )
774
+ {
775
+ throw new ArgumentException ( "Invalid ENS name." ) ;
776
+ }
777
+
778
+ if ( _ensCache . TryGetValue ( ensName , out var value ) )
779
+ {
780
+ return value ;
781
+ }
782
+
783
+ var registry = await ThirdwebContract . Create ( client : client , address : Constants . ENS_REGISTRY_ADDRESS , chain : 1 ) . ConfigureAwait ( false ) ;
784
+ var functionCallEncoder = new FunctionCallEncoder ( ) ;
785
+ var encodedAddr = "addr(bytes32)"
786
+ . HashMessage ( )
787
+ . HexToBytes ( )
788
+ . Take ( 4 )
789
+ . ToArray ( )
790
+ . Concat ( functionCallEncoder . EncodeParameters ( new Parameter [ ] { new ( "bytes32" , "name" ) } , new object [ ] { NameHash ( ensName ) . HexToBytes ( ) } ) )
791
+ . ToArray ( ) ;
792
+ var result = await registry . Read < ResolveReturnType > ( "resolve(bytes name, bytes data)" , PacketToBytes ( ensName ) , encodedAddr ) . ConfigureAwait ( false ) ;
793
+ var address = ( "0x" + result . Bytes . BytesToHex ( ) [ 26 ..] ) . ToChecksumAddress ( ) ;
794
+ _ensCache [ ensName ] = address ;
795
+ return address ;
796
+ }
797
+
798
+ public static bool IsValidAddress ( this string address )
799
+ {
800
+ if ( string . IsNullOrEmpty ( address ) )
801
+ {
802
+ return false ;
803
+ }
804
+
805
+ if ( address . StartsWith ( "0x" ) && address . Length == 42 && ! address . Contains ( '.' ) )
806
+ {
807
+ return true ;
808
+ }
809
+
810
+ return false ;
811
+ }
812
+
813
+ [ FunctionOutput ]
814
+ private class ResolveReturnType
815
+ {
816
+ [ Parameter ( "bytes" , "" , 1 ) ]
817
+ public byte [ ] Bytes { get ; set ; }
818
+
819
+ [ Parameter ( "address" , "" , 2 ) ]
820
+ public string Address { get ; set ; }
821
+ }
822
+
823
+ private static string NameHash ( string name )
824
+ {
825
+ var node = "0x0000000000000000000000000000000000000000000000000000000000000000" ;
826
+ if ( ! string . IsNullOrEmpty ( name ) )
827
+ {
828
+ name = ENSNormalize . ENSIP15 . Normalize ( name ) ;
829
+ var labels = name . Split ( '.' ) ;
830
+ for ( var i = labels . Length - 1 ; i >= 0 ; i -- )
831
+ {
832
+ var byteInput = ( node + labels [ i ] . HashMessage ( ) ) . HexToByteArray ( ) ;
833
+ node = byteInput . HashMessage ( ) . BytesToHex ( ) ;
834
+ }
835
+ }
836
+ return node . EnsureHexPrefix ( ) ;
837
+ }
685
838
}
0 commit comments