Nethereum.Signer.EIP712 5.8.0

Prefix Reserved
dotnet add package Nethereum.Signer.EIP712 --version 5.8.0
                    
NuGet\Install-Package Nethereum.Signer.EIP712 -Version 5.8.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Nethereum.Signer.EIP712" Version="5.8.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nethereum.Signer.EIP712" Version="5.8.0" />
                    
Directory.Packages.props
<PackageReference Include="Nethereum.Signer.EIP712" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Nethereum.Signer.EIP712 --version 5.8.0
                    
#r "nuget: Nethereum.Signer.EIP712, 5.8.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Nethereum.Signer.EIP712@5.8.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Nethereum.Signer.EIP712&version=5.8.0
                    
Install as a Cake Addin
#tool nuget:?package=Nethereum.Signer.EIP712&version=5.8.0
                    
Install as a Cake Tool

Nethereum.Signer.EIP712

EIP-712 typed structured data signing for secure off-chain message authentication compatible with MetaMask's eth_signTypedData_v4.

Overview

Nethereum.Signer.EIP712 implements EIP-712, the standard for hashing and signing typed structured data. This enables signing complex objects (not just strings) in a way that's human-readable in MetaMask and other wallets, preventing phishing attacks where users unknowingly sign malicious transactions.

Key Features:

  • Sign complex typed data structures (objects, arrays, nested types)
  • Compatible with MetaMask's eth_signTypedData_v4
  • Human-readable signature prompts in wallets (shows fields, not raw hex)
  • Domain separation prevents replay attacks across different dApps
  • Type-safe C# API with automatic schema generation
  • Signature recovery to verify signers

Use Cases:

  • Gasless meta-transactions (user signs intent, relayer pays gas)
  • Off-chain order books (DEX orders, NFT listings)
  • Permit functionality (ERC-20 approvals via signature)
  • DAO voting (off-chain vote aggregation)
  • Session keys and delegated permissions

Installation

dotnet add package Nethereum.Signer.EIP712

Or via Package Manager Console:

Install-Package Nethereum.Signer.EIP712

Dependencies

Nethereum:

  • Nethereum.ABI - EIP-712 encoding implementation
  • Nethereum.Signer - Core ECDSA signing
  • Nethereum.Util - Keccak hashing
  • Nethereum.Hex - Hex encoding

Key Concepts

EIP-712 vs Regular Message Signing

Aspect Regular (EIP-191) EIP-712
Data Arbitrary bytes/string Typed structured data
Wallet Display Hex hash (unreadable) Human-readable fields
Type Safety None Full type checking
Phishing Protection Weak Strong (user sees what they sign)
Use Cases Simple messages Complex objects, transactions

Domain Separator

The domain separator prevents signatures from being valid across different:

  • Name: Application name
  • Version: Schema version
  • ChainId: Network (prevents mainnet/testnet replay)
  • VerifyingContract: Contract address that will verify the signature

TypedData Structure

public class TypedData<TDomain>
{
    public TDomain Domain { get; set; }                           // Domain separator
    public Dictionary<string, MemberDescription[]> Types { get; set; }  // Type definitions
    public string PrimaryType { get; set; }                       // Main message type
    public object Message { get; set; }                           // Actual data
}

Quick Start

using Nethereum.Signer;
using Nethereum.Signer.EIP712;
using Nethereum.ABI.EIP712;

// 1. Define your message type
public class Mail
{
    public Person From { get; set; }
    public Person To { get; set; }
    public string Contents { get; set; }
}

public class Person
{
    public string Name { get; set; }
    public string Wallet { get; set; }
}

// 2. Create domain
var domain = new Domain
{
    Name = "Ether Mail",
    Version = "1",
    ChainId = 1,
    VerifyingContract = "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
};

// 3. Create typed data
var mail = new Mail
{
    From = new Person { Name = "Alice", Wallet = "0x..." },
    To = new Person { Name = "Bob", Wallet = "0x..." },
    Contents = "Hello Bob!"
};

// 4. Sign
var signer = new Eip712TypedDataSigner();
var key = new EthECKey("YOUR_PRIVATE_KEY");
string signature = signer.SignTypedData(mail, domain, "Mail", key);

Usage Examples

Example 1: Simple Typed Message (Real Test Example)

using Nethereum.Signer.EIP712;
using Nethereum.ABI.EIP712;
using Nethereum.Signer;
using System.Collections.Generic;

// Define domain
var domain = new Domain
{
    Name = "Ether Mail",
    Version = "1",
    ChainId = 1,
    VerifyingContract = "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
};

