Skip to content

Commit

Permalink
Refactor EIP-712 signatures
Browse files Browse the repository at this point in the history
Add support for using Nethereum for EIP-712 serialization. Upgrade Nethereum to 4.8.0
  • Loading branch information
AddressXception committed Sep 8, 2022
1 parent a4824c8 commit 945e139
Show file tree
Hide file tree
Showing 16 changed files with 407 additions and 111 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Examples/console/bin/Debug/net6.0/WalletConnectSharp.Examples.dll",
"args": ["nethereum_send_tx_example"],
"args": ["sign_typed_data_example"],
"cwd": "${workspaceFolder}/Examples/console",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
Expand Down
10 changes: 6 additions & 4 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Nethereum.JsonRpc.RpcClient" Version="4.7.0" />
<PackageVersion Include="Nethereum.RLP" Version="4.7.0" />
<PackageVersion Include="Nethereum.RPC" Version="4.7.0" />
<PackageVersion Include="Nethereum.Web3" Version="4.7.0" />
<PackageVersion Include="Nethereum.JsonRpc.RpcClient" Version="4.8.0" />
<PackageVersion Include="Nethereum.RLP" Version="4.8.0" />
<PackageVersion Include="Nethereum.RPC" Version="4.8.0" />
<PackageVersion Include="Nethereum.Web3" Version="4.8.0" />
<PackageVersion Include="Nethereum.Signer" Version="4.8.0" />
<PackageVersion Include="Nethereum.Signer.EIP712" Version="4.8.0" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
Expand Down
168 changes: 168 additions & 0 deletions Examples/console/Examples/SignTypedDataExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using System.Numerics;
using System.Diagnostics;
using Nethereum.ABI.FunctionEncoding.Attributes;
using Nethereum.Signer.EIP712;
using Newtonsoft.Json;
using WalletConnectSharp.Core.Models;
using WalletConnectSharp.Core.Models.Ethereum.Types;
using WalletConnectSharp.Desktop;
using WalletConnectSharp.NEthereum;
using Nethereum.ABI.EIP712;

namespace WalletConnectSharp.Examples.Examples;

[Struct("Message")]
public class Message
{
[EvmType("string", "stringValue", 1)]
[JsonProperty("stringValue", Order = 1)]
[Parameter("string", "stringValue", 1)]
public string StringValue { get; set; }

[EvmType("address", "addressValue", 2)]
[JsonProperty("addressValue", Order = 2)]
[Parameter("address", "addressValue", 2)]
public string AddressValue { get; set; }

[EvmType("uint8", "uint8Value", 3)]
[JsonProperty("uint8Value", Order = 3)]
[Parameter("uint8", "uint8Value", 3)]
public ushort Uint8Value { get; set; }

[EvmType("uint64", "uint64Value", 4)]
[JsonProperty("uint64Value", Order = 4)]
[Parameter("uint64", "uint64Value", 4)]
public ulong Uint64Value { get; set; }

[EvmType("uint256", "uint256Value", 5)]
[JsonProperty("uint256Value", Order = 5)]
[Parameter("uint256", "uint256Value", 5)]
public ulong Uint256Value { get; set; }
}

/// <summary>
/// Example demonstrating different methods to sign typed data.
/// </summary>
public class SignTypedDataExample : IExample
{

public string Name => "sign_typed_data_example";

public const string VerifyingContractName = "Some Contract Name";
public const string VerifyingContractVersion = "1.0";
public static BigInteger VerifyingContractChainId => new(1);
public const string VerifyingContractAddress = "0x0000000000000000000000000000000000000000";

public static EIP712Domain WalletConnectDomain => new()
{
Name = VerifyingContractName,
Version = VerifyingContractVersion,
ChainId = VerifyingContractChainId,
VerifyingContract = VerifyingContractAddress
};

public static Domain NethereumDomain => new()
{
Name = VerifyingContractName,
Version = VerifyingContractVersion,
ChainId = VerifyingContractChainId,
VerifyingContract = VerifyingContractAddress
};

public TypedData<Domain> NethereumSchema => new()
{
Domain = NethereumDomain,
Types = MemberDescriptionFactory.GetTypesMemberDescription(
typeof(Domain),
typeof(Message)
),
PrimaryType = nameof(Message),
};

private static Eip712TypedDataSigner _signer => new();


public async Task Execute(string[] args)
{
// create client metadata
var clientMeta = new ClientMeta()
{
Name = "WalletConnectSharp",
Description = "An example that showcases how to use the WalletConnectSharp library",
Icons = new[] { "https://app.warriders.com/favicon.ico" },
URL = "https://app.warriders.com/"
};

// create a client
var client = new WalletConnect(clientMeta);

Console.WriteLine("Connect using the following URL");
Console.WriteLine(client.URI);

await client.Connect();

var account = Nethereum.Util.AddressUtil.Current.ConvertToChecksumAddress(client.Accounts[0]);

Console.WriteLine("The account " + account + " has connected!");

// create a message to sign
var message = new Message
{
StringValue = "Some String",
AddressValue = account,
Uint8Value = 123,
Uint64Value = 12345,
Uint256Value = 123456,
};

try
{
await Sign_With_WalletConnect_Serialization(client, account, message);
await Sign_With_Nethereum_Serialization(client, account, message);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
await client.Disconnect();
}

Console.WriteLine($"{Name}: COMPLETE!");
}

private async Task Sign_With_WalletConnect_Serialization(WalletConnect client, string account, Message message)
{
Console.WriteLine($"Requesting Signature from {account}");

var signature = await client.EthSignTypedData(account, message, WalletConnectDomain);

Console.WriteLine($"WalletConnect signature: {signature}");

var recovered = _signer.RecoverFromSignatureV4(
message, NethereumSchema, signature);

Console.WriteLine($"WalletConnect recovered: {recovered}");

// assert the recovery matches the account
Debug.Assert(recovered == account);
}

private async Task Sign_With_Nethereum_Serialization(WalletConnect client, string account, Message message)
{
Console.WriteLine($"Requesting WC Signature from {account}");

var signature = await client.EthSignTypedData(account, message, NethereumSchema);

Console.WriteLine($"Nethereum signature: {signature}");

var recovered = _signer.RecoverFromSignatureV4(
message, NethereumSchema, signature);

Console.WriteLine($"Nethereum recovered: {recovered}");

// assert the recovery matches the account
Debug.Assert(recovered == account);
}
}
11 changes: 6 additions & 5 deletions Examples/console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ class Program
{
private static readonly IExample[] Examples = new IExample[]
{
new NEthereumSendTransactionExample()
new NEthereumSendTransactionExample(),
new SignTypedDataExample()
};

static void ShowHelp()
private static void ShowHelp()
{
Console.WriteLine("Please specify which example to run");
foreach (var e in Examples)
Expand All @@ -20,16 +21,16 @@ static void ShowHelp()
}
}

