<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Nikhil</title>
    <description>The latest articles on Forem by Nikhil (@nikbhintade).</description>
    <link>https://forem.com/nikbhintade</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F124865%2Fa287999f-45c4-4016-90a7-a69cc7d0c66f.jpg</url>
      <title>Forem: Nikhil</title>
      <link>https://forem.com/nikbhintade</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/nikbhintade"/>
    <language>en</language>
    <item>
      <title>Developer’s Guide to ERC-4337 #2 | Testing Simple Account</title>
      <dc:creator>Nikhil</dc:creator>
      <pubDate>Wed, 09 Oct 2024 11:45:33 +0000</pubDate>
      <link>https://forem.com/nikbhintade/developers-guide-to-erc-4337-2-testing-simple-account-33b4</link>
      <guid>https://forem.com/nikbhintade/developers-guide-to-erc-4337-2-testing-simple-account-33b4</guid>
      <description>&lt;p&gt;This is part 2 of the “Developer’s Guide to ERC-4337” series. If you haven’t read the last article, check it out here: &lt;a href="https://dev.to/nikbhintade/developers-guide-to-erc-4337-1-developing-simple-account-4f83"&gt;Developer's Guide to ERC-4337: Developing a Simple Account&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Many tutorials focus on how to use or work with new technologies, but often skip the important part—testing. Figuring out how to test on your own can be a valuable learning opportunity, solidifying your knowledge and clearing up any confusion you might have had before.&lt;/p&gt;

&lt;p&gt;In the last article of this series, we developed a simple ERC-4337-compliant account contract. Now, in this article, we’re going to write unit tests for it. I'll first outline the test cases, and then you can either write the tests yourself or follow along with this guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should We Test For?
&lt;/h2&gt;

&lt;p&gt;The approach I recommend is to start with basic checks, like verifying state variables and simple functions, then move on to more complex functions with multiple components. Here are the test cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Check Initialization of &lt;code&gt;i_entryPoint&lt;/code&gt; and &lt;code&gt;i_owner&lt;/code&gt;&lt;/strong&gt;: Verify that the contract initializes and stores &lt;code&gt;i_entryPoint&lt;/code&gt; and &lt;code&gt;i_owner&lt;/code&gt; correctly after deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access control check for the &lt;code&gt;execute&lt;/code&gt; function&lt;/strong&gt;: Ensure the &lt;code&gt;execute&lt;/code&gt; function only works when called by the correct &lt;code&gt;i_entryPoint&lt;/code&gt; and reverts otherwise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check successful execution of the &lt;code&gt;execute&lt;/code&gt; function&lt;/strong&gt;: Ensure the &lt;code&gt;execute&lt;/code&gt; function successfully forwards a call to the destination when invoked by the correct entry point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check failure handling in the &lt;code&gt;execute&lt;/code&gt; function&lt;/strong&gt;: Verify that the &lt;code&gt;execute&lt;/code&gt; function reverts with &lt;code&gt;SimpleAccount__CallFailed&lt;/code&gt; when the destination call fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature validation with correct owner signature&lt;/strong&gt;: Confirm that the &lt;code&gt;_validateSignature&lt;/code&gt; function returns &lt;code&gt;SIG_VALIDATION_SUCCESS&lt;/code&gt; for a valid owner signature.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature validation with incorrect owner signature&lt;/strong&gt;: Ensure the &lt;code&gt;_validateSignature&lt;/code&gt; function returns &lt;code&gt;SIG_VALIDATION_FAILED&lt;/code&gt; for an invalid owner signature.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are all of these important? Yes and no. Our contract isn’t very complex, so there aren’t many ways things can go wrong. However, writing tests for as much as possible will build good habits. By the time you start working on more complex contracts, the habit of thorough testing will stick with you, and you'll be less likely to overlook mistakes.&lt;/p&gt;

&lt;p&gt;We’ll aim for 100% test coverage for this contract, which is feasible in our case. In general, achieving 90% coverage is a good goal to have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit Testing
&lt;/h2&gt;

&lt;p&gt;Let’s start writing our tests. First, we need to set up our testing environment. If you’re unfamiliar with testing in Foundry, I recommend reading the relevant section of the Foundry documentation. We’ll write our tests in a file called &lt;code&gt;SimpleAccountTest.t.sol&lt;/code&gt;, so go ahead and create that file in your &lt;code&gt;test&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;All tests will be placed in a contract, which we’ll call &lt;code&gt;SimpleAccountTest&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