// Define type schema
var typedData = new TypedData<Domain>
{
    Domain = domain,
    Types = new Dictionary<string, MemberDescription[]>
    {
        ["EIP712Domain"] = new[]
        {
            new MemberDescription { Name = "name", Type = "string" },
            new MemberDescription { Name = "version", Type = "string" },
            new MemberDescription { Name = "chainId", Type = "uint256" },
            new MemberDescription { Name = "verifyingContract", Type = "address" }
        },
        ["Mail"] = new[]
        {
            new MemberDescription { Name = "from", Type = "Person" },
            new MemberDescription { Name = "to", Type = "Person[]" },
            new MemberDescription { Name = "contents", Type = "string" }
        },
        ["Person"] = new[]
        {
            new MemberDescription { Name = "name", Type = "string" },
            new MemberDescription { Name = "wallets", Type = "address[]" }
        }
    },
    PrimaryType = "Mail",
    Message = new[]
    {
        new MemberValue
        {
            TypeName = "Person",
            Value = new[]
            {
                new MemberValue { TypeName = "string", Value = "Cow" },
                new MemberValue { TypeName = "address[]", Value = new List<string>
                {
                    "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
                    "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF"
                }}
            }
        },
        new MemberValue
        {
            TypeName = "Person[]",
            Value = new List<MemberValue[]>
            {
                new[]
                {
                    new MemberValue { TypeName = "string", Value = "Bob" },
                    new MemberValue { TypeName = "address[]", Value = new List<string>
                    {
                        "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
                    }}
                }
            }
        },
        new MemberValue { TypeName = "string", Value = "Hello, Bob!" }
    }
};

// Sign
var signer = new Eip712TypedDataSigner();
var key = new EthECKey("94e001d6adf3a3275d5dd45971c2a5f6637d3e9c51f9693f2e678f649e164fa5");
string signature = signer.SignTypedDataV4(typedData, key);

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

// Verify
string recoveredAddress = signer.RecoverFromSignatureV4(typedData, signature);
Console.WriteLine($"Signer: {recoveredAddress}");
Console.WriteLine($"Match: {key.GetPublicAddress() == recoveredAddress}");

Example 2: ERC-2612 Permit (Gasless Approval)

using Nethereum.Signer.EIP712;
using Nethereum.ABI.EIP712;
using Nethereum.Signer;
using System.Numerics;

// ERC-20 Permit allows approvals via signature (no gas cost)
public class Permit
{
    public string Owner { get; set; }
    public string Spender { get; set; }
    public BigInteger Value { get; set; }
    public BigInteger Nonce { get; set; }
    public BigInteger Deadline { get; set; }
}

var domain = new Domain
{
    Name = "USD Coin",
    Version = "2",
    ChainId = 1,
    VerifyingContract = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" // USDC
};

var permit = new Permit
{
    Owner = "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
    Spender = "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2",
    Value = BigInteger.Parse("1000000000"), // 1000 USDC (6 decimals)
    Nonce = 0,
    Deadline = 1735689600 // Unix timestamp
};

var signer = new Eip712TypedDataSigner();
var key = new EthECKey("YOUR_PRIVATE_KEY");

// This signature can be submitted by anyone to approve the spender
string signature = signer.SignTypedData(permit, domain, "Permit", key);

// The spender can now call: token.permit(owner, spender, value, deadline, v, r, s)
// No gas cost for the owner!

Example 3: Meta-Transaction (Gasless Transaction)

using Nethereum.Signer.EIP712;
using Nethereum.ABI.EIP712;
using System.Numerics;

public class MetaTransaction
{
    public BigInteger Nonce { get; set; }
    public string From { get; set; }
    public string FunctionSignature { get; set; }
}

var domain = new Domain
{
    Name = "My dApp",
    Version = "1",
    ChainId = 137, // Polygon
    VerifyingContract = "0x..." // Your contract address
};

var metaTx = new MetaTransaction
{
    Nonce = 0,
    From = "0x...", // User address
    FunctionSignature = "0x..." // Encoded function call
};

var signer = new Eip712TypedDataSigner();
var key = new EthECKey("USER_PRIVATE_KEY");
string signature = signer.SignTypedData(metaTx, domain, "MetaTransaction", key);

// Relayer submits this to: contract.executeMetaTransaction(from, functionSignature, signature)
// User doesn't pay gas - relayer does!

Example 4: DEX Order (0x Protocol Style)

using Nethereum.Signer.EIP712;
using Nethereum.ABI.EIP712;
using System.Numerics;