static async Task Main(string[] args)
public static async Task Main(string[] args)
{
if (args.Length == 0)
{
ShowHelp();
return;
}

string name = args[0];
string[] exampleArgs = args.Skip(1).ToArray();
var name = args[0];
var exampleArgs = args.Skip(1).ToArray();

var example = Examples.FirstOrDefault(e => e.Name.ToLower() == name);

Expand Down
1 change: 1 addition & 0 deletions Examples/console/WalletConnectSharp.Examples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

<ItemGroup>
<PackageReference Include="Nethereum.Web3" />
<PackageReference Include="Nethereum.Signer.EIP712" />
</ItemGroup>

</Project>
24 changes: 24 additions & 0 deletions WalletConnectSharp.Core/Extensions/DictionaryExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

namespace WalletConnectSharp.Core.Extensions;

public static class DictionaryExtensions
{
/// <Summary>
/// Try to add the element to the dictionary if the key does not already exist.
/// </Summary>
public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
if (dictionary == null)
{
throw new ArgumentNullException(nameof(dictionary));
}

if (!dictionary.ContainsKey(key))
{
dictionary.Add(key, value);
return true;
}

return false;
}
}
36 changes: 24 additions & 12 deletions WalletConnectSharp.Core/Models/Ethereum/Types/EIP712Domain.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
using WalletConnectSharp.Core.Models.Ethereum.Types;
using System.Numerics;

namespace WalletConnectSharp.Core.Models;
namespace WalletConnectSharp.Core.Models.Ethereum.Types;

public class EIP712Domain
{
public string name;
public string version;
[EvmType("string", "name", 1)]
[JsonProperty("name", Order = 1)]
public virtual string Name { get; set; }

[EvmIgnore]
public int chainId;
[EvmType("string", "version", 2)]
[JsonProperty("version", Order = 2)]
public virtual string Version { get; set; }

[EvmType("address")]
public string verifyingContract;
[EvmType("uint256", "chainId", 3)]
[JsonProperty("chainId", Order = 3)]
public virtual BigInteger? ChainId { get; set; }

[EvmType("address", "verifyingContract", 4)]
[JsonProperty("verifyingContract", Order = 4)]
public virtual string VerifyingContract { get; set; }

public EIP712Domain()
{

}

public EIP712Domain(string name, string version, int chainId, string verifyingContract)
{
this.name = name;
this.version = version;
this.chainId = chainId;
this.verifyingContract = verifyingContract;
Name = name;
Version = version;
ChainId = chainId;
VerifyingContract = verifyingContract;
}
}
14 changes: 11 additions & 3 deletions WalletConnectSharp.Core/Models/Ethereum/Types/EvmTypeAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
namespace WalletConnectSharp.Core.Models.Ethereum.Types;

[AttributeUsage(AttributeTargets.Property)]
public class EvmTypeAttribute : Attribute
{
public string TypeName { get; }
public string Type { get; }

public string Name { get; }

public int Order { get; }

public EvmTypeAttribute(string typename)
public EvmTypeAttribute(string typename, string name = null, int order = 1)
{
TypeName = typename;
Type = typename;
Name = name;
Order = order;
}
}
Loading

0 comments on commit 945e139

Please sign in to comment.