contract SimpleAccountTest {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This contract will inherit the &lt;code&gt;Test&lt;/code&gt; contract from the forge-std library, which includes helpful functions for testing. Let’s import the &lt;code&gt;Test&lt;/code&gt; contract and extend it in our &lt;code&gt;SimpleAccountTest&lt;/code&gt; contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {Test} from "forge-std/Test.sol";

contract SimpleAccountTest is Test {}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing Internal Functions
&lt;/h3&gt;

&lt;p&gt;Before we dive into the tests, let’s briefly discuss testing internal functions. Testing external and public functions is straightforward because they’re designed for external interaction. However, testing internal or private functions can be tricky.&lt;/p&gt;

&lt;p&gt;For private functions, testing isn’t possible unless you copy-paste the code into the test, which isn’t ideal. But internal functions are inheritable, meaning we can create a contract that inherits the original contract and exposes the internal functions as public for testing purposes. These are called "harness" contracts.&lt;/p&gt;

&lt;p&gt;Let’s create a harness contract for &lt;code&gt;SimpleAccount&lt;/code&gt; that exposes the &lt;code&gt;_validateSignature&lt;/code&gt; function. We’ll call this contract &lt;code&gt;SimpleAccountHarness&lt;/code&gt;, and it will be the contract we use for testing since it includes all the functions from &lt;code&gt;SimpleAccount&lt;/code&gt;, plus some additional ones. Add the following contract to the same file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {SimpleAccount} from "src/SimpleAccount.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol";

contract SimpleAccountHarness is SimpleAccount {
    constructor(address entryPoint, address owner) SimpleAccount(entryPoint, owner) {}

    // exposes `_validateSignature` for testing
    function validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
        external
        view
        returns (uint256)
    {
        return _validateSignature(userOp, userOpHash);
    }
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use &lt;code&gt;SimpleAccount&lt;/code&gt; and expose its methods, we need to import both &lt;code&gt;SimpleAccount&lt;/code&gt; and &lt;code&gt;PackedUserOperation&lt;/code&gt;. We inherit from &lt;code&gt;SimpleAccount&lt;/code&gt;, meaning we need to satisfy its constructor. Just like &lt;code&gt;SimpleAccount&lt;/code&gt;, we pass the entry point contract’s address and the account owner’s address to the constructor.&lt;/p&gt;

&lt;p&gt;Next, we create the &lt;code&gt;validateSignature&lt;/code&gt; function, which has almost the same definition as &lt;code&gt;_validateSignature&lt;/code&gt; but with &lt;code&gt;external&lt;/code&gt; visibility. This allows us to expose the internal function for testing. We then pass the function’s arguments to &lt;code&gt;_validateSignature&lt;/code&gt; and return its result.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;setUp&lt;/code&gt; Function
&lt;/h3&gt;

&lt;p&gt;To start writing tests, we first need a &lt;code&gt;setUp&lt;/code&gt; function. This is a function that Forge automatically runs before each test to set up the necessary conditions, like deploying contracts or creating dummy users. In our &lt;code&gt;setUp&lt;/code&gt; function, we will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an instance of the &lt;code&gt;EntryPoint&lt;/code&gt; contract.&lt;/li&gt;
&lt;li&gt;Create two accounts: one for the owner and one for random users.&lt;/li&gt;
&lt;li&gt;Create an instance of &lt;code&gt;SimpleAccountHarness&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To deploy &lt;code&gt;EntryPoint&lt;/code&gt;, we first need to import it from &lt;code&gt;account-abstraction&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {EntryPoint} from "account-abstraction/core/EntryPoint.sol";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll store these contract instances and accounts in state variables so they can be accessed by the test functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract SimpleAccountTest is Test {
    SimpleAccountHarness private simpleAccountHarness;
    EntryPoint private entryPoint;

    Account owner;
    Account randomUser;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we'll create instances of the contracts and two accounts using Foundry’s cheat code &lt;code&gt;makeAccount&lt;/code&gt;. This cheat code generates an address and private key for each account, which we’ll use in our tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function setUp() external {
    owner = makeAccount("owner");
    randomUser = makeAccount("randomUser");

    entryPoint = new EntryPoint();
    simpleAccountHarness = new SimpleAccountHarness(address(entryPoint), owner.addr);
    vm.deal(address(simpleAccountHarness), 10 ether);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;makeAccount&lt;/code&gt; function takes a string to generate a wallet and returns an &lt;code&gt;Account&lt;/code&gt; struct. This struct has two fields: &lt;code&gt;addr&lt;/code&gt; (the account address) and &lt;code&gt;key&lt;/code&gt; (the private key). Both &lt;code&gt;Account&lt;/code&gt; and &lt;code&gt;makeAccount&lt;/code&gt; are available via &lt;code&gt;StdCheats.sol&lt;/code&gt;, which Foundry automatically imports.&lt;/p&gt;

&lt;p&gt;For contract instances, we use the &lt;code&gt;new&lt;/code&gt; keyword, passing the required constructor arguments. The &lt;code&gt;EntryPoint&lt;/code&gt; contract doesn’t require any arguments, but &lt;code&gt;SimpleAccountHarness&lt;/code&gt; requires both the &lt;code&gt;entryPoint&lt;/code&gt; address and the owner's address, which we get from the &lt;code&gt;addr&lt;/code&gt; field of the &lt;code&gt;owner&lt;/code&gt; account.&lt;/p&gt;

&lt;p&gt;Finally, we add some balance to &lt;code&gt;SimpleAccountHarness&lt;/code&gt; using the &lt;code&gt;deal&lt;/code&gt; function, which is part of the Forge standard library. This function takes an address and an amount, adding the specified balance to that address. We give &lt;code&gt;SimpleAccountHarness&lt;/code&gt; 10 ether, so it can execute actions like sending ether or paying back the entry point for user operations.&lt;/p&gt;

&lt;p&gt;Here's what the test contract looks like at this point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {Test} from "forge-std/Test.sol";
import {SimpleAccount} from "src/SimpleAccount.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol";
import {EntryPoint} from "account-abstraction/core/EntryPoint.sol";

contract SimpleAccountHarness is SimpleAccount {
    constructor(address entryPoint, address owner) SimpleAccount(entryPoint, owner) {}

    // exposes `_validateSignature` for testing
    function validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
        external
        view
        returns (uint256)
    {
        return _validateSignature(userOp, userOpHash);
    }
}

contract SimpleAccountTest is Test {
    SimpleAccountHarness private simpleAccountHarness;
    EntryPoint private entryPoint;

    Account owner;
    Account randomUser;

    function setUp() external {
        owner = makeAccount("owner");
        randomUser = makeAccount("randomUser");

        entryPoint = new EntryPoint();
        simpleAccountHarness = new SimpleAccountHarness(address(entryPoint), owner.addr);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  State Variable Check
&lt;/h3&gt;

&lt;p&gt;Let's write our first test to verify if the state variables initialized by the &lt;code&gt;SimpleAccount&lt;/code&gt; constructor are set correctly. In the &lt;code&gt;SimpleAccount&lt;/code&gt; constructor, we are simply setting the values for &lt;code&gt;entryPoint&lt;/code&gt; and &lt;code&gt;owner&lt;/code&gt;. Both of these variables have getter functions, so in our test, we'll retrieve their values and compare them to the expected values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function testStateVariables() public view {
    // Arrange
    // Act
    address contractOwner = simpleAccountHarness.getOwner();
    address contractEntryPoint = address(simpleAccountHarness.entryPoint());
    // Assert
    vm.assertEq(owner.addr, contractOwner);
    vm.assertEq(address(entryPoint), contractEntryPoint);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've written our first test case. To run this test, use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;forge &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--mt&lt;/span&gt; testStateVariables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;execute&lt;/code&gt; Function &amp;amp; Access Control Checks
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;SimpleAccount&lt;/code&gt;, the &lt;code&gt;execute&lt;/code&gt; function which interact with other accounts and contract. The &lt;code&gt;entryPoint&lt;/code&gt; contract directly executes calldata on the account contract, with the calldata being sent through a &lt;code&gt;PackedUserOperation&lt;/code&gt;.  The dapp will construct calldata with execute function selector and required function arguments. The &lt;code&gt;execute&lt;/code&gt; function can only be called by the &lt;code&gt;entryPoint&lt;/code&gt;; if anyone else tries to call it, the call should revert.&lt;/p&gt;

&lt;p&gt;Let’s test the &lt;code&gt;execute&lt;/code&gt; function by sending 1 ether to &lt;code&gt;randomUser&lt;/code&gt; and checking if &lt;code&gt;randomUser&lt;/code&gt;'s balance increases and the account contract's balance decreases. To do this, we need the initial balance for both accounts.&lt;/p&gt;

&lt;p&gt;Since only the &lt;code&gt;entryPoint&lt;/code&gt; can call the &lt;code&gt;execute&lt;/code&gt; function, we will use the cheat code &lt;code&gt;prank&lt;/code&gt; to make the call in the test originate from the &lt;code&gt;entryPoint&lt;/code&gt; address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; function testExecuteFunction() public {
    // Arrange
    uint256 initalBalanceOfRandomUser = randomUser.addr.balance;
    uint256 initalBalanceOfAccountContract = address(simpleAccountHarness).balance;

    uint256 valueToSend = 1 ether;
    // Act
    vm.prank(address(entryPoint));
    simpleAccountHarness.execute(randomUser.addr, valueToSend, "");
    // Assert
    vm.assertEq(randomUser.addr.balance, initalBalanceOfRandomUser + valueToSend);
    vm.assertEq(address(simpleAccountHarness).balance, initalBalanceOfAccountContract - valueToSend);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's ensure the function reverts if someone other than the &lt;code&gt;entryPoint&lt;/code&gt; calls it. The access control is handled by &lt;code&gt;_requireFromEntryPoint&lt;/code&gt;, which reverts with the message &lt;code&gt;"account: not from EntryPoint"&lt;/code&gt; if an unauthorized address tries to call it. We'll also check if the correct revert message is returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function testExecuteRevertsWithCorrectError() public {
    // Arrange
    uint256 valueToSend = 1 ether;

    // Act + Assert
    vm.prank(randomUser.addr);
    vm.expectRevert(bytes("account: not from EntryPoint")); 
    simpleAccountHarness.execute(randomUser.addr, valueToSend, "");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing &lt;code&gt;execute&lt;/code&gt; When Call Fails
&lt;/h3&gt;

&lt;p&gt;Finally, we need to test what happens if the call made by the &lt;code&gt;execute&lt;/code&gt; function fails. How do we simulate this?&lt;/p&gt;

&lt;p&gt;We’ll create a contract with a &lt;code&gt;fallback&lt;/code&gt; function that reverts on every call. You can add this contract below the &lt;code&gt;SimpleAccountHarness&lt;/code&gt; contract.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract RevertsOnEthTransfer {
    fallback() external {
        revert("");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test this, we’ll create an instance of &lt;code&gt;RevertsOnEthTransfer&lt;/code&gt; in our test and try to send ether to it, which will cause the call to revert and return the custom error &lt;code&gt;SimpleAccount__CallFailed&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function testCallFromExecuteFails() public {
    // Arrange
    RevertsOnEthTransfer revertsOnEthTransfer = new RevertsOnEthTransfer();

    uint256 valueToSend = 1 ether;

    // Act + Assert
    vm.prank(address(entryPoint)); // execute function needs to be executes from EntryPoint
    vm.expectRevert(SimpleAccount.SimpleAccount__CallFailed.selector);
    simpleAccountHarness.execute(address(revertsOnEthTransfer), valueToSend, "");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous test, we passed a string to &lt;code&gt;expectRevert&lt;/code&gt;, but here we pass the error selector since &lt;code&gt;SimpleAccount&lt;/code&gt; reverts with a custom error when the call fails.&lt;/p&gt;

&lt;p&gt;With this, we've covered testing the &lt;code&gt;execute&lt;/code&gt; function. Now, we can move on to the final part: testing the &lt;code&gt;validateSignature&lt;/code&gt; function, which exposes &lt;code&gt;_validateSignature&lt;/code&gt; from &lt;code&gt;SimpleAccount&lt;/code&gt;. This part is a bit more complicated but essential to complete the test coverage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signature Validation Checks
&lt;/h3&gt;

&lt;p&gt;Before we dive into writing the tests, we first need to create a function that generates a dummy &lt;code&gt;UserOperation&lt;/code&gt; and signs it using the private key of the account. Let’s call this function &lt;code&gt;generateAndSignUserOperation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To get started, import &lt;code&gt;MessageHashUtils&lt;/code&gt; from OpenZeppelin, which we'll use to generate the message the user will sign.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function will take an argument of type &lt;code&gt;Account&lt;/code&gt;, which we need to sign the &lt;code&gt;UserOperation&lt;/code&gt;, and return two values: the signed &lt;code&gt;UserOperation&lt;/code&gt; of type &lt;code&gt;PackedUserOperation&lt;/code&gt; and the &lt;code&gt;UserOperation&lt;/code&gt; hash of type &lt;code&gt;bytes32&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function generateSigAndSignedUserOp(Account memory user)
    public
    view
    returns (PackedUserOperation memory, bytes32)
{}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside this function, we’ll create a dummy &lt;code&gt;UserOperation&lt;/code&gt; with random values. Since we are unit testing &lt;code&gt;_validateSignature&lt;/code&gt;, we only care about working with the signature field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function generateSigAndSignedUserOp(Account memory user)
    public
    view
    returns (PackedUserOperation memory, bytes32)
{

    PackedUserOperation memory userOp = PackedUserOperation({
        sender: address(simpleAccountHarness),
        nonce: 1,
        initCode: hex"",
        callData: hex"",
        accountGasLimits: hex"",
        preVerificationGas: type(uint64).max,
        gasFees: hex"",
        paymasterAndData: hex"",
        signature: hex""
    });

    // Next part
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signature field will contain the signature generated by signing the &lt;code&gt;UserOperation&lt;/code&gt; hash. This hash is generated using the &lt;code&gt;getUserOpHash&lt;/code&gt; function from the &lt;code&gt;EntryPoint&lt;/code&gt; contract, which takes a &lt;code&gt;PackedUserOperation&lt;/code&gt; as an argument. We’ll pass the &lt;code&gt;UserOperation&lt;/code&gt; with an empty signature field. Once the &lt;code&gt;UserOpHash&lt;/code&gt; is signed, the signature is added and then sent to the &lt;code&gt;EntryPoint&lt;/code&gt; contract for validation and execution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function generateSigAndSignedUserOp(Account memory user)
    public
    view
    returns (PackedUserOperation memory, bytes32)
{

    PackedUserOperation memory userOp = PackedUserOperation({
        sender: address(simpleAccountHarness),
        nonce: 1,
        initCode: hex"",
        callData: hex"",
        accountGasLimits: hex"",
        preVerificationGas: type(uint64).max,
        gasFees: hex"",
        paymasterAndData: hex"",
        signature: hex""
    });

    bytes32 userOpHash = entryPoint.getUserOpHash(userOp);
    bytes32 formattedUserOpHash = MessageHashUtils.toEthSignedMessageHash(userOpHash);

        // Next Part

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to sign the &lt;code&gt;formattedUserOpHash&lt;/code&gt; using the &lt;code&gt;sign&lt;/code&gt; function from the Forge standard library. This function takes the message and private key used to sign the message. It returns the &lt;code&gt;v&lt;/code&gt;, &lt;code&gt;r&lt;/code&gt;, and &lt;code&gt;s&lt;/code&gt; components of the signature, which we then encode in &lt;code&gt;r&lt;/code&gt;, &lt;code&gt;s&lt;/code&gt;, and &lt;code&gt;v&lt;/code&gt; order using &lt;code&gt;encodePacked&lt;/code&gt;. The signature is finally added to the &lt;code&gt;signature&lt;/code&gt; field of &lt;code&gt;userOp&lt;/code&gt;. The function will then return the modified &lt;code&gt;userOp&lt;/code&gt; and the &lt;code&gt;userOpHash&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function generateSigAndSignedUserOp(Account memory user)
    public
    view
    returns (PackedUserOperation memory, bytes32)
{

    PackedUserOperation memory userOp = PackedUserOperation({
        sender: address(simpleAccountHarness),
        nonce: 1,
        initCode: hex"",
        callData: hex"",
        accountGasLimits: hex"",
        preVerificationGas: type(uint64).max,
        gasFees: hex"",
        paymasterAndData: hex"",
        signature: hex""
    });

    bytes32 userOpHash = entryPoint.getUserOpHash(userOp);
    bytes32 formattedUserOpHash = MessageHashUtils.toEthSignedMessageHash(userOpHash);

    (uint8 v, bytes32 r, bytes32 s) = vm.sign(user.key, formattedUserOpHash);

    userOp.signature = abi.encodePacked(r, s, v);

    return (userOp, userOpHash);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the &lt;code&gt;generateSigAndSignedUserOp&lt;/code&gt; function, let’s write our test cases for &lt;code&gt;_validateSignature&lt;/code&gt;. The first test will check if the owner has signed the &lt;code&gt;UserOperation&lt;/code&gt; and that the function returns the correct value. The expected success value is &lt;code&gt;SIG_VALIDATION_SUCCESS&lt;/code&gt;, which we first need to import.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { SIG_VALIDATION_SUCCESS} from "account-abstraction/core/Helpers.sol";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this test, we will use the &lt;code&gt;generateSigAndSignedUserOp&lt;/code&gt; function to create a &lt;code&gt;UserOperation&lt;/code&gt;, passing in the &lt;code&gt;owner&lt;/code&gt;. We’ll then pass the returned &lt;code&gt;userOp&lt;/code&gt; and &lt;code&gt;userOpHash&lt;/code&gt; to the &lt;code&gt;validateSignature&lt;/code&gt; function (which exposes &lt;code&gt;_validateSignature&lt;/code&gt; via &lt;code&gt;SimpleAccountHarness&lt;/code&gt;). Finally, we’ll check if the return value matches the expected value. Since no state modification happens here, we can make this a &lt;code&gt;view&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function testValidateSignature() public view {
    // Arrange
    (PackedUserOperation memory userOp, bytes32 userOpHash) = generateSigAndSignedUserOp(owner);
    // Act
    uint256 result = simpleAccountHarness.validateSignature(userOp, userOpHash);
    // Assert
    vm.assertEq(result, SIG_VALIDATION_SUCCESS);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last test case will verify the failure scenario, ensuring that the function returns &lt;code&gt;SIG_VALIDATION_FAILED&lt;/code&gt; when the &lt;code&gt;UserOperation&lt;/code&gt; is signed with an incorrect user. We need to import the &lt;code&gt;SIG_VALIDATION_FAILED&lt;/code&gt; constant from &lt;code&gt;account-abstraction&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED} from "account-abstraction/core/Helpers.sol";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test this, we’ll pass &lt;code&gt;randomUser&lt;/code&gt; to the &lt;code&gt;generateSigAndSignedUserOp&lt;/code&gt; function, and the rest will be similar to the previous test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function testValidateSignatureWithWrongSignature() public view {
    // Arrange
    (PackedUserOperation memory userOp, bytes32 userOpHash) = generateSigAndSignedUserOp(randomUser);
    // Act
    uint256 result = simpleAccountHarness.validateSignature(userOp, userOpHash);
    // Assert
    vm.assertEq(result, SIG_VALIDATION_FAILED);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these test cases, we have covered the &lt;code&gt;_validateSignature&lt;/code&gt; function. Here’s how the final test file should look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {Test} from "forge-std/Test.sol";
import {SimpleAccount} from "src/SimpleAccount.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol";
import {EntryPoint} from "account-abstraction/core/EntryPoint.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {SIG_VALIDATION_SUCCESS, SIG_VALIDATION_FAILED} from "account-abstraction/core/Helpers.sol";

contract SimpleAccountHarness is SimpleAccount {
    constructor(address entryPoint, address owner) SimpleAccount(entryPoint, owner) {}

    // exposes `_validateSignature` for testing
    function validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
        external
        view
        returns (uint256)
    {
        return _validateSignature(userOp, userOpHash);
    }
}

contract RevertsOnEthTransfer {
    fallback() external {
        revert("");
    }
}

contract SimpleAccountTest is Test {
    SimpleAccountHarness private simpleAccountHarness;
    EntryPoint private entryPoint;

    Account owner;
    Account randomUser;

    function setUp() external {
        owner = makeAccount("owner");
        randomUser = makeAccount("randomUser");

        entryPoint = new EntryPoint();
        simpleAccountHarness = new SimpleAccountHarness(address(entryPoint), owner.addr);
        vm.deal(address(simpleAccountHarness), 10 ether);
    }

    function testStateVariables() public view {
        // Arrange

        // Act
        address contractOwner = simpleAccountHarness.getOwner();
        address contractEntryPoint = address(simpleAccountHarness.entryPoint());

        // Assert
        vm.assertEq(owner.addr, contractOwner);
        vm.assertEq(address(entryPoint), contractEntryPoint);
    }

    function testExecuteFunction() public {
        // Arrange
        uint256 initalBalanceOfRandomUser = randomUser.addr.balance;
        uint256 initalBalanceOfAccountContract = address(simpleAccountHarness).balance;

        uint256 valueToSend = 1 ether;

        // Act
        vm.prank(address(entryPoint));
        simpleAccountHarness.execute(randomUser.addr, valueToSend, "");

        // Assert
        vm.assertEq(randomUser.addr.balance, initalBalanceOfRandomUser + valueToSend);
        vm.assertEq(address(simpleAccountHarness).balance, initalBalanceOfAccountContract - valueToSend);
    }

    function testExecuteRevertsWithCorrectError() public {
        // Arrange
        uint256 valueToSend = 1 ether;

        // Act + Assert
        vm.prank(randomUser.addr);
        vm.expectRevert(bytes("account: not from EntryPoint"));
        simpleAccountHarness.execute(randomUser.addr, valueToSend, "");
    }

    function testCallFromExecuteFails() public {
        // Arrange
        RevertsOnEthTransfer revertsOnEthTransfer = new RevertsOnEthTransfer();

        uint256 valueToSend = 1 ether;

        // Act + Assert
        vm.prank(address(entryPoint)); // execute function needs to be executes from EntryPoint
        vm.expectRevert(SimpleAccount.SimpleAccount__CallFailed.selector);
        simpleAccountHarness.execute(address(revertsOnEthTransfer), valueToSend, "");
    }

    function generateSigAndSignedUserOp(Account memory user)
        public
        view
        returns (PackedUserOperation memory, bytes32)
    {
        PackedUserOperation memory userOp = PackedUserOperation({
            sender: address(simpleAccountHarness),
            nonce: 1,
            initCode: hex"",
            callData: hex"",
            accountGasLimits: hex"",
            preVerificationGas: type(uint64).max,
            gasFees: hex"",
            paymasterAndData: hex"",
            signature: hex""
        });

        bytes32 userOpHash = entryPoint.getUserOpHash(userOp);
        bytes32 formattedUserOpHash = MessageHashUtils.toEthSignedMessageHash(userOpHash);

        (uint8 v, bytes32 r, bytes32 s) = vm.sign(user.key, formattedUserOpHash);

        userOp.signature = abi.encodePacked(r, s, v);

        return (userOp, userOpHash);
    }

    function testValidateSignature() public view {
        // Arrange
        (PackedUserOperation memory userOp, bytes32 userOpHash) = generateSigAndSignedUserOp(owner);
        // Act
        uint256 result = simpleAccountHarness.validateSignature(userOp, userOpHash);
        // Assert
        vm.assertEq(result, SIG_VALIDATION_SUCCESS);
    }

    function testValidateSignatureWithWrongSignature() public view {
        // Arrange
        (PackedUserOperation memory userOp, bytes32 userOpHash) = generateSigAndSignedUserOp(randomUser);
        // Act
        uint256 result = simpleAccountHarness.validateSignature(userOp, userOpHash);
        // Assert
        vm.assertEq(result, SIG_VALIDATION_FAILED);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we’ve finished writing our unit tests, let’s check the test coverage. You can do this by running the following command in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;forge coverage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Throughout the unit tests, we learned a lot, like creating &lt;code&gt;PackedUserOperation&lt;/code&gt; and working with &lt;code&gt;EntryPoint&lt;/code&gt; to generate the user hash. We also explored many other concepts related to testing with Forge. While this isn't a Foundry basics guide, working through it will help improve our skills and understanding.&lt;/p&gt;

&lt;p&gt;In our unit tests, we created a dummy &lt;code&gt;PackedUserOperation&lt;/code&gt;, but it was quite basic—there was no gas or calldata involved. Obviously, this won’t be enough for production use. In the next part, which will focus on integration testing, we’ll attempt to execute a real user operation through the entry point.&lt;/p&gt;

&lt;p&gt;I hope you’re as excited as I am for the next part. If you’ve enjoyed the series so far, stay tuned for the upcoming content.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you think there’s room for improvement in the code or explanation, please feel free to let me know in the comments!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Who Am I?
&lt;/h2&gt;

&lt;p&gt;My name is Nikhil. I’m a full-time technical writer focused on Web3 tech, especially Ethereum and Solidity. If you’re looking for a technical writer, either freelance or full-time, feel free to reach out—I’d love to explore any opportunities.&lt;/p&gt;

</description>
      <category>solidity</category>
      <category>ethereum</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
    <item>
      <title>Developer’s Guide to ERC-4337 #1 | Developing Simple Account</title>
      <dc:creator>Nikhil</dc:creator>
      <pubDate>Fri, 04 Oct 2024 06:11:37 +0000</pubDate>
      <link>https://forem.com/nikbhintade/developers-guide-to-erc-4337-1-developing-simple-account-4f83</link>
      <guid>https://forem.com/nikbhintade/developers-guide-to-erc-4337-1-developing-simple-account-4f83</guid>
      <description>&lt;p&gt;Lately, I’ve been exploring ERC-4337, which introduces account abstraction with an alternative mempool. It’s a really exciting topic because it lets developers hide much of the complexity with wallet management and dapp interaction, making the user experience more seamless.&lt;/p&gt;

&lt;p&gt;I’m creating this series for developers who want to get hands-on with ERC-4337. This isn’t a series to learn about pros and cons of account abstraction. Instead, this series will focus on the ERC-4337 specification and walk through building account contracts using it.&lt;/p&gt;

&lt;p&gt;Since we’ll be working with ERC-4337, I originally planned to explain concepts as we need them. But I realized it’s more helpful to start with an overview so you can understand the bigger picture upfront. So, let’s dive in and get familiar with ERC-4337!&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding ERC-4337: What It Is and How It Works
&lt;/h2&gt;

&lt;p&gt;ERC-4337 defines how account abstraction should work on Ethereum or any EVM-compatible chain without changing the consensus layer. It introduces two key ideas: the &lt;strong&gt;UserOperation&lt;/strong&gt; and the &lt;strong&gt;Alt Mempool&lt;/strong&gt;, which are higher-layer components that rely on existing blockchain infrastructure.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;UserOperation&lt;/strong&gt; is a high-level, pseudo-transaction object that holds both intent and verification data. Unlike regular transactions that are sent to validators, UserOperations are sent to &lt;strong&gt;bundlers&lt;/strong&gt; through public or private alt mempools. These bundlers collect multiple UserOperations, bundle them together into a single transaction, and submit them to get included on the blockchain.&lt;/p&gt;

&lt;p&gt;When bundlers send these UserOperations, they interact with a special contract called &lt;strong&gt;EntryPoint&lt;/strong&gt;. The EntryPoint contract is responsible for validating and executing UserOperations. However, it doesn’t handle verification itself. Instead, the verification logic is stored in the &lt;strong&gt;Account Contract&lt;/strong&gt;, which is the user’s smart contract wallet.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Account Contract&lt;/strong&gt; contains both the validation and execution logic for UserOperations. The ERC-4337 specification defines a standard interface for these contracts, ensuring they follow a consistent structure. Here's the &lt;code&gt;IAccount&lt;/code&gt; interface from the spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface IAccount {
  function validateUserOp
      (PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
      external returns (uint256 validationData);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a bundler submits a UserOperation to the EntryPoint contract, the EntryPoint first calls the &lt;code&gt;validateUserOp&lt;/code&gt; function on the user’s account contract. If the UserOperation is valid, the function returns &lt;code&gt;SIG_VALIDATION_SUCCESS&lt;/code&gt;. If validation is successful, the EntryPoint proceeds to execute the UserOperation's calldata on the account contract.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjhp6mwlyvoxyki3vb37z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjhp6mwlyvoxyki3vb37z.png" alt="ERC-4337 UserOp Flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s a high-level overview of account abstraction with ERC-4337. Next, we’ll dive into the code and break down the key components of ERC-4337 starting with account contract in more detail. Let’s get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Project
&lt;/h2&gt;

&lt;p&gt;We'll be using &lt;strong&gt;Foundry&lt;/strong&gt; to write and test our smart contracts. If you’re not familiar with Foundry, I recommend checking out the &lt;a href="https://book.getfoundry.sh/" rel="noopener noreferrer"&gt;Foundry documentation&lt;/a&gt; to get up to speed.&lt;/p&gt;

&lt;p&gt;To create a new Foundry project, run the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Creates a new directory named 'simple-account'&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;simple-account

&lt;span class="c"&gt;# Navigates into the 'simple-account' directory&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;simple-account

&lt;span class="c"&gt;# Initializes a new Foundry project&lt;/span&gt;
forge init

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to install some dependencies that will help us develop our account contract. The &lt;strong&gt;eth-infinitism&lt;/strong&gt; team has created an implementation of ERC-4337, which we’ll use as a base. To add it to our project, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Installs the ERC-4337 implementation from eth-infinitism&lt;/span&gt;
forge &lt;span class="nb"&gt;install &lt;/span&gt;eth-infinitism/account-abstraction@v0.7.0 &lt;span class="nt"&gt;--no-commit&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, clean up the default files by removing everything inside the &lt;code&gt;src&lt;/code&gt;, &lt;code&gt;script&lt;/code&gt;, and &lt;code&gt;test&lt;/code&gt; folders. Then, create a new file named &lt;code&gt;SimpleAccount.sol&lt;/code&gt; inside the &lt;code&gt;src&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;With this, your project setup is complete, and you’re ready to start developing the smart contract!&lt;/p&gt;

&lt;h2&gt;
  
  
  BaseAccount.sol
&lt;/h2&gt;

&lt;p&gt;Much like with ERC-20 tokens, we don’t need to build everything from scratch. We can use &lt;code&gt;BaseAccount.sol&lt;/code&gt; from the &lt;code&gt;eth-infinitism/account-abstraction&lt;/code&gt; package as a foundation. Here's the &lt;code&gt;BaseAccount.sol&lt;/code&gt; implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.23;

/* solhint-disable avoid-low-level-calls */
/* solhint-disable no-empty-blocks */

import "../interfaces/IAccount.sol";
import "../interfaces/IEntryPoint.sol";
import "./UserOperationLib.sol";

abstract contract BaseAccount is IAccount {
    using UserOperationLib for PackedUserOperation;

    function getNonce() public view virtual returns (uint256) {
        return entryPoint().getNonce(address(this), 0);
    }

    function entryPoint() public view virtual returns (IEntryPoint);

    function validateUserOp(
        PackedUserOperation calldata userOp,
        bytes32 userOpHash,
        uint256 missingAccountFunds
    ) external virtual override returns (uint256 validationData) {
        _requireFromEntryPoint();
        validationData = _validateSignature(userOp, userOpHash);
        _validateNonce(userOp.nonce);
        _payPrefund(missingAccountFunds);
    }

    function _requireFromEntryPoint() internal view virtual {
        require(
            msg.sender == address(entryPoint()),
            "account: not from EntryPoint"
        );
    }

    function _validateSignature(
        PackedUserOperation calldata userOp,
        bytes32 userOpHash
    ) internal virtual returns (uint256 validationData);

    function _validateNonce(uint256 nonce) internal view virtual {
    }

    function _payPrefund(uint256 missingAccountFunds) internal virtual {
        if (missingAccountFunds != 0) {
            (bool success, ) = payable(msg.sender).call{
                value: missingAccountFunds,
                gas: type(uint256).max
            }("");
            (success);
            //ignore failure (its EntryPoint's job to verify, not account.)
        }
    }
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before diving into the code, let’s first understand some key specifications that ERC-4337 defines for account contracts. These are important to understand before moving forward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trusted EntryPoint:&lt;/strong&gt; The contract MUST verify that the caller is a trusted EntryPoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature Validation:&lt;/strong&gt; If the account doesn't support signature aggregation, the contract MUST ensure that the signature is valid for the &lt;code&gt;userOpHash&lt;/code&gt;. On a signature mismatch, it SHOULD return &lt;code&gt;SIG_VALIDATION_FAILED&lt;/code&gt; (instead of reverting). Any other errors MUST cause a revert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paying EntryPoint:&lt;/strong&gt; The contract MUST ensure the EntryPoint (the caller) is paid at least the &lt;code&gt;missingAccountFunds&lt;/code&gt;, which may be zero if the account’s deposit is already sufficient.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The provided &lt;code&gt;BaseAccount.sol&lt;/code&gt; implementation satisfies all these requirements in the &lt;code&gt;validateUserOp&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function validateUserOp(
    PackedUserOperation calldata userOp,
    bytes32 userOpHash,
    uint256 missingAccountFunds
) external virtual override returns (uint256 validationData) {
    _requireFromEntryPoint();
    validationData = _validateSignature(userOp, userOpHash);
    _validateNonce(userOp.nonce);
    _payPrefund(missingAccountFunds);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down what happens in this function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;_requireFromEntryPoint:&lt;/strong&gt; This checks that the caller is the trusted EntryPoint contract.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;_validateSignature:&lt;/strong&gt; This function handles the signature validation logic, which we will implement in our contract.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;_payPrefund:&lt;/strong&gt; This ensures that the EntryPoint is paid the &lt;code&gt;missingAccountFunds&lt;/code&gt;, fulfilling the requirement of paying at least the minimum amount.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;_validateNonce:&lt;/strong&gt; This checks the transaction nonce, preventing replay attacks.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why Do We Have a Nonce?
&lt;/h3&gt;

&lt;p&gt;EOAs (Externally Owned Accounts) have a nonce to prevent replay attacks, ensuring that each transaction is unique. Now that we are building a smart contract wallet, we need a nonce for the same purpose. While we could define our own nonce validation logic, EntryPoint already handles this in a similar way, so we don’t need to focus too much on it for now.&lt;/p&gt;

&lt;p&gt;In summary, the &lt;code&gt;validateUserOp&lt;/code&gt; function satisfies all the key requirements from the ERC-4337 specification.&lt;/p&gt;

&lt;h2&gt;
  
  
  SimpleAccount.sol
&lt;/h2&gt;

&lt;p&gt;Now that we've met the core requirements, what’s left? From the &lt;code&gt;BaseAccount&lt;/code&gt; contract, we see that when inheriting it, we need to implement two key functions: &lt;code&gt;_validateSignature&lt;/code&gt; and &lt;code&gt;entryPoint&lt;/code&gt;. These two functions are essential to fulfill the basic requirements.&lt;/p&gt;

&lt;p&gt;In addition to these, we also need a function to allow interaction with other accounts and contracts. ERC-4337 doesn’t specify what this function should be named, and it’s not important since the &lt;code&gt;EntryPoint&lt;/code&gt; contract will execute the calldata directly on the account contract.&lt;/p&gt;

&lt;p&gt;Let’s begin by implementing the &lt;code&gt;entryPoint&lt;/code&gt; function, which is the simplest. Our goal is to define the trusted &lt;code&gt;entryPoint&lt;/code&gt; and return its address when the function is called.&lt;/p&gt;

&lt;h3&gt;
  
  
  EntryPoint Function
&lt;/h3&gt;

&lt;p&gt;We’ll start by developing &lt;code&gt;SimpleAccount.sol&lt;/code&gt;. Begin by creating the &lt;code&gt;SimpleAccount&lt;/code&gt; contract and importing &lt;code&gt;BaseAccount&lt;/code&gt; from the &lt;code&gt;BaseAccount.sol&lt;/code&gt; file. This contract will inherit from &lt;code&gt;BaseAccount&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {BaseAccount} from "account-abstraction/core/BaseAccount.sol";

contract SimpleAccount is BaseAccount {
    constructor() {}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to define the trusted &lt;code&gt;EntryPoint&lt;/code&gt; in the constructor. The &lt;code&gt;EntryPoint&lt;/code&gt; address will be passed in as a parameter and stored in an immutable variable called &lt;code&gt;i_entryPoint&lt;/code&gt;, which is of type &lt;code&gt;IEntryPoint&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;contract SimpleAccount is BaseAccount {
    IEntryPoint private immutable i_entryPoint;

    constructor(address entryPointAddress) {
        i_entryPoint = IEntryPoint(entryPointAddress);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also need to import the &lt;code&gt;IEntryPoint&lt;/code&gt; interface from the &lt;code&gt;account-abstraction&lt;/code&gt; module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol";

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we implement the &lt;code&gt;entryPoint&lt;/code&gt; function, which will return the trusted &lt;code&gt;i_entryPoint&lt;/code&gt;. This function will be a &lt;code&gt;view&lt;/code&gt; function and will override the inherited function from &lt;code&gt;BaseAccount&lt;/code&gt;, so we’ll add the &lt;code&gt;override&lt;/code&gt; keyword.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function entryPoint() public view override returns (IEntryPoint) {
    return i_entryPoint;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, your contract should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {BaseAccount} from "account-abstraction/core/BaseAccount.sol";
import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol";

contract SimpleAccount is BaseAccount {
    IEntryPoint private immutable i_entryPoint;

    constructor(address entryPointAddress) {
        i_entryPoint = IEntryPoint(entryPointAddress);
    }

    function entryPoint() public view override returns (IEntryPoint) {
        return i_entryPoint;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, you’ve successfully set up the basic structure of the &lt;code&gt;SimpleAccount&lt;/code&gt; contract, defining the trusted &lt;code&gt;EntryPoint&lt;/code&gt;. The next step would be to implement the &lt;code&gt;_validateSignature&lt;/code&gt; and the function that allows interaction with other contracts/accounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Account Contract Owner
&lt;/h3&gt;

&lt;p&gt;Developers can implement any method they prefer to validate a &lt;code&gt;UserOperation&lt;/code&gt;, but for this series, we will use an ECDSA signature for validation. To perform this validation, we need to have the owner of the account contract. The owner's address will be provided in the constructor and stored in an immutable variable, &lt;code&gt;i_owner&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how we define the owner in the constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Variable to store the owner's address
address private immutable i_owner;

constructor(address entryPointAddress, address owner) {
    i_entryPoint = IEntryPoint(entryPointAddress);
    // Set the owner
    i_owner = owner;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also want to add a getter function, &lt;code&gt;getOwner&lt;/code&gt;, which returns the owner's address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function getOwner() public view returns (address) {
    return i_owner;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we’ll work on the &lt;code&gt;validateUserOp&lt;/code&gt; function, but before diving into that, let's first understand the &lt;code&gt;UserOperation&lt;/code&gt; object and the data it contains.&lt;/p&gt;

&lt;h3&gt;
  
  
  UserOperation
&lt;/h3&gt;

&lt;p&gt;We've mentioned the &lt;code&gt;UserOperation&lt;/code&gt; object several times, but what exactly does it contain?&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;UserOperation&lt;/code&gt; consists of common fields like the sender, nonce, gas details, calldata, and more—similar to what you’d find in a typical transaction. In addition to these standard fields, it includes extra ones, like &lt;code&gt;accountFactory&lt;/code&gt;, &lt;code&gt;paymaster&lt;/code&gt;, and &lt;code&gt;signature&lt;/code&gt;. Don’t worry about the extra fields just yet; we’ll explore them later in this series.&lt;/p&gt;

&lt;p&gt;Once the &lt;code&gt;UserOperation&lt;/code&gt; is created by the user, it is sent to a bundler, which consolidates the fields and compresses the operation into a more compact format called &lt;code&gt;PackedUserOperation&lt;/code&gt;. This &lt;code&gt;PackedUserOperation&lt;/code&gt; will be sent to &lt;code&gt;EntryPoint&lt;/code&gt;. Here's what that looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct PackedUserOperation {
    address sender;
    uint256 nonce;
    bytes initCode;
    bytes callData;
    bytes32 accountGasLimits;
    uint256 preVerificationGas;
    bytes32 gasFees;
    bytes paymasterAndData;
    bytes signature;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, we only need to focus on two fields: &lt;code&gt;calldata&lt;/code&gt;, which is used for execution, and &lt;code&gt;signature&lt;/code&gt;, which is used to validate the &lt;code&gt;UserOperation&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;validateUserOp&lt;/code&gt; Function
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;validateUserOp&lt;/code&gt; function checks the validity of a &lt;code&gt;UserOperation&lt;/code&gt;. As we’ve seen in the &lt;code&gt;BaseAccount&lt;/code&gt; contract part of this article, this function is already implemented. However, the portion that handles signature validation—via the &lt;code&gt;_validateSignature&lt;/code&gt; function—needs to be implemented by the developer (in this case, us).&lt;/p&gt;

&lt;p&gt;We’ll use an ECDSA signature to verify that the &lt;code&gt;signature&lt;/code&gt; in the &lt;code&gt;UserOperation&lt;/code&gt; is signed by the account contract’s owner. If the signer matches the owner, we return &lt;code&gt;SIG_VALIDATION_SUCCESS&lt;/code&gt;. If not, we return &lt;code&gt;SIG_VALIDATION_FAILED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To verify the ECDSA signature, we can use the &lt;code&gt;ecrecover&lt;/code&gt; precompile, but it’s more efficient to use the OpenZeppelin ECDSA library, which simplifies the process and eliminates a lot of manual steps. Let’s install OpenZeppelin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;forge &lt;span class="nb"&gt;install &lt;/span&gt;OpenZeppelin/openzeppelin-contracts@v5.0.2 &lt;span class="nt"&gt;--no-commit&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If installed correctly, you should see a message like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Installed openzeppelin-contracts v5.0.2

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that OpenZeppelin is installed, we can begin writing the &lt;code&gt;_validateSignature&lt;/code&gt; function. The function signature is already defined in &lt;code&gt;BaseAccount&lt;/code&gt;, so we will override it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function _validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
    internal
    view
    override
    returns (uint256) {}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As this function uses the &lt;code&gt;PackedUserOperation&lt;/code&gt; struct, we need to import it from the &lt;code&gt;account-abstraction&lt;/code&gt; package. Additionally, we'll import the constants used for signature validation (&lt;code&gt;SIG_VALIDATION_SUCCESS&lt;/code&gt; and &lt;code&gt;SIG_VALIDATION_FAILED&lt;/code&gt;) from the same package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol";
import {SIG_VALIDATION_FAILED, SIG_VALIDATION_SUCCESS} from "account-abstraction/core/Helpers.sol";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we need to convert the &lt;code&gt;userOpHash&lt;/code&gt; into a structured message. We will use OpenZeppelin’s &lt;code&gt;MessageHashUtils.toEthSignedMessageHash&lt;/code&gt; function to convert &lt;code&gt;userOpHash&lt;/code&gt; into the appropriate Ethereum Signed Message format. To recover the signer's address from this structured message and the provided signature, we’ll use OpenZeppelin’s &lt;code&gt;ECDSA.recover&lt;/code&gt; function. Let’s import these libraries before writing the function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s use these libraries to retrieve the address of the signer and store it in the &lt;code&gt;messageSigner&lt;/code&gt; variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function _validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
    internal
    view
    override
    returns (uint256)
{
    bytes32 digest = MessageHashUtils.toEthSignedMessageHash(userOpHash);
    address messageSigner = ECDSA.recover(digest, userOp.signature);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the &lt;code&gt;messageSigner&lt;/code&gt;, we can compare it with the &lt;code&gt;i_owner&lt;/code&gt;. If they match, we return &lt;code&gt;SIG_VALIDATION_SUCCESS&lt;/code&gt;; otherwise, we return &lt;code&gt;SIG_VALIDATION_FAILED&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (messageSigner == i_owner) {
    return SIG_VALIDATION_SUCCESS;
} else {
    return SIG_VALIDATION_FAILED;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the complete &lt;code&gt;_validateSignature&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function _validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
    internal
    view
    override
    returns (uint256)
{
    bytes32 digest = MessageHashUtils.toEthSignedMessageHash(userOpHash);
    address messageSigner = ECDSA.recover(digest, userOp.signature);

    if (messageSigner == i_owner) {
        return SIG_VALIDATION_SUCCESS;
    } else {
        return SIG_VALIDATION_FAILED;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This completes the signature validation logic, ensuring the &lt;code&gt;UserOperation&lt;/code&gt; is signed by the account owner. The next step is to implement functionality for interacting with external accounts or contracts.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;execute&lt;/code&gt; Function
&lt;/h3&gt;

&lt;p&gt;This function allows the account contract to interact with external contracts or accounts. While the function's name is arbitrary (since the &lt;code&gt;EntryPoint&lt;/code&gt; contract directly executes the calldata), in this contract, we'll call it &lt;code&gt;execute&lt;/code&gt;. The user or dapp constructing the &lt;code&gt;UserOperation&lt;/code&gt; will need to construct calldata for execution.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;execute&lt;/code&gt; function will take three arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dest&lt;/code&gt;: The address of the contract or account to be called.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt;: The amount of ETH to be sent with the call.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;funcCallData&lt;/code&gt;: The calldata to be executed on the target address.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s the basic function structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function execute(address dest, uint256 value, bytes calldata funcCallData) external {}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this function allows transferring assets and executing actions on behalf of the user (i.e., the owner of the account contract), it must be protected to prevent unauthorized access. To enforce access control, we'll use the &lt;code&gt;_requireFromEntryPoint&lt;/code&gt; function provided by the &lt;code&gt;BaseAccount&lt;/code&gt; contract, making sure that only entry points can call this function.&lt;/p&gt;

&lt;p&gt;Next, the contract will perform the external call, and we’ll check if it was successful. If the call fails, we'll revert the transaction with a custom error, &lt;code&gt;SimpleAccount__CallFailed&lt;/code&gt;, which we define at the top of the contract.&lt;/p&gt;

&lt;p&gt;Here’s how the error is defined, and it should be added at the top of the contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error SimpleAccount__CallFailed();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete &lt;code&gt;execute&lt;/code&gt; function is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function execute(address dest, uint256 value, bytes calldata funcCallData) external {
    _requireFromEntryPoint(); // Restrict access to valid entry points
    (bool success, bytes memory result) = dest.call{value: value}(funcCallData);

    if (!success) {
        revert SimpleAccount__CallFailed(result); // Revert if the call fails
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this function in place, we now have a simple account contract that can interact with other contracts and accounts, complying with ERC-4337 and compatible with the &lt;code&gt;EntryPoint&lt;/code&gt; contract and bundlers.&lt;/p&gt;

&lt;p&gt;Here is the completed code for SimpleAccount:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import {BaseAccount} from "account-abstraction/core/BaseAccount.sol";
import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol";
import {PackedUserOperation} from "account-abstraction/interfaces/PackedUserOperation.sol";
import {SIG_VALIDATION_FAILED, SIG_VALIDATION_SUCCESS} from "account-abstraction/core/Helpers.sol";

import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";

contract SimpleAccount is BaseAccount {
    error SimpleAccount__CallFailed();

    IEntryPoint private immutable i_entryPoint;
    address private immutable i_owner;

    constructor(address entryPointAddress, address owner) {
        i_entryPoint = IEntryPoint(entryPointAddress);
        i_owner = owner;
    }

    function _validateSignature(PackedUserOperation calldata userOp, bytes32 userOpHash)
        internal
        view
        override
        returns (uint256)
    {
        bytes32 digest = MessageHashUtils.toEthSignedMessageHash(userOpHash);
        address messageSigner = ECDSA.recover(digest, userOp.signature);

        if (messageSigner == i_owner) {
            return SIG_VALIDATION_SUCCESS;
        } else {
            return SIG_VALIDATION_FAILED;
        }
    }

    function execute(address dest, uint256 value, bytes calldata funcCallData) external {
        _requireFromEntryPoint();
        (bool success,) = dest.call{value: value}(funcCallData);
        if (!success) {
            revert SimpleAccount__CallFailed();
        }
    }

    function entryPoint() public view override returns (IEntryPoint) {
        return i_entryPoint;
    }

    function getOwner() public view returns (address) {
        return i_owner;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;In this article, we walked through the process of developing a simple account contract that meets the basic requirements of ERC-4337. We explored each function, along with the libraries we used, such as the &lt;code&gt;account-abstraction&lt;/code&gt; package from eth-infinitism and OpenZeppelin’s ECDSA library.&lt;/p&gt;

&lt;p&gt;We also touched on important ERC-4337 concepts like bundlers and the entry point, though we haven't fully explored them yet. We’ll cover them in more detail as we progress through this series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;In the next article, we’ll shift focus to testing this contract and to gain better grasp of &lt;code&gt;UserOperation&lt;/code&gt;, including how to generate the &lt;code&gt;UserOperation&lt;/code&gt; hash via the entry point. If you’re excited to dive in, you can start writing tests now, which will help improve your understanding and ensure that the contract works as expected.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this article, and I hope you’ll follow along with the rest of the series!&lt;/p&gt;

</description>
      <category>solidity</category>
      <category>ethereum</category>
      <category>foundry</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>How to Track liquidity for token pairs on Uniswap</title>
      <dc:creator>Nikhil</dc:creator>
      <pubDate>Tue, 05 Sep 2023 09:30:00 +0000</pubDate>
      <link>https://forem.com/bitquery/how-to-track-liquidity-for-token-pairs-on-uniswap-hgc</link>
      <guid>https://forem.com/bitquery/how-to-track-liquidity-for-token-pairs-on-uniswap-hgc</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Today we will explore different aspects of the liquidity pools of any token pair on Uniswap. Uniswap is the most popular dapp in the crypto ecosystem; it is a decentralized exchange (DEX) that allows you to trade tokens on a blockchain. Out of the different types of DEX, Uniswap is called AMM (Automated money market), which relies on the concept of liquidity pools. Let’s refresh on liquidity pools and then explore data using Bitquery V2 APIs.&lt;/p&gt;

&lt;p&gt;Our &lt;a href="https://docs.bitquery.io/docs/intro/"&gt;V2 APIs&lt;/a&gt; allow us to access blockchain data in real-time using GraphQL subscriptions. You can try out V2 APIs &lt;a href="https://ide.bitquery.io/streaming"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding Token-Pair Liquidity&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;There are different types of DEX, which you can divide mainly into AMM DEX and Order Book DEX. An &lt;a href="https://academy.binance.com/en/articles/what-is-an-automated-market-maker-amm"&gt;AMM DEX&lt;/a&gt; like Uniswap, Sushiswap, etc. relies on algorithmic pricing and liquidity pools, while an order book DEX matches buyers and sellers directly based on their submitted orders.&lt;/p&gt;

&lt;p&gt;Liquidity, in the context of &lt;a href="https://uniswap.org/"&gt;Uniswap&lt;/a&gt; and similar AMM DEXs, refers to the availability of tokens in liquidity pools that facilitate trades. The pools are created by liquidity providers, who provide tokens to liquidity pools in exchange for earning part of trading fees. The depth of liquidity pools plays an important role in determining how easily traders can execute trades without causing significant fluctuations in market prices.&lt;/p&gt;

&lt;p&gt;Now that we have clarified what liquidity pools are and how they affect DEX working, let’s see how to fetch data using the &lt;a href="https://bitquery.io/products/dex"&gt;DEX API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Get All Liquidity Pools For a Token&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As anyone can create a liquidity pool for any token, there will be many pools for a single token, so let’s try to fetch a list of all the pools.&lt;/p&gt;

&lt;p&gt;In this query, we will be fetching a list of liquidity pools created for USDT, for which we need to set buy currency to the &lt;a href="https://explorer.bitquery.io/ethereum/token/0xdAC17F958D2ee523a2206206994597C13D831ec7"&gt;address of USDT token&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://ide.bitquery.io/uniswap-streaming-get-all-liquidity-pools-for-a-token/"&gt;this query&lt;/a&gt; in the GraphQL IDE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;EVM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;combined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;DEXTrades&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Trade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Buy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xdAC17F958D2ee523a2206206994597C13D831ec7"&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;limitBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Trade_Sell_Currency_SmartContract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;Trade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Dex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;ProtocolName&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;OwnerAddress&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;ProtocolVersion&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;PairTokenAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Symbol&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Buy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Sell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Get all pools for tokens on a certain DEX&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When we run the above query, you will notice all those pairs are from &lt;a href="https://community.bitquery.io/t/introducing-uniswap-v3-protocol-on-eth/336"&gt;Uniswap V3&lt;/a&gt;. This is because the API sends data for the default DEX if there is no DEX specified in the query.&lt;/p&gt;

&lt;p&gt;In this query, we have defined the name of the DEX from which we need data, which is “&lt;a href="https://explorer.bitquery.io/ethereum/smart_contract/0x7a250d5630b4cf539739df2c5dacb4c659f2488d"&gt;Uniswap V2&lt;/a&gt;”.&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://ide.bitquery.io/DEX-Streaming-Get-Pools-from-Any-DEX"&gt;this query&lt;/a&gt; in the GraphQL IDE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;EVM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;combined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;DEXTrades&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Trade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Buy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xdAC17F958D2ee523a2206206994597C13D831ec7"&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; 
                &lt;/span&gt;&lt;span class="n"&gt;Dex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Uniswap V2"&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;limitBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Trade_Sell_Currency_SmartContract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;Trade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Dex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;ProtocolName&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;OwnerAddress&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;ProtocolVersion&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;PairTokenAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Symbol&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Buy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Sell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use the smart contract address of the DEX instead of the protocol name, as the API might not recognize the name of some DEXs. Instead of selecting the protocolName field in the DEX filter, select the SmartContract field and input the smart contract address. Here is an example query that uses the smart contract address of DEX to filter.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Get liquidity of a liquidity pool&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we can fetch a list of liquidity pools, we can look into fetching details about each pool using the address of a specific liquidity pool.&lt;/p&gt;

&lt;p&gt;This query fetches the balance of tokens in the pool, which is the liquidity of that liquidity pool.&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://ide.bitquery.io/Uniswap-Streaming-Get-Depth-of-Liquidity-Pool"&gt;this query&lt;/a&gt; in the GraphQL IDE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;EVM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;combined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;BalanceUpdates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;BalanceUpdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x6ca298d2983ab03aa1da7679389d955a4efee15c"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;descendingByField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"balance"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;BalanceUpdate_Amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;BalanceUpdate&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Address&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Also Read: &lt;a href="https://bitquery.io/blog/opensea-nft-api"&gt;OpenSea NFT API&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Get number of trades for a liquidity pool&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We can also get the number of trades that happened in a pool, which will tell us how frequently that pool is being used.&lt;/p&gt;

&lt;p&gt;This query allows you to count all the successful trades in that pool. We have to filter using the address of the pool as an argument for Trade.Pair. SmartContract and using the count method to count distinct Transaction_Hash values.&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://ide.bitquery.io/Uniswap-Streaming-Get-Number-of-Trades-in-a-Pool"&gt;this query&lt;/a&gt; in the GraphQL IDE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;EVM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;combined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;DEXTrades&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;descending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Block_Date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Trade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Dex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852"&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;days&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;Trades&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Transaction_Hash&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TransactionStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Get all the recent token pairs created on Uniswap&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As anyone can create a liquidity pool without any restrictions, there are many pools created every day, so let’s see how to fetch details about created pools.&lt;/p&gt;

&lt;p&gt;Whenever a new pool is created using the &lt;a href="https://explorer.bitquery.io/ethereum/smart_contract/0x1f98431c8ad98523631ae4a59f267346ea31f984"&gt;Uniswap Factory Smart Contract&lt;/a&gt;, an event called PoolCreated is emitted. We can fetch all those events in order to get details about the newly created pool.&lt;/p&gt;

&lt;p&gt;To fetch events, we will use the &lt;a href="https://docs.bitquery.io/docs/examples/events/events_api/"&gt;Events API&lt;/a&gt; with the address of the Factory contract and the name of the event passed as arguments to the Log.SmartContract and Log.Signature.Name filters, respectively.&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://ide.bitquery.io/Uniswap-Streaming-Get-Recently-Created-Pools-by-PoolCreated-Event"&gt;this query&lt;/a&gt; in the GraphQL IDE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;EVM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0x1F98431c8aD98523631AE4a59f267346ea31F984"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; 
                &lt;/span&gt;&lt;span class="n"&gt;Signature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PoolCreated"&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;descending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Block_Date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;Arguments&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EVM_ABI_Integer_Value_Arg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;integer&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EVM_ABI_String_Value_Arg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EVM_ABI_Address_Value_Arg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EVM_ABI_BigInt_Value_Arg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;bigInteger&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EVM_ABI_Bytes_Value_Arg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;EVM_ABI_Boolean_Value_Arg&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;bool&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;Block&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Hash&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Also Read:  &lt;a href="https://bitquery.io/blog/ethereum-dex-graphql-api"&gt;Ethereum DEX GraphQL APIs with Examples&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Get all pools for token pair&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As anyone can create liquidity pools, there can be many pools for the same token pairs. As some pools will have low liquidity and some might have high liquidity, getting that data is important.&lt;/p&gt;

&lt;p&gt;To get a list of liquidity pools for a token pair, we will pass the token addresses for both tokens in the pair to Trade.Buy.Currency.SmartContract and Trade.Sell.Currency. SmartContract respectively. Finally, we will limit the result by setting the value of Trade_Dex_Pair_SmartContract to 1, so we will get all the unique pools for the particular token pair.&lt;/p&gt;

&lt;p&gt;Open &lt;a href="https://ide.bitquery.io/DEX-Streaming-Get-All-Pools-for-Token-Pair"&gt;this query&lt;/a&gt; in the GraphQL IDE.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;EVM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;combined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;eth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;DEXTrades&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Trade&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="n"&gt;Buy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; 
                &lt;/span&gt;&lt;span class="n"&gt;Sell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="n"&gt;is&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;limitBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Trade_Dex_Pair_SmartContract&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;Trade&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Dex&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;ProtocolName&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;OwnerAddress&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;ProtocolVersion&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;PairTokenAddress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;Pair&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Symbol&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Buy&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Sell&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;Currency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;SmartContract&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Get Uniswap Data From Arbitrum&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For demonstration, we have fetched the data from Uniswap Contracts deployed on Ethereum, but you can also fetch data from Uniswap Contracts deployed on Arbitrum by changing the value of the network field. Just make sure you change the addresses for all the tokens or DEXs to appropriate Arbitrum addresses.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Get Liquidity Pool Data From DEXs Besides Uniswap&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We explored Uniswap V3 and V2 data here, but you can also fetch data from &lt;a href="https://explorer.bitquery.io/ethereum/token/0x6b3595068778dd592e39a122f4f5a5cf09c90fe2"&gt;SushiSwap&lt;/a&gt;, &lt;a href="https://explorer.bitquery.io/ethereum/token/0xd533a949740bb3306d119cc777fa900ba034cd52"&gt;Curve&lt;/a&gt;, &lt;a href="https://explorer.bitquery.io/ethereum/token/0xba100000625a3754423978a60c9317c58a424e3d"&gt;Balancer&lt;/a&gt;, and many other DEXs.&lt;/p&gt;

&lt;p&gt;The Bitquery V2 API currently supports Ethereum, Arbitrum, and BSC, so you will be able to fetch data from many DEXs on these chains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Also Read:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bitquery.io/blog/top-uniswap-APIs"&gt;Top Uniswap APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bitquery.io/blog/tron-dex-api"&gt;Tron DEX APIs with Bitquery | GraphQL APIs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>tutorial</category>
      <category>graphql</category>
      <category>blockchain</category>
    </item>
  </channel>
</rss>