public class Order
{
    public string MakerAddress { get; set; }
    public string TakerAddress { get; set; }
    public string MakerAssetAddress { get; set; }
    public string TakerAssetAddress { get; set; }
    public BigInteger MakerAssetAmount { get; set; }
    public BigInteger TakerAssetAmount { get; set; }
    public BigInteger ExpirationTimeSeconds { get; set; }
    public BigInteger Salt { get; set; }
}

var domain = new Domain
{
    Name = "0x Protocol",
    Version = "3.0.0",
    ChainId = 1,
    VerifyingContract = "0x..." // Exchange contract
};

var order = new Order
{
    MakerAddress = "0x...",
    TakerAddress = "0x0000000000000000000000000000000000000000", // Anyone can fill
    MakerAssetAddress = "0x...", // WETH
    TakerAssetAddress = "0x...", // DAI
    MakerAssetAmount = BigInteger.Parse("1000000000000000000"), // 1 WETH
    TakerAssetAmount = BigInteger.Parse("2000000000000000000000"), // 2000 DAI
    ExpirationTimeSeconds = 1735689600,
    Salt = BigInteger.Parse("12345")
};

var signer = new Eip712TypedDataSigner();
var key = new EthECKey("MAKER_PRIVATE_KEY");
string signature = signer.SignTypedData(order, domain, "Order", key);

// Order is signed off-chain, submitted to relayer, filled on-chain

Example 5: DAO Vote (Snapshot Style)

using Nethereum.Signer.EIP712;
using Nethereum.ABI.EIP712;

public class Vote
{
    public string From { get; set; }
    public string Space { get; set; }
    public long Timestamp { get; set; }
    public string Proposal { get; set; }
    public int Choice { get; set; } // 1 = For, 2 = Against, 3 = Abstain
}

var domain = new Domain
{
    Name = "snapshot",
    Version = "0.1.4"
};

var vote = new Vote
{
    From = "0x...", // Voter address
    Space = "aave.eth",
    Timestamp = 1735689600,
    Proposal = "0x...", // Proposal ID
    Choice = 1 // Vote "For"
};

var signer = new Eip712TypedDataSigner();
var key = new EthECKey("VOTER_PRIVATE_KEY");
string signature = signer.SignTypedData(vote, domain, "Vote", key);

// Vote is aggregated off-chain, no gas cost for voters

Example 6: Sign from JSON (Real Test Example)

using Nethereum.Signer.EIP712;
using Nethereum.Signer;

// Sign typed data directly from JSON (useful for frontend integration)
var typedDataJson = @"{
    'domain': {
        'chainId': 1,
        'name': 'Ether Mail',
        'verifyingContract': '0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC',
        'version': '1'
    },
    'message': {
        'contents': 'Hello, Bob!',
        'from': {
            'name': 'Cow',
            'wallets': [
                '0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826',
                '0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF'
            ]
        },
        'to': [{
            'name': 'Bob',
            'wallets': ['0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB']
        }]
    },
    'primaryType': 'Mail',
    'types': {
        'EIP712Domain': [
            {'name': 'name', 'type': 'string'},
            {'name': 'version', 'type': 'string'},
            {'name': 'chainId', 'type': 'uint256'},
            {'name': 'verifyingContract', 'type': 'address'}
        ],
        'Mail': [
            {'name': 'from', 'type': 'Person'},
            {'name': 'to', 'type': 'Person[]'},
            {'name': 'contents', 'type': 'string'}
        ],
        'Person': [
            {'name': 'name', 'type': 'string'},
            {'name': 'wallets', 'type': 'address[]'}
        ]
    }
}";

var signer = new Eip712TypedDataSigner();
var key = new EthECKey("94e001d6adf3a3275d5dd45971c2a5f6637d3e9c51f9693f2e678f649e164fa5");

// Sign JSON directly
string signature = signer.SignTypedDataV4(typedDataJson, key);

// Recover signer from JSON + signature
string recoveredAddress = signer.RecoverFromSignatureV4(typedDataJson, signature);
Console.WriteLine($"Signer: {recoveredAddress}");

Example 7: NFT Lazy Minting

using Nethereum.Signer.EIP712;
using Nethereum.ABI.EIP712;
using System.Numerics;

public class LazyMint
{
    public BigInteger TokenId { get; set; }
    public string TokenURI { get; set; }
    public string Creator { get; set; }
    public BigInteger RoyaltyBps { get; set; } // Basis points (100 = 1%)
}

