diff --git a/Thirdweb.Tests/Thirdweb.Contracts.Tests.cs b/Thirdweb.Tests/Thirdweb.Contracts.Tests.cs index dd2f7eb..cdd7d26 100644 --- a/Thirdweb.Tests/Thirdweb.Contracts.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.Contracts.Tests.cs @@ -129,6 +129,85 @@ public async Task WriteTest_PrivateKeyAccount() } } + [Fact] + public async Task SignatureMint_Generate() + { + var client = ThirdwebClient.Create(secretKey: _secretKey); + var signer = await PrivateKeyWallet.Create(client, _testPrivateKey); + + var randomDomain = "Test"; + var randomVersion = "1.0.0"; + var randomChainId = 421614; + var randomContractAddress = "0xD04F98C88cE1054c90022EE34d566B9237a1203C"; + + // GenerateSignature_MinimalForwarder + var forwardRequest = new Contracts.Forwarder.ForwardRequest + { + From = "0x123", + To = "0x456", + Value = BigInteger.Zero, + Gas = BigInteger.Zero, + Nonce = BigInteger.Zero, + Data = "0x" + }; + var signature = await EIP712.GenerateSignature_MinimalForwarder(randomDomain, randomVersion, randomChainId, randomContractAddress, forwardRequest, signer); + Assert.NotNull(signature); + Assert.StartsWith("0x", signature); + // GenerateSignature_TokenERC20 + var mintRequest20 = new Contracts.TokenERC20.MintRequest + { + To = await signer.GetAddress(), + PrimarySaleRecipient = await signer.GetAddress(), + Quantity = 1, + Price = 0, + Currency = Constants.ADDRESS_ZERO, + ValidityEndTimestamp = 0, + ValidityStartTimestamp = Utils.GetUnixTimeStampIn10Years(), + Uid = new byte[] { 0x01 } + }; + var signature20 = await EIP712.GenerateSignature_TokenERC20(randomDomain, randomVersion, randomChainId, randomContractAddress, mintRequest20, signer); + Assert.NotNull(signature20); + Assert.StartsWith("0x", signature20); + + // GenerateSignature_TokenERC721 + var mintRequest721 = new Contracts.TokenERC721.MintRequest + { + To = await signer.GetAddress(), + RoyaltyRecipient = await signer.GetAddress(), + RoyaltyBps = 0, + PrimarySaleRecipient = await signer.GetAddress(), + Uri = "https://example.com", + Price = 0, + Currency = Constants.ADDRESS_ZERO, + ValidityEndTimestamp = 0, + ValidityStartTimestamp = Utils.GetUnixTimeStampIn10Years(), + Uid = new byte[] { 0x01 } + }; + var signature721 = await EIP712.GenerateSignature_TokenERC721(randomDomain, randomVersion, randomChainId, randomContractAddress, mintRequest721, signer); + Assert.NotNull(signature721); + Assert.StartsWith("0x", signature721); + + // GenerateSignature_TokenERC1155 + var mintRequest1155 = new Contracts.TokenERC1155.MintRequest + { + To = await signer.GetAddress(), + RoyaltyRecipient = await signer.GetAddress(), + RoyaltyBps = 0, + PrimarySaleRecipient = await signer.GetAddress(), + TokenId = 1, + Uri = "https://example.com", + Quantity = 1, + PricePerToken = 0, + Currency = Constants.ADDRESS_ZERO, + ValidityEndTimestamp = 0, + ValidityStartTimestamp = Utils.GetUnixTimeStampIn10Years(), + Uid = new byte[] { 0x01 } + }; + var signature1155 = await EIP712.GenerateSignature_TokenERC1155(randomDomain, randomVersion, randomChainId, randomContractAddress, mintRequest1155, signer); + Assert.NotNull(signature1155); + Assert.StartsWith("0x", signature1155); + } + private async Task GetAccount() { var client = ThirdwebClient.Create(secretKey: _secretKey); diff --git a/Thirdweb/Thirdweb.Contracts/ContractTypes.cs b/Thirdweb/Thirdweb.Contracts/ContractTypes.cs new file mode 100644 index 0000000..44f4dc2 --- /dev/null +++ b/Thirdweb/Thirdweb.Contracts/ContractTypes.cs @@ -0,0 +1,149 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Newtonsoft.Json; + +namespace Thirdweb.Contracts.TokenERC20 +{ + public partial class MintRequest : MintRequestBase { } + + public class MintRequestBase + { + [Parameter("address", "to", 1)] + public virtual string To { get; set; } + + [Parameter("address", "primarySaleRecipient", 2)] + public virtual string PrimarySaleRecipient { get; set; } + + [Parameter("uint256", "quantity", 3)] + public virtual BigInteger Quantity { get; set; } + + [Parameter("uint256", "price", 4)] + public virtual BigInteger Price { get; set; } + + [Parameter("address", "currency", 5)] + public virtual string Currency { get; set; } + + [Parameter("uint128", "validityStartTimestamp", 6)] + public virtual BigInteger ValidityStartTimestamp { get; set; } + + [Parameter("uint128", "validityEndTimestamp", 7)] + public virtual BigInteger ValidityEndTimestamp { get; set; } + + [Parameter("bytes32", "uid", 8)] + public virtual byte[] Uid { get; set; } + } +} + +namespace Thirdweb.Contracts.TokenERC721 +{ + public partial class MintRequest : MintRequestBase { } + + public class MintRequestBase + { + [Parameter("address", "to", 1)] + public virtual string To { get; set; } + + [Parameter("address", "royaltyRecipient", 2)] + public virtual string RoyaltyRecipient { get; set; } + + [Parameter("uint256", "royaltyBps", 3)] + public virtual BigInteger RoyaltyBps { get; set; } + + [Parameter("address", "primarySaleRecipient", 4)] + public virtual string PrimarySaleRecipient { get; set; } + + [Parameter("string", "uri", 5)] + public virtual string Uri { get; set; } + + [Parameter("uint256", "price", 6)] + public virtual BigInteger Price { get; set; } + + [Parameter("address", "currency", 7)] + public virtual string Currency { get; set; } + + [Parameter("uint128", "validityStartTimestamp", 8)] + public virtual BigInteger ValidityStartTimestamp { get; set; } + + [Parameter("uint128", "validityEndTimestamp", 9)] + public virtual BigInteger ValidityEndTimestamp { get; set; } + + [Parameter("bytes32", "uid", 10)] + public virtual byte[] Uid { get; set; } + } +} + +namespace Thirdweb.Contracts.TokenERC1155 +{ + public partial class MintRequest : MintRequestBase { } + + public class MintRequestBase + { + [Parameter("address", "to", 1)] + public virtual string To { get; set; } + + [Parameter("address", "royaltyRecipient", 2)] + public virtual string RoyaltyRecipient { get; set; } + + [Parameter("uint256", "royaltyBps", 3)] + public virtual BigInteger RoyaltyBps { get; set; } + + [Parameter("address", "primarySaleRecipient", 4)] + public virtual string PrimarySaleRecipient { get; set; } + + [Parameter("uint256", "tokenId", 5)] + public virtual BigInteger TokenId { get; set; } + + [Parameter("string", "uri", 6)] + public virtual string Uri { get; set; } + + [Parameter("uint256", "quantity", 7)] + public virtual BigInteger Quantity { get; set; } + + [Parameter("uint256", "pricePerToken", 8)] + public virtual BigInteger PricePerToken { get; set; } + + [Parameter("address", "currency", 9)] + public virtual string Currency { get; set; } + + [Parameter("uint128", "validityStartTimestamp", 10)] + public virtual BigInteger ValidityStartTimestamp { get; set; } + + [Parameter("uint128", "validityEndTimestamp", 11)] + public virtual BigInteger ValidityEndTimestamp { get; set; } + + [Parameter("bytes32", "uid", 12)] + public virtual byte[] Uid { get; set; } + } +} + +namespace Thirdweb.Contracts.Forwarder +{ + public partial class ForwardRequest : ForwardRequestBase { } + + public class ForwardRequestBase + { + [Parameter("address", "from", 1)] + [JsonProperty("from")] + public virtual string From { get; set; } + + [Parameter("address", "to", 2)] + [JsonProperty("to")] + public virtual string To { get; set; } + + [Parameter("uint256", "value", 3)] + [JsonProperty("value")] + public virtual BigInteger Value { get; set; } + + [Parameter("uint256", "gas", 4)] + [JsonProperty("gas")] + public virtual BigInteger Gas { get; set; } + + [Parameter("uint256", "nonce", 5)] + [JsonProperty("nonce")] + public virtual BigInteger Nonce { get; set; } + + [Parameter("bytes", "data", 6)] + [JsonProperty("data")] + public virtual string Data { get; set; } + } +} diff --git a/Thirdweb/Thirdweb.Wallets/EIP712.cs b/Thirdweb/Thirdweb.Wallets/EIP712.cs index b0e2539..5ad71bd 100644 --- a/Thirdweb/Thirdweb.Wallets/EIP712.cs +++ b/Thirdweb/Thirdweb.Wallets/EIP712.cs @@ -15,7 +15,9 @@ namespace Thirdweb { public static class EIP712 { - public async static Task GenerateSignature_SmartAccount( + #region Generation + + public static async Task GenerateSignature_SmartAccount( string domainName, string version, BigInteger chainId, @@ -28,7 +30,7 @@ IThirdwebWallet signer return await signer.SignTypedDataV4(signerPermissionRequest, typedData); } - public async static Task GenerateSignature_SmartAccount_AccountMessage( + public static async Task GenerateSignature_SmartAccount_AccountMessage( string domainName, string version, BigInteger chainId, @@ -56,6 +58,62 @@ IThirdwebWallet signer return SerializeEip712(transaction, signatureRaw, chainId); } + public static async Task GenerateSignature_MinimalForwarder( + string domainName, + string version, + BigInteger chainId, + string verifyingContract, + Contracts.Forwarder.ForwardRequest forwardRequest, + IThirdwebWallet signer + ) + { + var typedData = GetTypedDefinition_MinimalForwarder(domainName, version, chainId, verifyingContract); + return await signer.SignTypedDataV4(forwardRequest, typedData); + } + + public static async Task GenerateSignature_TokenERC20( + string domainName, + string version, + BigInteger chainId, + string verifyingContract, + Contracts.TokenERC20.MintRequest mintRequest, + IThirdwebWallet signer + ) + { + var typedData = GetTypedDefinition_TokenERC20(domainName, version, chainId, verifyingContract); + return await signer.SignTypedDataV4(mintRequest, typedData); + } + + public static async Task GenerateSignature_TokenERC721( + string domainName, + string version, + BigInteger chainId, + string verifyingContract, + Contracts.TokenERC721.MintRequest mintRequest, + IThirdwebWallet signer + ) + { + var typedData = GetTypedDefinition_TokenERC721(domainName, version, chainId, verifyingContract); + return await signer.SignTypedDataV4(mintRequest, typedData); + } + + public static async Task GenerateSignature_TokenERC1155( + string domainName, + string version, + BigInteger chainId, + string verifyingContract, + Contracts.TokenERC1155.MintRequest mintRequest, + IThirdwebWallet signer + ) + { + var typedData = GetTypedDefinition_TokenERC1155(domainName, version, chainId, verifyingContract); + return await signer.SignTypedDataV4(mintRequest, typedData); + } + + #endregion + + #region Typed Definitions + public static TypedData GetTypedDefinition_SmartAccount(string domainName, string version, BigInteger chainId, string verifyingContract) { return new TypedData @@ -103,6 +161,74 @@ public static TypedData GetTypedDefinition_ZkSy }; } + public static TypedData GetTypedDefinition_TokenERC20(string domainName, string version, BigInteger chainId, string verifyingContract) + { + return new TypedData + { + Domain = new Domain + { + Name = domainName, + Version = version, + ChainId = chainId, + VerifyingContract = verifyingContract, + }, + Types = MemberDescriptionFactory.GetTypesMemberDescription(typeof(Domain), typeof(Contracts.TokenERC20.MintRequest)), + PrimaryType = nameof(Contracts.TokenERC20.MintRequest), + }; + } + + public static TypedData GetTypedDefinition_TokenERC721(string domainName, string version, BigInteger chainId, string verifyingContract) + { + return new TypedData + { + Domain = new Domain + { + Name = domainName, + Version = version, + ChainId = chainId, + VerifyingContract = verifyingContract, + }, + Types = MemberDescriptionFactory.GetTypesMemberDescription(typeof(Domain), typeof(Contracts.TokenERC721.MintRequest)), + PrimaryType = nameof(Contracts.TokenERC721.MintRequest), + }; + } + + public static TypedData GetTypedDefinition_TokenERC1155(string domainName, string version, BigInteger chainId, string verifyingContract) + { + return new TypedData + { + Domain = new Domain + { + Name = domainName, + Version = version, + ChainId = chainId, + VerifyingContract = verifyingContract, + }, + Types = MemberDescriptionFactory.GetTypesMemberDescription(typeof(Domain), typeof(Contracts.TokenERC1155.MintRequest)), + PrimaryType = nameof(Contracts.TokenERC1155.MintRequest), + }; + } + + public static TypedData GetTypedDefinition_MinimalForwarder(string domainName, string version, BigInteger chainId, string verifyingContract) + { + return new TypedData + { + Domain = new Domain + { + Name = domainName, + Version = version, + ChainId = chainId, + VerifyingContract = verifyingContract, + }, + Types = MemberDescriptionFactory.GetTypesMemberDescription(typeof(Domain), typeof(Contracts.Forwarder.ForwardRequest)), + PrimaryType = nameof(Contracts.Forwarder.ForwardRequest), + }; + } + + #endregion + + #region Helpers + private static string SerializeEip712(AccountAbstraction.ZkSyncAATransaction transaction, EthECDSASignature signature, BigInteger chainId) { if (chainId == 0) @@ -137,5 +263,7 @@ private static string SerializeEip712(AccountAbstraction.ZkSyncAATransaction tra return "0x71" + RLP.EncodeDataItemsAsElementOrListAndCombineAsList(fields.ToArray(), new int[] { 13, 15 }).ToHex(); } + + #endregion } }