var domain = new Domain
{
    Name = "LazyNFT",
    Version = "1",
    ChainId = 1,
    VerifyingContract = "0x..." // NFT contract
};

var lazyMint = new LazyMint
{
    TokenId = 12345,
    TokenURI = "ipfs://QmYx...",
    Creator = "0x...", // Artist address
    RoyaltyBps = 1000 // 10% royalty
};

var signer = new Eip712TypedDataSigner();
var key = new EthECKey("ARTIST_PRIVATE_KEY");
string signature = signer.SignTypedData(lazyMint, domain, "LazyMint", key);

// NFT is not minted until someone buys it
// Buyer pays gas to mint + purchase in one transaction
// contract.buyAndMint(tokenId, tokenURI, creator, royaltyBps, signature)

Example 8: Session Key Authorization

using Nethereum.Signer.EIP712;
using Nethereum.ABI.EIP712;
using System.Numerics;

public class SessionKey
{
    public string SessionPublicKey { get; set; }
    public BigInteger ExpiresAt { get; set; }
    public string[] AllowedContracts { get; set; }
}

var domain = new Domain
{
    Name = "GameSession",
    Version = "1",
    ChainId = 137,
    VerifyingContract = "0x..." // Game contract
};

var sessionKey = new SessionKey
{
    SessionPublicKey = "0x...", // Temporary key for gaming session
    ExpiresAt = DateTimeOffset.UtcNow.AddHours(24).ToUnixTimeSeconds(),
    AllowedContracts = new[] { "0x...", "0x..." } // Game contracts
};

var signer = new Eip712TypedDataSigner();
var mainKey = new EthECKey("MAIN_WALLET_PRIVATE_KEY");
string signature = signer.SignTypedData(sessionKey, domain, "SessionKey", mainKey);

// Session key can now make transactions within constraints
// User doesn't need to approve each action - better UX for games

Example 9: Verify Signature Without Private Key

using Nethereum.Signer.EIP712;
using Nethereum.ABI.EIP712;
using Nethereum.Util;

// You have a signature and need to verify who signed it
var typedData = new TypedData<Domain>
{
    Domain = new Domain { Name = "MyApp", Version = "1", ChainId = 1 },
    // ... rest of typed data
};

string receivedSignature = "0x...";
string expectedSigner = "0x...";

var signer = new Eip712TypedDataSigner();

// Recover the address that created the signature
string recoveredAddress = signer.RecoverFromSignatureV4(typedData, receivedSignature);

// Verify it matches expected signer
bool isValid = expectedSigner.IsTheSameAddress(recoveredAddress);

if (isValid)
{
    Console.WriteLine("Signature is valid!");
    // Process the signed message
}
else
{
    Console.WriteLine($"Invalid signature!");
    Console.WriteLine($"Expected: {expectedSigner}");
    Console.WriteLine($"Got: {recoveredAddress}");
}

API Reference

Eip712TypedDataSigner

Main class for EIP-712 signing operations.

public class Eip712TypedDataSigner
{
    // Sign typed data (generates schema automatically)
    public string SignTypedData<T, TDomain>(T data, TDomain domain, string primaryTypeName, EthECKey key);

    // Sign pre-defined typed data
    public string SignTypedData<TDomain>(TypedData<TDomain> typedData, EthECKey key);

    // Sign for eth_signTypedData_v4 compatibility
    public string SignTypedDataV4<TDomain>(TypedData<TDomain> typedData, EthECKey key);
    public string SignTypedDataV4(string json, EthECKey key);
    public string SignTypedDataV4<T, TDomain>(T message, TypedData<TDomain> typedData, EthECKey key);

    // Sign with external signer (hardware wallet, etc.)
    public Task<string> SignTypedDataV4<TDomain>(TypedData<TDomain> typedData, IEthExternalSigner signer);

    // Recover signer address from signature
    public string RecoverFromSignatureV4<TDomain>(TypedData<TDomain> typedData, string signature);
    public string RecoverFromSignatureV4(string json, string signature);
    public string RecoverFromSignatureV4(byte[] encodedData, string signature);

    // Encode typed data (for custom workflows)
    public byte[] EncodeTypedData<TDomain>(TypedData<TDomain> typedData);
    public byte[] EncodeTypedData(string json);

    // Singleton instance
    public static Eip712TypedDataSigner Current { get; }
}

Used By (Consumers)

  • Nethereum.Accounts - Account signing with EIP-712
  • Nethereum.Contracts.Standards - ERC-2612 Permit, EIP-3009
  • Nethereum.X402 - HTTP 402 payment authorization

Dependencies

  • Nethereum.ABI - EIP-712 encoding engine
  • Nethereum.Signer - ECDSA signing primitives
  • Nethereum.Util - Keccak hashing
  • Nethereum.Hex - Hex encoding

Important Notes

MetaMask Compatibility

Always use SignTypedDataV4 for MetaMask compatibility:

// CORRECT - Works with MetaMask
string signature = signer.SignTypedDataV4(typedData, key);

// WRONG - Old format, not recommended
string signature = signer.SignTypedData(typedData, key);

Domain Separator is Critical

Always include proper domain to prevent cross-app replay:

// CORRECT - Unique per app and chain
var domain = new Domain
{
    Name = "My dApp",
    Version = "1",
    ChainId = 1, // REQUIRED for replay protection
    VerifyingContract = "0x..." // REQUIRED
};

// WRONG - Missing chainId allows replay attacks
var domain = new Domain
{
    Name = "My dApp",
    Version = "1"
};

Type Order Matters

Member order in type definitions must match exactly:

// CORRECT - Consistent order
new MemberDescription { Name = "name", Type = "string" },
new MemberDescription { Name = "wallet", Type = "address" }

// WRONG - Different order produces different hash
new MemberDescription { Name = "wallet", Type = "address" },
new MemberDescription { Name = "name", Type = "string" }

Frontend Integration

JSON format matches JavaScript exactly:

// Frontend (JavaScript)
const signature = await ethereum.request({
  method: 'eth_signTypedData_v4',
  params: [account, JSON.stringify(typedData)]
});

// Backend (.NET) - Same JSON structure
string signature = signer.SignTypedDataV4(jsonString, key);

Additional Resources

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net451 is compatible.  net452 was computed.  net46 was computed.  net461 is compatible.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (10)

Showing the top 5 NuGet packages that depend on Nethereum.Signer.EIP712:

Package Downloads
Nethereum.Accounts

Nethereum.Accounts Ethereum Accounts and Transaction Managers Class Library

Ubiq.SportXAPI

A .NET wrapper library for the SX BET exchange API

Nethereum.GnosisSafe

Nethereum.GnosisSafe Ethereum Service to interact with Gnosis Safe contracts

LoopringSharp

A library to help you connect with the Loopring API https://docs.loopring.io/

WalletConnect.NEthereum

An NEthereum extension to access the WalletConnect protocol through a Web3 Provider. A lightweight C# implementation of the WalletConnect protocol that can be used to connect to external wallets or connect a wallet to an external Dapp

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on Nethereum.Signer.EIP712:

Repository Stars
ChainSafe/web3.unity
🕹 Unity SDK for building games that interact with blockchains.
Version Downloads Last Updated
5.8.0 4,139 1/6/2026
5.0.0 315,744 5/28/2025
4.29.0 251,187 2/10/2025
4.28.0 77,281 1/7/2025
4.27.1 12,712 12/24/2024
4.27.0 1,978 12/24/2024
4.26.0 100,809 10/1/2024
4.25.0 24,087 9/19/2024
4.21.4 98,713 8/9/2024
4.21.3 13,178 7/22/2024
4.21.2 67,786 6/26/2024
4.21.1 2,630 6/26/2024
4.21.0 11,051 6/18/2024
4.20.0 317,622 3/28/2024
4.19.0 89,764 2/16/2024
4.18.0 270,500 11/21/2023
4.17.1 77,289 9/28/2023
4.17.0 16,416 9/27/2023
4.16.0 118,881 8/14/2023
4.15.2 122,929 7/11/2023
4.15.1 3,409 7/11/2023
4.15.0 3,910 7/11/2023
4.14.0 184,363 3/19/2023
4.13.0 132,620 2/18/2023
4.12.0 267,900 12/9/2022
4.11.0 173,748 10/27/2022
4.9.0 111,584 9/27/2022
4.8.0 178,286 8/24/2022
4.7.0 7,305 7/20/2022
4.6.1 3,713 6/18/2022
4.6.0 1,539 6/16/2022
4.5.0 3,334 5/13/2022
4.4.1 13,673 4/27/2022
4.4.0 1,472 4/27/2022
4.3.0 4,334 4/12/2022
4.2.0 6,859 2/18/2022
4.1.1 13,065 11/4/2021
4.1.0 1,316 10/15/2021
4.0.5 1,502 8/12/2021
4.0.4 1,099 8/10/2021
4.0.3 1,062 8/8/2021
4.0.2 1,062 8/5/2021
4.0.1 1,216 7/28/2021
3.8.0 7,236 7/3/2020