master de2aeff5f5c2 cached
48 files
136.1 KB
33.5k tokens
355 symbols
1 requests
Download .txt
Repository: blockchain/api-v1-client-java
Branch: master
Commit: de2aeff5f5c2
Files: 48
Total size: 136.1 KB

Directory structure:
gitextract_nic81c8s/

├── .gitignore
├── CHANGELOG.md
├── LICENSE.txt
├── README.md
├── docs/
│   ├── blockexplorer.md
│   ├── exchangerates.md
│   ├── pushtx.md
│   ├── receive.md
│   ├── statistics.md
│   └── wallet.md
├── pom.xml
└── src/
    ├── main/
    │   └── java/
    │       └── info/
    │           └── blockchain/
    │               └── api/
    │                   ├── APIException.java
    │                   ├── HttpClient.java
    │                   ├── HttpClientInterface.java
    │                   ├── OkClient.java
    │                   ├── Util.java
    │                   ├── blockexplorer/
    │                   │   ├── BlockExplorer.java
    │                   │   └── entity/
    │                   │       ├── Address.java
    │                   │       ├── AddressSummary.java
    │                   │       ├── Balance.java
    │                   │       ├── Block.java
    │                   │       ├── FilterType.java
    │                   │       ├── Input.java
    │                   │       ├── LatestBlock.java
    │                   │       ├── MultiAddress.java
    │                   │       ├── MultiAddressBalance.java
    │                   │       ├── Output.java
    │                   │       ├── SimpleBlock.java
    │                   │       ├── Transaction.java
    │                   │       ├── UnspentOutput.java
    │                   │       └── XpubFull.java
    │                   ├── etc/
    │                   │   └── Base58.java
    │                   ├── exchangerates/
    │                   │   ├── Currency.java
    │                   │   └── ExchangeRates.java
    │                   ├── pushtx/
    │                   │   └── PushTx.java
    │                   ├── receive/
    │                   │   ├── CallbackLog.java
    │                   │   ├── Receive.java
    │                   │   └── ReceiveResponse.java
    │                   ├── statistics/
    │                   │   ├── Chart.java
    │                   │   ├── Point.java
    │                   │   ├── Statistics.java
    │                   │   └── StatisticsResponse.java
    │                   └── wallet/
    │                       ├── Wallet.java
    │                       └── entity/
    │                           ├── Address.java
    │                           ├── CreateWalletResponse.java
    │                           └── PaymentResponse.java
    └── test/
        └── java/
            └── info/
                └── blockchain/
                    └── api/
                        ├── AppTest.java
                        └── blockexplorer/
                            └── BlockExplorerTest.java

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*.class

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.ear

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

# Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties

# Eclipse
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders

# Eclipse Core
.project

# External tool builders
.externalToolBuilders/

# Locally stored "Eclipse launch configurations"
*.launch

# PyDev specific (Python IDE for Eclipse)
*.pydevproject

# CDT-specific (C/C++ Development Tooling)
.cproject

# JDT-specific (Eclipse Java Development Tools)
.classpath

# Java annotation processor (APT)
.factorypath

# PDT-specific (PHP Development Tools)
.buildpath

# sbteclipse plugin
.target

# Tern plugin
.tern-project

# TeXlipse plugin
.texlipse

# STS (Spring Tool Suite)
.springBeans

# Code Recommenders
.recommenders/


================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [2.0.0] - 2017-06-16
### Added
- This CHANGELOG file to record all the changes in current and following version of this project.
- Filter, limit, and offset optional parameters to getAddress in BlockExplorer.
- Confirms and limit optional parameters to getUnspentOutputs in BlockExplorer.
- A new getBalance method in BlockExplorer.
- A new getMultiAddress method in BlockExplorer.
- A new getXpub method in BlockExplorer.
- A new toFiat method in ExchangeRates.
- A new checkGap method in Receive.
- A new getCallbackLog method in Receive.
- A new getChart method in Statistics.
- A new getPools method in Statistics.

### Changed
- ExchangeRate series interfaces to allow API be used for all methods.
- Receive series interfaces to allow API be used for all methods.
- Statistics series interfaces to allow API be used for all methods.
- Moved createWallet functions from CreateWallet class to Wallet class.

### Deprecated
- “Get transaction and block by index” interfaces in BlockExplorer.

### Removed
- Method getInventoryData in BlockExplorer
- Parameter “note” from send and sendMany methods in Wallet.
- Parameter “confirmations” from listAddress and getAddress method in Wallet.
- Method consolidate in Wallet.


================================================
FILE: LICENSE.txt
================================================
The MIT License (MIT)

Copyright (c) 2014 Blockchain

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Blockchain API library (Java, v2.0.0)

An official Java library for interacting with the Blockchain.info API (Java 1.6 required).

### Getting started

If you prefer building from source:

```
$ git clone https://github.com/blockchain/api-v1-client-java
$ cd api-v1-client-java
$ mvn install
```

We also provide a snapshot Maven repository for users who prefer managing dependencies that way.

Add the following to your pom.xml:
```xml
<project>
    ...
    <repositories>
        ...
        <repository>
            <id>api-v1-client-java-mvn-repo</id>
            <url>https://raw.githubusercontent.com/blockchain/api-v1-client-java/mvn-repo/</url>
            <snapshots>
                <enabled>true</enabled>
                <updatePolicy>always</updatePolicy>
            </snapshots>
        </repository>
    </repositories>
    ...
    <dependencies>
        ...
        <dependency>
  	        <groupId>info.blockchain</groupId>
  	        <artifactId>api</artifactId>
  	        <version>LATEST</version> <!-- for a specific version see the list of tags -->
        </dependency>
    <dependencies>
    ...
</project>
```

The above Maven repository also works with Gradle.

Add this to your `build.gradle`:
```
...
repositories {
    ...
    maven {
        url = 'https://raw.githubusercontent.com/blockchain/api-v1-client-java/mvn-repo'
    }
}
...
dependencies {
    ...
    compile 'info.blockchain:api:2.0.2'
}
...
```

Note that the above procedures require Maven or Gradle. If you do not wish to use Maven or Gradle, please compile the source manually.

The library consists of the following packages:

* `info.blockchain.api.blockexplorer` ([docs](docs/blockexplorer.md)) ([api/blockchain_api][api1])
* `info.blockchain.api.createwallet` ([docs](docs/createwallet.md)) ([api/create_wallet][api2])
* `info.blockchain.api.exchangerates` ([docs](docs/exchangerates.md)) ([api/exchange\_rates\_api][api3])
* `info.blockchain.api.pushtx` ([docs](docs/pushtx.md)) ([pushtx][api7])
* `info.blockchain.api.receive` ([docs](docs/receive.md)) ([api/api_receive][api4])
* `info.blockchain.api.statistics` ([docs](docs/statistics.md)) ([api/charts_api][api5])
* `info.blockchain.api.wallet` ([docs](docs/wallet.md)) ([api/blockchain\_wallet\_api][api6])

In order to use `createwallet` and `wallet` you need to run an instance of [service-my-wallet-v3](https://github.com/blockchain/service-my-wallet-v3).

### Error handling

All methods may throw exceptions caused by incorrectly passed parameters or other problems. If a call is rejected server-side, the `APIException` exception will be thrown. In case of a network error, the `IOException` exception will be thrown.

### Connection timeouts

It is possible to set arbitrary connection timeouts.

```java
info.blockchain.api.HttpClient.TIMEOUT_MS = 2000; // time out after 2000 milliseconds
```

### Request limits and API keys

In order to prevent abuse some API methods require an API key approved with some basic contact information and a description of its intended use. Please request an API key [here](https://blockchain.info/api/api_create_code).

The same API key can be used to bypass the request limiter.

### Code Coverage Report generation

To generate the code coverage report, execute the following command:
```sh
mvn clean verify
```

This will generate a code coverage report in `target/site/jacoco/index.html`.


[api1]: https://blockchain.info/api/blockchain_api
[api2]: https://blockchain.info/api/create_wallet
[api3]: https://blockchain.info/api/exchange_rates_api
[api4]: https://blockchain.info/api/api_receive
[api5]: https://blockchain.info/api/charts_api
[api6]: https://blockchain.info/api/blockchain_wallet_api
[api7]: https://blockchain.info/pushtx


================================================
FILE: docs/blockexplorer.md
================================================
## `info.blockchain.api.blockexplorer` package

The `blockexplorer` package contains the `BlockExplorer` class that reflects the functionality documented at  https://blockchain.info/api/blockchain_api. It can be used to query the block chain, fetch block, transaction and address data, get unspent outputs for an address etc.

Example usage:

```java

package test;
import info.blockchain.api.blockexplorer.*;

public class App
{
    public static void main(String[] args) throws Exception
    {
    	// instantiate a block explorer
    	BlockExplorer blockExplorer = new BlockExplorer();

    	// get a transaction by hash and list the value of all its inputs
    	Transaction tx = blockExplorer.getTransaction("df67414652722d38b43dcbcac6927c97626a65bd4e76a2e2787e22948a7c5c47");
    	for (Input i : tx.getInputs())
    	{
    		System.out.println(i.getPreviousOutput().getValue());
    	}

    	// get a block by hash and read the number of transactions in the block
    	Block block = blockExplorer.getBlock("0000000000000000050fe18c9b961fc7c275f02630309226b15625276c714bf1");
    	int numberOfTxsInBlock = block.getTransactions().size();

    	// get an address and read its final balance
    	Address address = blockExplorer.getAddress("1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw");
    	long finalBalance = address.getFinalBalance();
    	
    	// get an address and read its final balance with filter, limit, and offset
        Address address = client.getAddress("1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW", FilterType.All, 10, 5);    	
        long finalBalance = address.getFinalBalance();


    	// get a list of currently unconfirmed transactions and print the relay IP address for each
    	List<Transaction> unconfirmedTxs = blockExplorer.getUnconfirmedTransactions();
    	for (Transaction unconfTx : unconfirmedTxs)
    		System.out.println(tx.getRelayedBy());

    	// calculate the balanace of an address by fetching a list of all its unspent outputs
    	List<UnspentOutput> outs = blockExplorer.getUnspentOutputs("1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw");
    	long totalUnspentValue = 0;
    	for (UnspentOutput out : outs)
    		totalUnspentValue += out.getValue();

    	// calculate the balanace of an address by fetching a list of all its unspent outputs with confirmations and limit
    	List<UnspentOutput> outs = blockExplorer.getUnspentOutputs(Arrays.asList("1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw"), 5, 10);
    	long totalUnspentValue = 0;
    	for (UnspentOutput out : outs)
    		totalUnspentValue += out.getValue();
    		
    	// returns the address balance summary for each address provided
    	Map<String, Balance> balances = blockExplorer.getBalance(Arrays.asList("1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw"), FilterType.All);
    	
    	// returns an aggregated summary on all addresses provided.
    	MultiAddress multiAddr = blockExplorer.getMultiAddress(Arrays.asList("1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw"), FilterType.All, 10, 5);
    	
    	// returns xpub summary on a xpub provided, with its overall balance and its transactions.
    	XpubFull xpub = blockExplorer.getXpub("xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn", FilterType.All, 10, 5);
    	
    	// get the latest block on the main chain and read its height
    	LatestBlock latestBlock = blockExplorer.getLatestBlock();
    	long latestBlockHeight = latestBlock.getHeight();

    	// use the previous block height to get a list of blocks at that height
		// and detect a potential chain fork
    	List<Block> blocksAtHeight = blockExplorer.getBlocksAtHeight(latestBlockHeight);
    	if (blocksAtHeight.size() > 1)
    		System.out.println("The chain has forked!");
    	else
    		System.out.println("The chain is still in one piece :)");

    	// get a list of all blocks that were mined today since 00:00 UTC
    	List<SimpleBlock> todaysBlocks = blockExplorer.getBlocks();
    	System.out.println(todaysBlocks.size());
    }
}

```

================================================
FILE: docs/exchangerates.md
================================================
## `info.blockchain.api.exchangerates` package

The `exchangerates` package contains the `ExchangeRates` class that reflects the functionality documented at https://blockchain.info/api/exchange_rates_api. It allows users to get price tickers for most major currencies and directly convert fiat amounts to BTC.

Example usage:

```java

package test;
import info.blockchain.api.exchangerates.*;

public class App
{
    public static void main(String[] args) throws Exception
    {
        // get the Exchange object
        ExchangeRates exchange = new ExchangeRates();
        
		// get the ticker map
        Map<String, Currency> ticker = exchange.getTicker();
        BigDecimal BTCUSDsell = ticker.get("USD").getSell();

        // convert 5362 EUR to BTC
        BigDecimal EURToBTC = exchange.toBTC("EUR", new BigDecimal(53620));
        
        // convert 100,000,000 satoshi to USD
        BigDecimal BTCToUSD = exchange.toFiat("USD", new BigDecimal(100000000));
    }
}

```

================================================
FILE: docs/pushtx.md
================================================
## `info.blockchain.api.pushtx` package

The `pushtx` package contains the `PushTx` class that reflects the functionality provided at https://blockchain.info/pushtx . It allows users to broadcast hex encoded transactions to the bitcoin network.

Example usage:

```java

package test;
import info.blockchain.api.pushtx.*;

public class App
{
    public static void main(String[] args) throws Exception
    {
		PushTx.pushTx( "0100000001fd468e431cf5797b108e4d22724e1e055b3ecec59af4ef17b063afd36d3c5cf6010000008c4930460221009918eee8be186035be8ca573b7a4ef7bc672c59430785e5390cc375329a2099702210085b86387e3e15d68c847a1bdf786ed0fdbc87ab3b7c224f3c5490ac19ff4e756014104fe2cfcf0733e559cbf28d7b1489a673c0d7d6de8470d7ff3b272e7221afb051b777b5f879dd6a8908f459f950650319f0e83a5cf1d7c1dfadf6458f09a84ba80ffffffff01185d2033000000001976a9144be9a6a5f6fb75765145d9c54f1a4929e407d2ec88ac00000000");
    }
}

```


================================================
FILE: docs/receive.md
================================================
## `info.blockchain.api.receive` package

The `receive` package contains the `Receive` class that reflects the functionality documented at https://blockchain.info/api/api_receive. It allows merchants to derive addresses from their HD wallets and be notified upon payment.

Example usage:

```java

package test;
import info.blockchain.api.receive.*;

public class App
{
    public static void main(String[] args) throws Exception
    {
        Receive receive = new Receive("YOUR_API_CODE");
        
    	ReceiveResponse response = receive.receive(
    	        "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
    			URLEncoder.encode("https://your.url.com?secret=foo", "UTF-8"));

    	System.out.println(String.format("The receiving address is %s. "
    			+ "The address index is %d",
    			response.getReceivingAddress(),
    			response.getIndex()));
    			
    	int xpubGap = receive.checkGap("xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
    	CallbackLog cbLog = receive.getCallbackLog(URLEncoder.encode("https://your.url.com?secret=foo", "UTF-8"));
    }
}

```

================================================
FILE: docs/statistics.md
================================================
## `info.blockchain.api.statistics` package

The `statistics` package contains the `Statistics` class that reflects the functionality documented at at https://blockchain.info/api/charts_api. It makes various network statistics available, such as the total number of blocks in existence, next difficulty retarget block, total BTC mined in the past 24 hours etc.

Example usage:

```java

package test;
import info.blockchain.api.statistics.*;

public class App
{
    public static void main(String[] args) throws Exception
    {
        Statistics stats = new Statistics();
        
    	StatisticsResponse stats = stats.get();
    	System.out.println(String.format("The current difficulty is %s. "
    			+ "The next retarget will happen in %s hours.",
    			stats.getDifficulty(),
    			(stats.getNextRetarget() - stats.getTotalBlocks()) * stats.getMinutesBetweenBlocks() / 60));
    			
        Chart txPerSec = stats.getChart("transactions-per-second", "5weeks", "8hours");
        
        Map<String, Integer> pools = stats.getPools("5weeks");
    }
}

```

================================================
FILE: docs/wallet.md
================================================
## `info.blockchain.api.wallet` package

The `wallet` package contains the `Wallet` class that reflects the functionality documented at at https://blockchain.info/api/blockchain_wallet_api. It allows users to directly interact with their existing Blockchain.info wallet, send funds, manage addresses etc.

Example usage:

```java

package test;
import info.blockchain.api.wallet.*;

public class App
{
    public static void main(String[] args) throws Exception
    {
    
        CreateWalletResponse wallet = Wallet.create(
        	    "http://localhost:3000/",
        	    "YOUR_SUPER_SECURE_PASSWORD",
        	    "YOUR_API_CODE");
    
        System.out.println(wallet.getIdentifier());
        	
    	Wallet wallet = new Wallet(
    	        "http://localhost:3000/",
    	        "YOU_API_CODE",
    	        "YOUR_GUID",
    	        "YOUR_SUPER_SECURE_PASSWORD");

    	// get an address from your wallet and include only transactions with up to 3
    	// confirmations in the balance
    	Address addr = wallet.getAddress("1JzSZFs2DQke2B3S4pBxaNaMzzVZaG4Cqh", 3);
    	System.out.println(String.format("The balance is %s", addr.getBalance()));

    	// send 0.2 bitcoins with a custom fee of 0.01 BTC and a note
        // public notes require a minimum transaction size of 0.005 BTC
    	PaymentResponse payment = wallet.send("1dice6YgEVBf88erBFra9BHf6ZMoyvG88", 20000000L, null, 1000000L);
    	System.out.println(String.format("The payment TX hash is %s", payment.getTxHash()));

    	long totalBalance = wallet.getBalance();
    	System.out.println(String.format("The total wallet balance is %s", totalBalance));

    	// list all addresses and their balances 
    	List<Address> addresses = wallet.listAddresses();
    	for (Address a : addresses)
    	{
    		System.out.println(String.format("The address %s has a balance of %s satoshi",
    				a.getAddress(), a.getBalance()));
    	}

    	// archive an old address
    	wallet.archiveAddress("1JzSZFs2DQke2B3S4pBxaNaMzzVZaG4Cqh");

    	// create a new address and attach a label to it
    	Address newAddr = wallet.newAddress("test label 123");
    	System.out.println(String.format("The new address is %s", newAddr.getAddress()));
    }
}

```

================================================
FILE: pom.xml
================================================
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>info.blockchain</groupId>
  <artifactId>api</artifactId>
  <version>2.0.2</version>
  <packaging>jar</packaging>

  <name>api</name>
  <url>https://blockchain.info/api</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <github.global.server>github</github.global.server>
    <maven.compiler.source>1.6</maven.compiler.source>
    <maven.compiler.target>1.6</maven.compiler.target>
  </properties>

  <distributionManagement>
    <repository>
      <id>internal.repo</id>
      <name>Temporary Staging Repository</name>
      <url>file://${project.build.directory}/mvn-repo</url>
    </repository>
  </distributionManagement>

  <dependencies>

    <!-- JSON marshalling/unmarshalling -->
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.3</version>
    </dependency>

    <dependency>
      <groupId>com.squareup.okhttp</groupId>
      <artifactId>okhttp</artifactId>
      <version>2.6.0</version>
    </dependency>

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.4</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-deploy-plugin</artifactId>
        <version>2.8.2</version>
        <configuration>
          <altDeploymentRepository>internal.repo::default::file://${project.build.directory}/mvn-repo</altDeploymentRepository>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.7.5.201505241946</version>
        <executions>
          <execution>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
          </execution>
          <execution>
            <id>report</id>
            <phase>prepare-package</phase>
            <goals>
              <goal>report</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <plugin>
        <groupId>com.github.github</groupId>
        <artifactId>site-maven-plugin</artifactId>
        <version>0.12</version>
        <configuration>
          <!-- git commit message -->
          <message>Maven artifacts for ${project.version}</message>
          <!-- disable webpage processing -->
          <noJekyll>true</noJekyll>
          <!-- matches distribution management repository url above -->
          <outputDirectory>${project.build.directory}/mvn-repo</outputDirectory>
          <!-- Keep older versions around -->
          <merge>true</merge>
          <!-- remote branch name -->
          <branch>refs/heads/mvn-repo</branch>
          <includes><include>**/*</include></includes>
          <!-- github repo name -->
          <repositoryName>api-v1-client-java</repositoryName>
          <!-- github username -->
          <repositoryOwner>blockchain</repositoryOwner>
        </configuration>
        <executions>
          <!-- run site-maven-plugin's 'site' target as part of the build's normal 'deploy' phase -->
          <execution>
            <goals>
              <goal>site</goal>
            </goals>
            <phase>deploy</phase>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

</project>


================================================
FILE: src/main/java/info/blockchain/api/APIException.java
================================================
package info.blockchain.api;

/**
 * The class `APIException` represents a failed call to the Blockchain API. Whenever
 * the server is unable to process a request (usually due to parameter validation errors),
 * an instance of this class is thrown.
 */
public class APIException extends Exception {
    private static final long serialVersionUID = -7731961787745059713L;

    public APIException (String message) {
        super(message);
    }
}


================================================
FILE: src/main/java/info/blockchain/api/HttpClient.java
================================================
package info.blockchain.api;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Map.Entry;

/**
 * This is a utility class for performing API calls using GET and POST requests. It is
 * possible to override these calls by implementing HttpClientInterface. Add the new
 * implementation via setCustomHttpClient(...), such that it will get used globally.
 */
public class HttpClient implements HttpClientInterface {
    private static final String BASE_URL = "https://blockchain.info/";

    public volatile static int TIMEOUT_MS = 10000;

    private static HttpClientInterface instance;

    public synchronized static HttpClientInterface getInstance () {
        if (instance == null) {
            // Thread Safe. Might be costly operation in some case
            synchronized (HttpClient.class) {
                if (instance == null) {
                    instance = new HttpClient();
                }
            }
        }
        return instance;
    }

    public static void setCustomHttpClient (HttpClientInterface httpClient) {
        instance = httpClient;
    }

    /**
     * Perform a GET request on a Blockchain.info API resource.
     *
     * @param resource Resource path after https://blockchain.info/api/
     * @param params   Map containing request parameters
     * @return String response
     * @throws APIException If the server returns an error
     */
    public String get (String resource, Map<String, String> params) throws APIException, IOException {
        return openURL(BASE_URL, resource, params, "GET");
    }

    public String get (String baseURL, String resource, Map<String, String> params) throws APIException, IOException {
        return openURL(baseURL, resource, params, "GET");
    }

    /**
     * Perform a POST request on a Blockchain.info API resource.
     *
     * @param resource Resource path after https://blockchain.info/api/
     * @param params   Map containing request parameters
     * @return String response
     * @throws APIException If the server returns an error
     * @throws IOException  If the server is not reachable
     */
    public String post (String resource, Map<String, String> params) throws APIException, IOException {
        return openURL(BASE_URL, resource, params, "POST");
    }

    public String post (String baseURL, String resource, Map<String, String> params) throws APIException, IOException {
        return openURL(baseURL, resource, params, "POST");
    }

    private static String openURL (String baseURL, String resource, Map<String, String> params, String requestMethod) throws APIException, IOException {
        String encodedParams = urlEncodeParams(params);
        URL url = null;
        APIException apiException = null;
        IOException ioException = null;

        String responseStr = null;

        if (requestMethod.equals("GET")) {
            if (encodedParams.isEmpty()) {
                url = new URL(baseURL + resource);
            } else {
                url = new URL(baseURL + resource + '?' + encodedParams);
            }            
        } else if (requestMethod.equals("POST")) {
            url = new URL(baseURL + resource);
        }

        HttpURLConnection conn = null;

        try {
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod(requestMethod);
            conn.setConnectTimeout(TIMEOUT_MS);

            if (requestMethod.equals("POST")) {
                byte[] postBytes = encodedParams.getBytes("UTF-8");
                conn.setDoOutput(true);
                conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                conn.setRequestProperty("Content-Length", String.valueOf(postBytes.length));
                conn.getOutputStream().write(postBytes);
                conn.getOutputStream().close();
            }

            if (conn.getResponseCode() != 200) {
                apiException = new APIException(inputStreamToString(conn.getErrorStream()));
            } else {
                responseStr = inputStreamToString(conn.getInputStream());
            }
        } catch (IOException e) {
            ioException = e;
        } finally {
            try {
                if (apiException != null) {
                    conn.getErrorStream().close();
                }
                conn.getInputStream().close();
            } catch (Exception ex) {
            }

            if (ioException != null) {
                throw ioException;
            }

            if (apiException != null) {
                throw apiException;
            }
        }

        return responseStr;
    }

    private static String urlEncodeParams (Map<String, String> params) {
        String result = "";

        if (params != null && params.size() > 0) {
            try {
                StringBuilder data = new StringBuilder();
                for (Entry<String, String> kvp : params.entrySet()) {
                    if (data.length() > 0) {
                        data.append('&');
                    }

                    data.append(URLEncoder.encode(kvp.getKey(), "UTF-8"));
                    data.append('=');
                    data.append(URLEncoder.encode(kvp.getValue(), "UTF-8"));
                }
                result = data.toString();
            } catch (UnsupportedEncodingException e) {
            }
        }

        return result;
    }

    private static String inputStreamToString (InputStream is) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));

        StringBuilder responseStringBuilder = new StringBuilder();
        String line = "";

        while ((line = reader.readLine()) != null) {
            responseStringBuilder.append(line);
        }

        reader.close();

        return responseStringBuilder.toString();
    }
}


================================================
FILE: src/main/java/info/blockchain/api/HttpClientInterface.java
================================================
package info.blockchain.api;

import java.io.IOException;
import java.util.Map;

/**
 * This is a utility class for performing API calls using GET and POST requests. It is
 * possible to override these calls with your own implementation by overriding the 'get'
 * and 'post' methods.
 */
public interface HttpClientInterface {

    String get (String resource, Map<String, String> params) throws APIException, IOException;

    String get (String baseURL, String resource, Map<String, String> params) throws APIException, IOException;

    String post (String resource, Map<String, String> params) throws APIException, IOException;

    String post (String baseURL, String resource, Map<String, String> params) throws APIException, IOException;

}


================================================
FILE: src/main/java/info/blockchain/api/OkClient.java
================================================
package info.blockchain.api;

import com.squareup.okhttp.*;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * This class implements the {@link HttpClientInterface} using OkHttp.
 * OkHttp handles Gzip, connection pooling, caching and retries out of the box.
 * The library also provides a set of fluent builders for creating requests and responses.
 * For more information about OkHttp visit http://square.github.io/okhttp/
 */
public class OkClient implements HttpClientInterface {

    public static final String URL_SCHEME = "https";
    public static final String URL_HOST = "blockchain.info";
    public static final int TIMEOUT_MS = 10000;

    private static OkHttpClient okHttpClientInstance = null;
    private static OkClient okClientInstance = null;

    /**
     * Private constructor to prevent instantiation.
     */
    private OkClient() {
        //prevent instantiation through reflection.
        if (okClientInstance != null) {
            throw new IllegalStateException("Already initialized.");
        }
    }

    /**
     * OkClient singleton
     */
    public synchronized static OkClient getOkClientInstance() {
        if (okClientInstance == null) {
            okClientInstance = new OkClient();
        }
        return okClientInstance;
    }

    /**
     * OkHttpClient singleton
     */
    private synchronized static OkHttpClient getOkHttpClientInstance() {
        if (okHttpClientInstance == null) {
            okHttpClientInstance = new OkHttpClient();
            okHttpClientInstance.setConnectTimeout(TIMEOUT_MS, TimeUnit.MILLISECONDS);
        }
        return okHttpClientInstance;
    }

    /**
     * This method makes a get request to a specific API resource.
     * Request is built using the OkHttp {@link HttpUrl} builder.
     *
     * @param baseURL  The API base URL
     * @param resource The API resource being requested.
     * @param params   A set of params sent to the resource
     * @return Returns a response as a {@link String}
     * @throws IOException Thrown if the request was unsuccessful.
     */
    @Override
    public String get(String baseURL, String resource, Map<String, String> params) throws IOException {
        HttpUrl.Builder url = getHttpUrlBuilder(baseURL, resource);

        for (String paramName : params.keySet()) {
            url.addEncodedQueryParameter(paramName, params.get(paramName));
        }

        return getInternal(url.build());
    }

    /**
     * This method makes a get request to a specific API resource.
     * Request is built using the OkHttp {@link HttpUrl} builder.
     *
     * @param resource The API resource being requested.
     * @param params   A set of params sent to the resource
     * @return Returns a response as a {@link String}
     * @throws IOException Thrown if the request was unsuccessful.
     */
    @Override
    public String get(String resource, Map<String, String> params) throws IOException {
        HttpUrl.Builder url = getHttpUrlBuilder(resource);

        for (String paramName : params.keySet()) {
            url.addEncodedQueryParameter(paramName, params.get(paramName));
        }

        return getInternal(url.build());
    }

    private String getInternal(HttpUrl url) throws IOException {
        Request request = new Request.Builder()
                .url(url)
                .build();

        return makeRequest(request);
    }

    /**
     * This method makes a post request to a specific API resource.
     * Request is built using the OkHttp {@link HttpUrl} builder.
     * Request body is built using the OkHttp {@link FormEncodingBuilder} builder .
     *
     * @param baseURL  The API base URL
     * @param resource The API resource being requested.
     * @param params   A set of params sent to the resource
     * @return Returns a response as a {@link String}
     * @throws IOException Thrown if the request was unsuccessful.
     */
    @Override
    public String post(String baseURL, String resource, Map<String, String> params) throws IOException {
          HttpUrl.Builder url = getHttpUrlBuilder(baseURL, resource);

          return postInternal(url.build(), params);
    }

    /**
     * This method makes a post request to a specific API resource.
     * Request is built using the OkHttp {@link HttpUrl} builder.
     * Request body is built using the OkHttp {@link FormEncodingBuilder} builder .
     *
     * @param resource The API resource being requested.
     * @param params   A set of params sent to the resource
     * @return Returns a response as a {@link String}
     * @throws IOException Thrown if the request was unsuccessful.
     */
    @Override
    public String post(String resource, Map<String, String> params) throws IOException {
        HttpUrl.Builder url = getHttpUrlBuilder(resource);

        return postInternal(url.build(), params);
    }

    private String postInternal(HttpUrl url, Map<String, String> params) throws IOException {
        FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder();
        for (String paramName : params.keySet()) {
            formEncodingBuilder.addEncoded(paramName, params.get(paramName));
        }

        Request request = new Request.Builder()
                .url(url)
                .post(formEncodingBuilder.build())
                .build();

        return makeRequest(request);
  }

    private String makeRequest(Request request) throws IOException {
      Response response = getOkHttpClientInstance().newCall(request).execute();
      if (isNotSuccessfulResponse(response))
          throw new IOException(String.format("Unsuccessful call to %s Response: %s",
                  request.urlString(),
                  response));

      return response.body().string();
    }

    private HttpUrl.Builder getHttpUrlBuilder(String resource) {
        HttpUrl.Builder url = new HttpUrl.Builder();
        url.scheme(URL_SCHEME)
                .host(URL_HOST)
                .addPathSegment(resource);

        return url;
    }

    private HttpUrl.Builder getHttpUrlBuilder(String baseURL, String resource) throws MalformedURLException {
        HttpUrl url = HttpUrl.parse(baseURL);
        if (url == null) {
          throw new MalformedURLException();
        }

        HttpUrl.Builder urlBuilder = url.newBuilder();
        urlBuilder.addPathSegment(resource);

        return urlBuilder;
    }

    private boolean isNotSuccessfulResponse(Response response) {
        return !response.isSuccessful();
    }
}


================================================
FILE: src/main/java/info/blockchain/api/Util.java
================================================
package info.blockchain.api;

public class Util {
    public static final int SATOSHI_IN_BTC = 100000000;
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/BlockExplorer.java
================================================
package info.blockchain.api.blockexplorer;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import info.blockchain.api.APIException;
import info.blockchain.api.HttpClient;
import info.blockchain.api.blockexplorer.entity.*;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.*;

/**
 * The BlockExplorer class reflects the functionality documented at
 * https://blockchain.info/api/blockchain_api. It can be used to query the block chain,
 * fetch block, transaction and address data, get unspent outputs for an address etc.
 */
public class BlockExplorer {
    private final String apiCode;

    public BlockExplorer () {
        this(null);
    }

    /**
     * @param apiCode Blockchain.info API code (optional, nullable)
     */
    public BlockExplorer (String apiCode) {
        this.apiCode = apiCode;
    }

    /**
     * Gets a single transaction based on a transaction index.
     *
     * @param txIndex Transaction index
     * @return An instance of the {@link Transaction} class
     * @throws APIException If the server returns an error
     * @deprecated As of 1.1.5, will be removed in future releases
     */
    @Deprecated
    public Transaction getTransaction (long txIndex) throws APIException, IOException {
        return getTransaction(String.valueOf(txIndex));
    }

    /**
     * Gets a single transaction based on a transaction hash.
     *
     * @param txHash Transaction hash
     * @return An instance of the {@link Transaction} class
     * @throws APIException If the server returns an error
     */
    public Transaction getTransaction (String txHash) throws APIException, IOException {
        String response = HttpClient.getInstance().get("rawtx/" + txHash, buildBasicRequest());
        JsonObject txJson = new JsonParser().parse(response).getAsJsonObject();
        return new Transaction(txJson);
    }

    /**
     * Gets a single block based on a block index.
     *
     * @param blockIndex Block index
     * @return An instance of the {@link Block} class
     * @throws APIException If the server returns an error
     * @deprecated As of 1.1.5, will be removed in future releases
     */
    @Deprecated
    public Block getBlock (long blockIndex) throws APIException, IOException {
        return getBlock(String.valueOf(blockIndex));
    }

    /**
     * Gets a single block based on a block hash.
     *
     * @param blockHash Block hash
     * @return An instance of the {@link Block} class
     * @throws APIException If the server returns an error
     */
    public Block getBlock (String blockHash) throws APIException, IOException {
        String response = HttpClient.getInstance().get("rawblock/" + blockHash, buildBasicRequest());
        JsonObject blockJson = new JsonParser().parse(response).getAsJsonObject();
        return new Block(blockJson);
    }

    /**
     * Gets data for a single address.
     *
     * @param address Base58check or hash160 address string
     * @param filter the filter for transactions selection, use null to indicate default
     * @param limit an integer to limit number of transactions to display, use null to indicate default
     * @param offset an integer to set number of transactions to skip when fetch, use null to indicate default
     * @return An instance of the {@link Address} class
     * @throws APIException If the server returns an error
     */
    public Address getAddress (String address, FilterType filter, Integer limit, Integer offset) throws APIException, IOException {
        Map<String, String> params = buildBasicRequest();
        if (filter != null) {
            params.put("filter", filter.getFilterInt().toString());
        }
        if (limit != null) {
            params.put("limit", limit.toString());
        }
        if (offset != null) {
            params.put("offset", offset.toString());
        }
        String response = HttpClient.getInstance().get("rawaddr/" + address, params);
        JsonObject addrJson = new JsonParser().parse(response).getAsJsonObject();

        return new Address(addrJson);
    }

    /**
     * Gets data for a single address.
     *
     * @param address Base58check or hash160 address string
     * @return An instance of the {@link Address} class
     * @throws APIException If the server returns an error
     */
    public Address getAddress (String address) throws APIException, IOException {
        return getAddress(address, null, null, null);
    }

    /**
     * Gets a list of blocks at the specified height. Normally, only one block will be
     * returned, but in case of a chain fork, multiple blocks may be present.
     *
     * @param height Block height
     * @return A list of blocks at the specified height
     * @throws APIException If the server returns an error
     */
    public List<Block> getBlocksAtHeight (long height) throws APIException, IOException {
        List<Block> blocks = new ArrayList<Block>();

        String response = HttpClient.getInstance().get("block-height/" + height, buildBasicRequest());
        JsonObject blocksJson = new JsonParser().parse(response).getAsJsonObject();

        for (JsonElement blockElem : blocksJson.get("blocks").getAsJsonArray()) {
            blocks.add(new Block(blockElem.getAsJsonObject()));
        }

        return blocks;
    }

    /**
     * Returns list of unspent outputs.
     *
     * @param addressList a list of Base58 or xpub addresses
     * @param confirms an integer for minimum confirms of the outputs, use null to indicate default
     * @param limit an integer to limit number of transactions to display, use null to indicate default
     * @return A list of unspent outputs for the specified address
     * @throws APIException If the server returns an error
     */
    public List<UnspentOutput> getUnspentOutputs (List<String> addressList, Integer confirms, Integer limit) throws APIException, IOException {
        List<UnspentOutput> outputs = new ArrayList<UnspentOutput>();

        Map<String, String> params = buildBasicRequest();
        String pipedAddresses = StringUtils.join(addressList, "|");
        params.put("active", pipedAddresses);
        if (confirms != null) {
            params.put("confirmations", confirms.toString());
        }
        if (limit != null) {
            params.put("limit", limit.toString());
        }

        String response = null;
        try {
            response = HttpClient.getInstance().get("unspent", params);
        } catch (APIException e) {
            // The server endpoint will return error if no output, however this should be a valid situation so we return an empty result.
            if (e.getMessage().equals("No free outputs to spend")) {
                return outputs;
            }
        }

        JsonObject outsJson = new JsonParser().parse(response).getAsJsonObject();
        for (JsonElement outElem : outsJson.get("unspent_outputs").getAsJsonArray()) {
            outputs.add(new UnspentOutput(outElem.getAsJsonObject()));
        }

        return outputs;
    }

    /**
     * Gets unspent outputs for a single address.
     *
     * @param address Base58check or hash160 address string
     * @return A list of unspent outputs for the specified address
     * @throws APIException If the server returns an error
     */
    public List<UnspentOutput> getUnspentOutputs (String address) throws APIException, IOException {
        return getUnspentOutputs(Arrays.asList(address), null, null);
    }

    /**
     * Gets the latest block on the main chain (simplified representation).
     *
     * @return An instance of the {@link LatestBlock} class
     * @throws APIException If the server returns an error
     */
    public LatestBlock getLatestBlock () throws APIException, IOException {
        String response = HttpClient.getInstance().get("latestblock", buildBasicRequest());
        JsonObject blockObj = new JsonParser().parse(response).getAsJsonObject();
        return new LatestBlock(blockObj);
    }

    /**
     * Gets a list of currently unconfirmed transactions.
     *
     * @return A list of unconfirmed {@link Transaction} objects
     * @throws APIException If the server returns an error
     */
    public List<Transaction> getUnconfirmedTransactions () throws APIException, IOException {
        List<Transaction> transactions = new ArrayList<Transaction>();

        String response = HttpClient.getInstance().get("unconfirmed-transactions", buildBasicRequest());
        JsonObject txList = new JsonParser().parse(response).getAsJsonObject();

        for (JsonElement txElem : txList.get("txs").getAsJsonArray()) {
            JsonObject txObj = txElem.getAsJsonObject();
            transactions.add(new Transaction(txObj, -1, txObj.get("double_spend").getAsBoolean()));
        }

        return transactions;
    }

    /**
     * Gets a list of blocks mined today by all pools since 00:00 UTC.
     *
     * @return A list of {@link SimpleBlock} objects
     * @throws APIException APIException If the server returns an error
     */
    public List<SimpleBlock> getBlocks () throws APIException, IOException {
        return getBlocks(null);
    }

    /**
     * Gets a list of blocks mined on a specific day.
     *
     * @param timestamp Unix timestamp (without milliseconds) that falls between
     *                  00:00 UTC and 23:59 UTC of the desired day.
     * @return A list of {@link SimpleBlock} objects
     */
    public List<SimpleBlock> getBlocks (long timestamp) throws APIException, IOException {
        return getBlocks(String.valueOf(timestamp * 1000));
    }

    /**
     * Gets a list of recent blocks by a specific mining pool.
     *
     * @param poolName Name of the mining pool
     * @return A list of {@link SimpleBlock} objects
     * @throws APIException If the server returns an error
     */
    public List<SimpleBlock> getBlocks (String poolName) throws APIException, IOException {
        List<SimpleBlock> blocks = new ArrayList<SimpleBlock>();
        poolName = poolName == null ? "" : poolName;

        String response = HttpClient.getInstance().get("blocks/" + poolName, buildBasicRequest());
        JsonObject blockList = new JsonParser().parse(response).getAsJsonObject();

        for (JsonElement blockElem : blockList.get("blocks").getAsJsonArray()) {
            blocks.add(new SimpleBlock(blockElem.getAsJsonObject()));
        }

        return blocks;
    }

    /**
     * Returns the address balance summary for each address provided
     *
     * @param addressList base58 or xpub addresses
     * @param filter the filter for transactions selection, use null to indicate default
     * @return a map of (address, {@link Balance})
     */
    public Map<String, Balance> getBalance(List<String> addressList, FilterType filter) throws APIException, IOException {
        Map<String, String> params = buildBasicRequest();
        String pipedAddresses = StringUtils.join(addressList, "|");
        params.put("active", pipedAddresses);
        if (filter != null) {
            params.put("filter", filter.getFilterInt().toString());
        }

        String response = HttpClient.getInstance().get("balance", params);
        JsonObject balanceMap = new JsonParser().parse(response).getAsJsonObject();

        Map<String, Balance> balances = new HashMap<String, Balance>();
        for (String address : addressList) {
            JsonObject balance = balanceMap.getAsJsonObject(address);
            balances.put(address, new Balance(balance));
        }

        return balances;
    }

    /**
     * Returns an aggregated summary on all addresses provided.
     *
     * @param addressList a list of Base58 or xpub addresses
     * @param filter the filter for transactions selection, use null to indicate default
     * @param limit an integer to limit number of transactions to display, use null to indicate default
     * @param offset an integer to set number of transactions to skip when fetch, use null to indicate default
     * @return An instance of the {@link MultiAddress} class
     * @throws APIException If the server returns an error
     */
    public MultiAddress getMultiAddress(List<String> addressList, FilterType filter, Integer limit, Integer offset) throws APIException, IOException {
        Map<String, String> params = buildBasicRequest();
        String pipedAddresses = StringUtils.join(addressList, "|");
        params.put("active", pipedAddresses);
        if (filter != null) {
            params.put("filter", filter.getFilterInt().toString());
        }
        if (limit != null) {
            params.put("n", limit.toString());
        }
        if (offset != null) {
            params.put("offset", offset.toString());
        }

        String response = HttpClient.getInstance().get("multiaddr", params);
        JsonObject addrJson = new JsonParser().parse(response).getAsJsonObject();

        return new MultiAddress(addrJson);
    }

    /**
     * Returns xpub summary on a xpub provided, with its overall balance and its transactions.
     *
     * @param xpub a xpub address
     * @param filter the filter for transactions selection, use null to indicate default
     * @param limit an integer to limit number of transactions to display, use null to indicate default
     * @param offset an integer to set number of transactions to skip when fetch
     * @return {@link XpubFull} an object to represent the xpub summary
     */
    public XpubFull getXpub(String xpub, FilterType filter, Integer limit, Integer offset) throws APIException, IOException {
        MultiAddress multiAddress = getMultiAddress(Arrays.asList(xpub), filter, limit, offset);

        return new XpubFull(multiAddress.getAddresses().get(0), multiAddress.getTxs());
    }

    private Map<String, String> buildBasicRequest () {
        Map<String, String> params = new HashMap<String, String>();

        params.put("format", "json");
        if (apiCode != null) {
            params.put("api_code", apiCode);
        }

        return params;
  }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Address.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.util.ArrayList;
import java.util.List;

/**
 * Representation of an address.
 */
public class Address {
    private String hash160;
    private String address;
    private long totalReceived;
    private long totalSent;
    private long finalBalance;
    private int txCount;
    private List<Transaction> transactions;

    public Address (String hash160, String address, long totalReceived, long totalSent, long finalBalance, int txCount, List<Transaction> transactions) {
        this.hash160 = hash160;
        this.address = address;
        this.totalReceived = totalReceived;
        this.totalSent = totalSent;
        this.finalBalance = finalBalance;
        this.txCount = txCount;
        this.transactions = transactions;
    }

    public Address (JsonObject a) {

        this(
                a.has("hash160") ? a.get("hash160").getAsString() : "",
                a.has("address") ? a.get("address").getAsString() : "",
                a.has("total_received") ? a.get("total_received").getAsLong() : 0,
                a.has("total_sent") ? a.get("total_sent").getAsLong() : 0,
                a.has("final_balance") ? a.get("final_balance").getAsLong() : 0,
                a.has("n_tx") ? a.get("n_tx").getAsInt() : 0,
                null);

        transactions = new ArrayList<Transaction>();
        for (JsonElement txElem : a.get("txs").getAsJsonArray()) {
            JsonObject addrObj = txElem.getAsJsonObject();
            transactions.add(new Transaction(addrObj));
        }
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Address address1 = (Address) o;

        if (totalReceived != address1.totalReceived) {
            return false;
        }
        if (totalSent != address1.totalSent) {
            return false;
        }
        if (finalBalance != address1.finalBalance) {
            return false;
        }
        if (!hash160.equals(address1.hash160)) {
            return false;
        }
        return address.equals(address1.address);

    }

    @Override
    public int hashCode () {
        int result = hash160.hashCode();
        result = 31 * result + address.hashCode();
        result = 31 * result + (int) (totalReceived ^ (totalReceived >>> 32));
        result = 31 * result + (int) (totalSent ^ (totalSent >>> 32));
        result = 31 * result + (int) (finalBalance ^ (finalBalance >>> 32));
        return result;
    }

    /**
     * @return Hash160 representation of the address
     */
    public String getHash160 () {
        return hash160;
    }

    /**
     * @return Base58Check representation of the address
     */
    public String getAddress () {
        return address;
    }

    /**
     * @return Total amount received (in satoshi)
     */
    public long getTotalReceived () {
        return totalReceived;
    }

    /**
     * @return Total amount sent (in satoshi)
     */
    public long getTotalSent () {
        return totalSent;
    }

    /**
     * @return Final balance of the address (in satoshi)
     */
    public long getFinalBalance () {
        return finalBalance;
    }

    /**
     * @return Original number of transactions before filtering and limiting
     */
    public int getTxCount () {
        return txCount;
    }

    /**
     * @return List of transactions associated with this address
     */
    public List<Transaction> getTransactions () {
        return transactions;
    }

}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/AddressSummary.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonObject;

public class AddressSummary {

    private String address;
    private int txCount;
    private long totalReceived;
    private long totalSent;
    private long finalBalance;
    private int changeIndex;
    private int accountIndex;
    private int gapLimit;

    public AddressSummary (String address, int txCount, long totalReceived, long totalSent, long finalBalance,
                           int changeIndex, int accountIndex, int gapLimit) {
        this.address = address;
        this.txCount = txCount;
        this.totalReceived = totalReceived;
        this.totalSent = totalSent;
        this.finalBalance = finalBalance;
        this.changeIndex = changeIndex;
        this.accountIndex = accountIndex;
        this.gapLimit = gapLimit;
    }

    public AddressSummary (JsonObject json) {
        this(
                json.has("address") ? json.get("address").getAsString() : "",
                json.has("n_tx") ? json.get("n_tx").getAsInt() : 0,
                json.has("total_received") ? json.get("total_received").getAsLong() : 0,
                json.has("total_sent") ? json.get("total_sent").getAsLong() : 0,
                json.has("final_balance") ? json.get("final_balance").getAsLong() : 0,
                json.has("change_index") ? json.get("change_index").getAsInt() : 0,
                json.has("account_index") ? json.get("account_index").getAsInt() : 0,
                json.has("gap_limit") ? json.get("gap_limit").getAsInt() : 0
        );
    }

    public String getAddress() {
        return address;
    }

    public int getTxCount() {
        return txCount;
    }

    public void setTxCount(int txCount) {
        this.txCount = txCount;
    }

    public long getTotalReceived() {
        return totalReceived;
    }

    public long getTotalSent() {
        return totalSent;
    }

    public long getFinalBalance() {
        return finalBalance;
    }

    public int getChangeIndex() {
        return changeIndex;
    }

    public int getAccountIndex() {
        return accountIndex;
    }

    public void setFinalBalance(long final_balance) {
        this.finalBalance = final_balance;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void setTotalReceived(long total_received) {
        this.totalReceived = total_received;
    }

    public void setTotalSent(long total_sent) {
        this.totalSent = total_sent;
    }

    public void setChangeIndex(int change_index) {
        this.changeIndex = change_index;
    }

    public void setAccountIndex(int account_index) {
        this.accountIndex = account_index;
    }

    public int getGapLimit () {
        return gapLimit;
    }

    public void setGapLimit (int gapLimit) {
        this.gapLimit = gapLimit;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Balance.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonObject;

public class Balance {

    private long finalBalance;
    private long txCount;
    private long totalReceived;

    public Balance (long finalBalance, long txCount, long totalReceived) {
        this.finalBalance = finalBalance;
        this.txCount = txCount;
        this.totalReceived = totalReceived;
    }

    public Balance (JsonObject json) {

        this(
                json.has("final_balance") ? json.get("final_balance").getAsLong() : 0,
                json.has("n_tx") ? json.get("n_tx").getAsLong() : 0,
                json.has("total_received") ? json.get("total_received").getAsLong() : 0
        );
    }

    public long getFinalBalance() {
        return finalBalance;
    }

    public long getTxCount() {
        return txCount;
    }

    public long getTotalReceived() {
        return totalReceived;
    }

    public void setFinalBalance(long final_balance) {
        this.finalBalance = final_balance;
    }

    public void setTxCount(long n_tx) {
        this.txCount = n_tx;
    }

    public void setTotalReceived(long total_received) {
        this.totalReceived = total_received;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Block.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.util.ArrayList;
import java.util.List;

/**
 * This class is a full representation of a block. For simpler representations, see
 * `SimpleBlock` and `LatestBlock`.
 */
public class Block extends SimpleBlock {
    private int version;
    private String previousBlockHash;
    private String merkleRoot;
    private long bits;
    private long fees;
    private long nonce;
    private long size;
    private long index;
    private long receivedTime;
    private String relayedBy;
    private List<Transaction> transactions;

    public Block (long height, String hash, long time, boolean mainChain, int version, String previousBlockHash, String merkleRoot, long bits, long fees, long nonce, long size, long index, long receivedTime, String relayedBy, List<Transaction> transactions) {
        super(height, hash, time, mainChain);
        this.version = version;
        this.previousBlockHash = previousBlockHash;
        this.merkleRoot = merkleRoot;
        this.bits = bits;
        this.fees = fees;
        this.nonce = nonce;
        this.size = size;
        this.index = index;
        this.receivedTime = receivedTime;
        this.relayedBy = relayedBy;
        this.transactions = transactions;
    }

    public Block (JsonObject b) {
        this(b.get("height").getAsLong(), b.get("hash").getAsString(), b.get("time").getAsLong(), b.get("main_chain").getAsBoolean(), b.get("ver").getAsInt(), b.get("prev_block").getAsString(), b.get("mrkl_root").getAsString(), b.get("bits").getAsLong(), b.get("fee").getAsLong(), b.get("nonce").getAsLong(), b.get("size").getAsLong(), b.get("block_index").getAsLong(), b.has("received_time") ? b.get("received_time").getAsLong() : b.get("time").getAsLong(), b.has("relayed_by") ? b.get("relayed_by").getAsString() : null, null);

        transactions = new ArrayList<Transaction>();
        for (JsonElement txElem : b.get("tx").getAsJsonArray()) {
            transactions.add(new Transaction(txElem.getAsJsonObject(), super.getHeight(), false));
        }
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        if (!super.equals(o)) {
            return false;
        }

        Block block = (Block) o;

        if (version != block.version) {
            return false;
        }
        if (fees != block.fees) {
            return false;
        }
        if (nonce != block.nonce) {
            return false;
        }
        if (size != block.size) {
            return false;
        }
        if (index != block.index) {
            return false;
        }
        if (!previousBlockHash.equals(block.previousBlockHash)) {
            return false;
        }
        return merkleRoot.equals(block.merkleRoot);

    }

    @Override
    public int hashCode () {
        int result = version;
        result = 31 * result + previousBlockHash.hashCode();
        result = 31 * result + merkleRoot.hashCode();
        result = 31 * result + (int) (fees ^ (fees >>> 32));
        result = 31 * result + (int) (nonce ^ (nonce >>> 32));
        result = 31 * result + (int) (size ^ (size >>> 32));
        result = 31 * result + (int) (index ^ (index >>> 32));
        return result;
    }

    /**
     * @return Block version as specified by the protocol
     */
    public int getVersion () {
        return version;
    }

    /**
     * @return Hash of the previous block
     */
    public String getPreviousBlockHash () {
        return previousBlockHash;
    }

    /**
     * @return Merkle root of the block
     */
    public String getMerkleRoot () {
        return merkleRoot;
    }

    /**
     * @return Representation of the difficulty target for this block
     */
    public long getBits () {
        return bits;
    }

    /**
     * @return Total transaction fees from this block (in satoshi)
     */
    public long getFees () {
        return fees;
    }

    /**
     * @return Block nonce
     */
    public long getNonce () {
        return nonce;
    }

    /**
     * @return Serialized size of this block
     */
    public long getSize () {
        return size;
    }

    /**
     * @return Index of this block
     */
    public long getIndex () {
        return index;
    }

    /**
     * @return The time this block was received by Blockchain.info
     */
    public long getReceivedTime () {
        return receivedTime;
    }

    /**
     * @return IP address that relayed the block
     */
    public String getRelayedBy () {
        return relayedBy;
    }

    /**
     * @return Transactions in the block
     */
    public List<Transaction> getTransactions () {
        return transactions;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/FilterType.java
================================================
package info.blockchain.api.blockexplorer.entity;

public enum FilterType {
    All(4),
    ConfirmedOnly(5),
    RemoveUnspendable(6);

    private final Integer filterInt;

    FilterType (Integer filterInt) {
        this.filterInt = filterInt;
    }

    public Integer getFilterInt () {
        return filterInt;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Input.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonObject;

/**
 * Represents a transaction input. If the `previousOutput` object is null, this is a
 * coinbase input.
 */
public class Input {
    private Output previousOutput;
    private long sequence;
    private String scriptSignature;

    public Input (Output previousOutput, long sequence, String scriptSignature) {
        this.previousOutput = previousOutput;
        this.sequence = sequence;
        this.scriptSignature = scriptSignature;
    }

    public Input (JsonObject i) {
        if (i.has("prev_out") && !i.get("prev_out").isJsonNull()) {
            this.previousOutput = new Output(i.get("prev_out").getAsJsonObject(), true);
        }

        this.sequence = i.get("sequence").getAsLong();
        this.scriptSignature = i.get("script").getAsString();
    }

    /**
     * @return Previous output. If null, this is a coinbase input.
     */
    public Output getPreviousOutput () {
        return previousOutput;
    }

    /**
     * @return Sequence number of the input
     */
    public long getSequence () {
        return sequence;
    }

    /**
     * @return Script signature
     */
    public String getScriptSignature () {
        return scriptSignature;
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Input input = (Input) o;

        if (sequence != input.sequence) {
            return false;
        }
        if (previousOutput == null) {
            if (input.previousOutput != null) {
                return false;
            }
        }
        else if (!previousOutput.equals(input.previousOutput)) {
            return false;
        }
        
        return scriptSignature.equals(input.scriptSignature);
    }

    @Override
    public int hashCode () {
        int result = previousOutput == null ? 1 : previousOutput.hashCode();
        result = 31 * result + (int) (sequence ^ (sequence >>> 32));
        result = 31 * result + scriptSignature.hashCode();
        return result;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/LatestBlock.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.util.ArrayList;
import java.util.List;

/**
 * Used as a response to the `getLatestBlock` method in the `BlockExplorer` class.
 */
public class LatestBlock extends SimpleBlock {
    private long index;
    private List<Long> transactionIndexes;

    public LatestBlock (long height, String hash, long time, boolean mainChain, long index, List<Long> transactionIndexes) {
        super(height, hash, time, mainChain);
        this.index = index;
        this.transactionIndexes = transactionIndexes;
    }

    public LatestBlock (JsonObject b) {
        this(b.get("height").getAsLong(), b.get("hash").getAsString(), b.get("time").getAsLong(), true, b.get("block_index").getAsLong(), null);

        transactionIndexes = new ArrayList<Long>();
        for (JsonElement idxElem : b.get("txIndexes").getAsJsonArray()) {
            transactionIndexes.add(idxElem.getAsLong());
        }
    }

    /**
     * @return Transaction indexes included in this block
     */
    public List<Long> getTransactionIndexes () {
        return transactionIndexes;
    }

    /**
     * @return Block index
     */
    public long getIndex () {
        return index;
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        if (!super.equals(o)) {
            return false;
        }

        LatestBlock that = (LatestBlock) o;

        if (index != that.index) {
            return false;
        }
        return transactionIndexes.equals(that.transactionIndexes);

    }

    @Override
    public int hashCode () {
        int result = (int) (index ^ (index >>> 32));
        result = 31 * result + transactionIndexes.hashCode();
        return result;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/MultiAddress.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.util.ArrayList;
import java.util.List;

public class MultiAddress {

    private MultiAddressBalance multiAddressBalance;

    private List<AddressSummary> addresses;

    private List<Transaction> txs;

    public MultiAddress (MultiAddressBalance multiAddressBalance, List<AddressSummary> addresses, List<Transaction> txs) {
        this.multiAddressBalance = multiAddressBalance;
        this.addresses = addresses;
        this.txs = txs;
    }

    public MultiAddress (JsonObject json) {
        List<AddressSummary> addresses = new ArrayList<AddressSummary>();
        for (JsonElement txElem : json.get("addresses").getAsJsonArray()) {
            JsonObject addrObj = txElem.getAsJsonObject();
            addresses.add(new AddressSummary(addrObj));
        }
        this.addresses = addresses;

        List<Transaction> txs = new ArrayList<Transaction>();
        for (JsonElement txElem : json.get("txs").getAsJsonArray()) {
            JsonObject addrObj = txElem.getAsJsonObject();
            txs.add(new Transaction(addrObj));
        }
        this.txs = txs;

        this.multiAddressBalance = new MultiAddressBalance(json.getAsJsonObject("wallet"));
    }

    public MultiAddressBalance getMultiAddressBalance () {
        return multiAddressBalance;
    }

    public List<AddressSummary> getAddresses() {
        return addresses;
    }

    public List<Transaction> getTxs() {
        return txs;
    }

    public void setMultiAddressBalance (MultiAddressBalance multiAddressBalance) {
        this.multiAddressBalance = multiAddressBalance;
    }

    public void setAddresses(List<AddressSummary> addresses) {
        this.addresses = addresses;
    }

    public void setTxs(List<Transaction> txs) {
        this.txs = txs;
    }

}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/MultiAddressBalance.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonObject;

public class MultiAddressBalance {

    private int txCount;
    private int txCountFiltered;
    private long totalReceived;
    private long totalSent;
    private long finalBalance;

    public MultiAddressBalance (int txCount, int txCountFiltered, long totalReceived, long totalSent, long finalBalance) {
        this.txCount = txCount;
        this.txCountFiltered = txCountFiltered;
        this.totalReceived = totalReceived;
        this.totalSent = totalSent;
        this.finalBalance = finalBalance;
    }

    public MultiAddressBalance (JsonObject json) {
        this(
                json.has("n_tx") ? json.get("n_tx").getAsInt() : 0,
                json.has("n_tx_filtered") ? json.get("n_tx").getAsInt() : 0,
                json.has("total_received") ? json.get("total_received").getAsLong() : 0,
                json.has("total_sent") ? json.get("total_sent").getAsLong() : 0,
                json.has("final_balance") ? json.get("final_balance").getAsLong() : 0
        );
    }

    public int getTxCount() {
        return txCount;
    }

    public void setTxCount(int txCount) {
        this.txCount = txCount;
    }

    public long getTotalReceived() {
        return totalReceived;
    }

    public long getTotalSent() {
        return totalSent;
    }

    public long getFinalBalance() {
        return finalBalance;
    }

    public void setFinalBalance(long final_balance) {
        this.finalBalance = final_balance;
    }

    public void setTotalReceived(long total_received) {
        this.totalReceived = total_received;
    }

    public void setTotalSent(long total_sent) {
        this.totalSent = total_sent;
    }

    public int getTxCountFiltered () {
        return txCountFiltered;
    }

    public void setTxCountFiltered (int txCountFiltered) {
        this.txCountFiltered = txCountFiltered;
    }

}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Output.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonObject;

/**
 * Represents a transaction output.
 */
public class Output {
    private int n;
    private long value;
    private String address;
    private long txIndex;
    private String script;
    private boolean spent;
    private boolean spentToAddress;

    public Output (int n, long value, String address, long txIndex, String script, boolean spent) {
        this.n = n;
        this.value = value;
        this.address = address;
        this.txIndex = txIndex;
        this.script = script;
        this.spent = spent;
        if (address != "") {
            spentToAddress = true;
        }
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Output output = (Output) o;

        if (value != output.value) {
            return false;
        }
        if (txIndex != output.txIndex) {
            return false;
        }
        if (n != output.n) {
          return false;
        }
        
        return !(script != null ? !script.equals(output.script) : output.script != null);

    }

    @Override
    public int hashCode () {
        int result = (int) (value ^ (value >>> 32));
        result = 31 * result + (int) (txIndex ^ (txIndex >>> 32));
        result = 31 * result + (int) (n ^ (n >>> 32));
        result = 31 * result + (script != null ? script.hashCode() : 0);
        return result;
    }

    public Output (JsonObject o) {
        this(o, o.get("spent").getAsBoolean());
    }

    public Output (JsonObject o, boolean spent) {
        this(o.get("n").getAsInt(), o.get("value").getAsLong(), o.has("addr") ? o.get("addr").getAsString() : "", o.get("tx_index").getAsLong(), o.get("script").getAsString(), spent);
    }

    /**
     * @return Index of the output in a transaction
     */
    public int getN () {
        return n;
    }

    /**
     * @return Value of the output (in satoshi)
     */
    public long getValue () {
        return value;
    }

    /**
     * @return Address that the output belongs to
     */
    public String getAddress () {
        return address;
    }

    /**
     * @return Transaction index
     */
    public long getTxIndex () {
        return txIndex;
    }

    /**
     * @return Output script
     */
    public String getScript () {
        return script;
    }

    /**
     * @return Whether the output is spent
     */
    public boolean isSpent () {
        return spent;
    }

    /**
     * @return Whether the output pays to an address.
     */
    public boolean isSpentToAddress () {
        return spentToAddress;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/SimpleBlock.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonObject;

/**
 * Simple representation of a block
 */
public class SimpleBlock {
    private long height;
    private String hash;
    private long time;
    private boolean mainChain;

    public SimpleBlock (long height, String hash, long time, boolean mainChain) {
        this.height = height;
        this.hash = hash;
        this.time = time;
        this.mainChain = mainChain;
    }

    public SimpleBlock (JsonObject b) {
        this(b.get("height").getAsLong(), b.get("hash").getAsString(), b.get("time").getAsLong(), b.get("main_chain").getAsBoolean());
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        SimpleBlock that = (SimpleBlock) o;

        if (height != that.height) {
            return false;
        }
        if (time != that.time) {
            return false;
        }
        if (mainChain != that.mainChain) {
            return false;
        }
        return !(hash != null ? !hash.equals(that.hash) : that.hash != null);

    }

    @Override
    public int hashCode () {
        int result = (int) (height ^ (height >>> 32));
        result = 31 * result + (hash != null ? hash.hashCode() : 0);
        result = 31 * result + (int) (time ^ (time >>> 32));
        result = 31 * result + (mainChain ? 1 : 0);
        return result;
    }

    /**
     * @return Block height
     */
    public long getHeight () {
        return height;
    }

    /**
     * @return Block hash
     */
    public String getHash () {
        return hash;
    }

    /**
     * @return Block timestamp set by the miner (unix time in seconds)
     */
    public long getTime () {
        return time;
    }

    /**
     * @return Whether the block is on the main chain
     */
    public boolean isMainChain () {
        return mainChain;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Transaction.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.util.ArrayList;
import java.util.List;

/**
 * Represents a transaction.
 */
public class Transaction {
    private boolean doubleSpend;
    private long blockHeight;
    private long time;
    private long lockTime;
    private String relayedBy;
    private String hash;
    private long index;
    private int version;
    private long size;
    private List<Input> inputs;
    private List<Output> outputs;

    public Transaction (boolean doubleSpend, long blockHeight, long time, long lockTime, String relayedBy,
        String hash, long index, int version, long size, List<Input> inputs, List<Output> outputs) {
        this.doubleSpend = doubleSpend;
        this.blockHeight = blockHeight;
        this.time = time;
        this.lockTime = lockTime;
        this.relayedBy = relayedBy;
        this.hash = hash;
        this.index = index;
        this.version = version;
        this.size = size;
        this.inputs = inputs;
        this.outputs = outputs;
    }

    public Transaction (JsonObject t) {
        this(t, t.has("block_height") ? t.get("block_height").getAsLong() : -1,
            t.has("double_spend") ? t.get("double_spend").getAsBoolean() : false);
    }

    public Transaction (JsonObject t, long blockHeight, boolean doubleSpend) {
        this(doubleSpend, blockHeight, t.get("time").getAsLong(), t.get("lock_time").getAsLong(),
            t.get("relayed_by").getAsString(), t.get("hash").getAsString(), t.get("tx_index").getAsLong(),
            t.get("ver").getAsInt(), t.get("size").getAsLong(), null, null);

        inputs = new ArrayList<Input>();
        for (JsonElement inputElem : t.get("inputs").getAsJsonArray()) {
            inputs.add(new Input(inputElem.getAsJsonObject()));
        }

        outputs = new ArrayList<Output>();
        for (JsonElement outputElem : t.get("out").getAsJsonArray()) {
            outputs.add(new Output(outputElem.getAsJsonObject()));
        }
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Transaction that = (Transaction) o;

        if (index != that.index) {
            return false;
        }
        if (version != that.version) {
            return false;
        }
        if (size != that.size) {
            return false;
        }
        if (hash != null ? !hash.equals(that.hash) : that.hash != null) {
            return false;
        }
        if (inputs != null ? !inputs.equals(that.inputs) : that.inputs != null) {
            return false;
        }
        return !(outputs != null ? !outputs.equals(that.outputs) : that.outputs != null);

    }

    @Override
    public int hashCode () {
        int result = hash != null ? hash.hashCode() : 0;
        result = 31 * result + (int) (index ^ (index >>> 32));
        result = 31 * result + version;
        result = 31 * result + (int) (size ^ (size >>> 32));
        result = 31 * result + (inputs != null ? inputs.hashCode() : 0);
        result = 31 * result + (outputs != null ? outputs.hashCode() : 0);
        return result;
    }

    /**
     * @return Whether the transaction is a double spend
     */
    public boolean isDoubleSpend () {
        return doubleSpend;
    }

    /**
     * @return Block height of the parent block. -1 for unconfirmed transactions.
     */
    public long getBlockHeight () {
        return blockHeight;
    }

    /**
     * @return Timestamp of the transaction
     */
    public long getTime () {
        return time;
    }

    /**
     * @return Locktime of the transaction
     */
    public long getLockTime () {
        return lockTime;
    }

    /**
     * @return IP address that relayed the transaction
     */
    public String getRelayedBy () {
        return relayedBy;
    }

    /**
     * @return Transaction hash
     */
    public String getHash () {
        return hash;
    }

    /**
     * @return Transaction index
     */
    public long getIndex () {
        return index;
    }

    /**
     * @return Transaction format version
     */
    public int getVersion () {
        return version;
    }

    /**
     * @return Serialized size of the transaction
     */
    public long getSize () {
        return size;
    }

    /**
     * @return List of inputs
     */
    public List<Input> getInputs () {
        return inputs;
    }

    /**
     * @return List of outputs
     */
    public List<Output> getOutputs () {
        return outputs;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/UnspentOutput.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonObject;

/**
 * Represents an unspent transaction output.
 */
public class UnspentOutput {
    private int n;
    private String transactionHash;
    private long transactionIndex;
    private String script;
    private long value;
    private long confirmations;

    public UnspentOutput (int n, String transactionHash, long transactionIndex, String script, long value, long confirmations) {
        this.n = n;
        this.transactionHash = transactionHash;
        this.transactionIndex = transactionIndex;
        this.script = script;
        this.value = value;
        this.confirmations = confirmations;
    }

    public UnspentOutput (JsonObject o) {
        this(o.get("tx_output_n").getAsInt(), o.get("tx_hash").getAsString(), o.get("tx_index").getAsLong(), o.get("script").getAsString(), o.get("value").getAsLong(), o.get("confirmations").getAsLong());
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        UnspentOutput that = (UnspentOutput) o;

        if (transactionIndex != that.transactionIndex) {
            return false;
        }
        if (value != that.value) {
            return false;
        }
        if (transactionHash != null ? !transactionHash.equals(that.transactionHash) : that.transactionHash != null) {
            return false;
        }
        return !(script != null ? !script.equals(that.script) : that.script != null);

    }

    @Override
    public int hashCode () {
        int result = transactionHash != null ? transactionHash.hashCode() : 0;
        result = 31 * result + (int) (transactionIndex ^ (transactionIndex >>> 32));
        result = 31 * result + (script != null ? script.hashCode() : 0);
        result = 31 * result + (int) (value ^ (value >>> 32));
        return result;
    }

    /**
     * @return Index of the output in a transaction
     */
    public int getN () {
        return n;
    }

    /**
     * @return Transaction hash
     */
    public String getTransactionHash () {
        return transactionHash;
    }

    /**
     * @return Transaction index
     */
    public long getTransactionIndex () {
        return transactionIndex;
    }

    /**
     * @return Output script
     */
    public String getScript () {
        return script;
    }

    /**
     * @return Value of the output (in satoshi)
     */
    public long getValue () {
        return value;
    }

    /**
     * @return Number of confirmations
     */
    public long getConfirmations () {
        return confirmations;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/blockexplorer/entity/XpubFull.java
================================================
package info.blockchain.api.blockexplorer.entity;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.util.ArrayList;
import java.util.List;

public class XpubFull {

    private String address;
    private int txCount;
    private long totalReceived;
    private long totalSent;
    private long finalBalance;
    private int changeIndex;
    private int accountIndex;
    private int gapLimit;
    private List<Transaction> txs;

    public XpubFull (String address, int txCount, long totalReceived, long totalSent, long finalBalance, int changeIndex, int accountIndex, int gapLimit,
                     List<Transaction> txs) {
        this.address = address;
        this.txCount = txCount;
        this.totalReceived = totalReceived;
        this.totalSent = totalSent;
        this.finalBalance = finalBalance;
        this.changeIndex = changeIndex;
        this.accountIndex = accountIndex;
        this.gapLimit = gapLimit;
        this.txs = txs;
    }

    public XpubFull (AddressSummary addressSummary, List<Transaction> txs) {
        this.address = addressSummary.getAddress();
        this.txCount = addressSummary.getTxCount();
        this.accountIndex = addressSummary.getAccountIndex();
        this.changeIndex = addressSummary.getChangeIndex();
        this.finalBalance = addressSummary.getFinalBalance();
        this.gapLimit = addressSummary.getGapLimit();
        this.totalReceived = addressSummary.getTotalReceived();
        this.totalSent = addressSummary.getTotalSent();
        this.txs = txs;
    }

    public XpubFull (JsonObject json) {
        this(
                json.has("address") ? json.get("address").getAsString() : "",
                json.has("n_tx") ? json.get("n_tx").getAsInt() : 0,
                json.has("total_received") ? json.get("total_received").getAsLong() : 0,
                json.has("total_sent") ? json.get("total_sent").getAsLong() : 0,
                json.has("final_balance") ? json.get("final_balance").getAsLong() : 0,
                json.has("change_index") ? json.get("change_index").getAsInt() : 0,
                json.has("account_index") ? json.get("account_index").getAsInt() : 0,
                json.has("gap_limit") ? json.get("gap_limit").getAsInt() : 0,
                null
        );

        txs = new ArrayList<Transaction>();
        for (JsonElement txElem : json.get("txs").getAsJsonArray()) {
            JsonObject addrObj = txElem.getAsJsonObject();
            txs.add(new Transaction(addrObj));
        }
    }

    public String getAddress() {
        return address;
    }

    public int getTxCount() {
        return txCount;
    }

    public void setTxCount(int txCount) {
        this.txCount = txCount;
    }

    public long getTotalReceived() {
        return totalReceived;
    }

    public long getTotalSent() {
        return totalSent;
    }

    public long getFinalBalance() {
        return finalBalance;
    }

    public int getChangeIndex() {
        return changeIndex;
    }

    public int getAccountIndex() {
        return accountIndex;
    }

    public void setFinalBalance(long final_balance) {
        this.finalBalance = final_balance;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void setTotalReceived(long total_received) {
        this.totalReceived = total_received;
    }

    public void setTotalSent(long total_sent) {
        this.totalSent = total_sent;
    }

    public void setChangeIndex(int change_index) {
        this.changeIndex = change_index;
    }

    public void setAccountIndex(int account_index) {
        this.accountIndex = account_index;
    }

    public int getGapLimit () {
        return gapLimit;
    }

    public void setGapLimit (int gapLimit) {
        this.gapLimit = gapLimit;
    }

    public List<Transaction> getTxs () {
        return txs;
    }

    public void setTxs (List<Transaction> txs) {
        this.txs = txs;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/etc/Base58.java
================================================
package info.blockchain.api.etc;

/**
 * Copyright 2011 Google Inc.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
import java.util.Arrays;

/**
 * Base58 is a way to encode Bitcoin addresses (or arbitrary data) as alphanumeric strings.
 * <p>
 * Note that this is not the same base58 as used by Flickr, which you may find referenced around the Internet.
 * <p>
 * Satoshi explains: why base-58 instead of standard base-64 encoding?
 * <ul>
 * <li>Don't want 0OIl characters that look the same in some fonts and
 * could be used to create visually identical looking account numbers.</li>
 * <li>A string with non-alphanumeric characters is not as easily accepted as an account number.</li>
 * <li>E-mail usually won't line-break if there's no punctuation to break at.</li>
 * <li>Doubleclicking selects the whole number as one word if it's all alphanumeric.</li>
 * </ul>
 * <p>
 * However, note that the encoding/decoding runs in O(n&sup2;) time, so it is not useful for large data.
 * <p>
 * The basic idea of the encoding is to treat the data bytes as a large number represented using
 * base-256 digits, convert the number to be represented using base-58 digits, preserve the exact
 * number of leading zeros (which are otherwise lost during the mathematical operations on the
 * numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters.
 */
public class Base58 {
    public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
    private static final char ENCODED_ZERO = ALPHABET[0];
    private static final int[] INDEXES = new int[128];

    static {
        Arrays.fill(INDEXES, -1);
        for (int i = 0; i < ALPHABET.length; i++) {
            INDEXES[ALPHABET[i]] = i;
        }
    }

    /**
     * Encodes the given bytes as a base58 string (no checksum is appended).
     *
     * @param input the bytes to encode
     * @return the base58-encoded string
     */
    public static String encode (byte[] input) {
        if (input.length == 0) {
            return "";
        }
        // Count leading zeros.
        int zeros = 0;
        while (zeros < input.length && input[zeros] == 0) {
            ++zeros;
        }
        // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters)
        input = Arrays.copyOf(input, input.length); // since we modify it in-place
        char[] encoded = new char[input.length * 2]; // upper bound
        int outputStart = encoded.length;
        for (int inputStart = zeros; inputStart < input.length; ) {
            encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)];
            if (input[inputStart] == 0) {
                ++inputStart; // optimization - skip leading zeros
            }
        }
        // Preserve exactly as many leading encoded zeros in output as there were leading zeros in input.
        while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {
            ++outputStart;
        }
        while (--zeros >= 0) {
            encoded[--outputStart] = ENCODED_ZERO;
        }
        // Return encoded string (including encoded leading zeros).
        return new String(encoded, outputStart, encoded.length - outputStart);
    }

    /**
     * Decodes the given base58 string into the original data bytes.
     *
     * @param input the base58-encoded string to decode
     * @return the decoded data bytes
     * @throws ParseException if the given string is not a valid base58 string
     */
    public static byte[] decode (String input) throws ParseException {
        if (input.length() == 0) {
            return new byte[0];
        }
        // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).
        byte[] input58 = new byte[input.length()];
        for (int i = 0; i < input.length(); ++i) {
            char c = input.charAt(i);
            int digit = c < 128 ? INDEXES[c] : -1;
            if (digit < 0) {
                throw new ParseException("Illegal character " + c + " at position " + i, i);
            }
            input58[i] = (byte) digit;
        }
        // Count leading zeros.
        int zeros = 0;
        while (zeros < input58.length && input58[zeros] == 0) {
            ++zeros;
        }
        // Convert base-58 digits to base-256 digits.
        byte[] decoded = new byte[input.length()];
        int outputStart = decoded.length;
        for (int inputStart = zeros; inputStart < input58.length; ) {
            decoded[--outputStart] = divmod(input58, inputStart, 58, 256);
            if (input58[inputStart] == 0) {
                ++inputStart; // optimization - skip leading zeros
            }
        }
        // Ignore extra leading zeroes that were added during the calculation.
        while (outputStart < decoded.length && decoded[outputStart] == 0) {
            ++outputStart;
        }
        // Return decoded data (including original number of leading zeros).
        return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length);
    }

    public static BigInteger decodeToBigInteger (String input) throws ParseException {
        return new BigInteger(1, decode(input));
    }

    /**
     * Decodes the given base58 string into the original data bytes, using the checksum in the
     * last 4 bytes of the decoded data to verify that the rest are correct. The checksum is
     * removed from the returned data.
     *
     * @param input the base58-encoded string to decode (which should include the checksum)
     * @throws ParseException if the input is not base 58 or the checksum does not validate.
     */
    public static byte[] decodeChecked (String input) throws ParseException {
        byte[] decoded = decode(input);
        if (decoded.length < 4) {
            throw new ParseException("Input too short", 0);
        }
        byte[] data = Arrays.copyOfRange(decoded, 0, decoded.length - 4);
        byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length);
        byte[] actualChecksum = Arrays.copyOfRange(hashTwice(data), 0, 4);
        if (!Arrays.equals(checksum, actualChecksum)) {
            throw new ParseException("Checksum does not validate", 0);
        }
        return data;
    }

    /**
     * Divides a number, represented as an array of bytes each containing a single digit
     * in the specified base, by the given divisor. The given number is modified in-place
     * to contain the quotient, and the return value is the remainder.
     *
     * @param number     the number to divide
     * @param firstDigit the index within the array of the first non-zero digit
     *                   (this is used for optimization by skipping the leading zeros)
     * @param base       the base in which the number's digits are represented (up to 256)
     * @param divisor    the number to divide by (up to 256)
     * @return the remainder of the division operation
     */
    private static byte divmod (byte[] number, int firstDigit, int base, int divisor) {
        // this is just long division which accounts for the base of the input digits
        int remainder = 0;
        for (int i = firstDigit; i < number.length; i++) {
            int digit = (int) number[i] & 0xFF;
            int temp = remainder * base + digit;
            number[i] = (byte) (temp / divisor);
            remainder = temp % divisor;
        }
        return (byte) remainder;
    }

    public static byte[] hashTwice (byte[] input) {
        MessageDigest digest = null;
        try {
            digest = MessageDigest.getInstance("SHA-256");
            digest.update(input, 0, input.length);
            return digest.digest(digest.digest());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }

    }
}

================================================
FILE: src/main/java/info/blockchain/api/exchangerates/Currency.java
================================================
package info.blockchain.api.exchangerates;

import java.math.BigDecimal;

/**
 * Used in response to the `getTicker` method in the `ExchangeRates` class.
 */
public class Currency {
    private BigDecimal buy;
    private BigDecimal sell;
    private BigDecimal last;
    private BigDecimal price15m;
    private String symbol;

    public Currency (double buy, double sell, double last, double price15m, String symbol) {
        this.buy = BigDecimal.valueOf(buy);
        this.sell = BigDecimal.valueOf(sell);
        this.last = BigDecimal.valueOf(last);
        this.price15m = BigDecimal.valueOf(price15m);
        this.symbol = symbol;
    }

    /**
     * @return Current buy price
     */
    public BigDecimal getBuy () {
        return buy;
    }

    /**
     * @return Current sell price
     */
    public BigDecimal getSell () {
        return sell;
    }

    /**
     * @return Most recent market price
     */
    public BigDecimal getLast () {
        return last;
    }

    /**
     * @return 15 minutes delayed market price
     */
    public BigDecimal getPrice15m () {
        return price15m;
    }

    /**
     * @return Currency symbol
     */
    public String getSymbol () {
        return symbol;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/exchangerates/ExchangeRates.java
================================================
package info.blockchain.api.exchangerates;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import info.blockchain.api.APIException;
import info.blockchain.api.HttpClient;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
 * This class reflects the functionality documented
 * at https://blockchain.info/api/exchange_rates_api. It allows users to fetch the latest
 * ticker data and convert amounts between BTC and fiat currencies.
 */
public class ExchangeRates {

    private final String apiCode;

    public ExchangeRates () {
        this(null);
    }

    public ExchangeRates (String apiCode) {
        this.apiCode = apiCode;
    }

    /**
     * Gets the price ticker from https://blockchain.info/ticker
     *
     * @return A map of currencies where the key is a 3-letter currency symbol and the
     * value is the `Currency` class
     * @throws APIException If the server returns an error
     */
    public Map<String, Currency> getTicker () throws APIException, IOException {
        Map<String, String> params = new HashMap<String, String>();
        if (apiCode != null) {
            params.put("api_code", apiCode);
        }

        String response = HttpClient.getInstance().get("ticker", params);
        JsonObject ticker = new JsonParser().parse(response).getAsJsonObject();

        Map<String, Currency> resultMap = new HashMap<String, Currency>();
        for (Entry<String, JsonElement> ccyKVP : ticker.entrySet()) {
            JsonObject ccy = ccyKVP.getValue().getAsJsonObject();
            Currency currency = new Currency(ccy.get("buy").getAsDouble(), ccy.get("sell").getAsDouble(), ccy.get("last").getAsDouble(), ccy.get("15m").getAsDouble(), ccy.get("symbol").getAsString());

            resultMap.put(ccyKVP.getKey(), currency);
        }

        return resultMap;
    }

    /**
     * Converts x value in the provided currency to BTC.
     *
     * @param currency Currency code
     * @param value    Value to convert
     * @return Converted value in BTC
     * @throws APIException If the server returns an error
     */
    public BigDecimal toBTC (String currency, BigDecimal value) throws APIException, IOException {
        Map<String, String> params = new HashMap<String, String>();
        params.put("currency", currency);
        params.put("value", String.valueOf(value));
        if (apiCode != null) {
            params.put("api_code", apiCode);
        }

        String response = HttpClient.getInstance().get("tobtc", params);
        return new BigDecimal(response);
    }

    /**
     * Converts x value in BTC to the provided currency.
     *
     * @param currency Currency code
     * @param value    Value to convert
     * @return Converted value in the provided currency
     * @throws APIException If the server returns an error
     */
    public BigDecimal toFiat (String currency, BigDecimal value) throws APIException, IOException {
        Map<String, String> params = new HashMap<String, String>();
        params.put("currency", currency);
        params.put("value", String.valueOf(value.multiply(BigDecimal.valueOf(100000000L)))); // The endpoint is expecting satoshi
        if (apiCode != null) {
            params.put("api_code", apiCode);
        }

        String response = HttpClient.getInstance().get("frombtc", params);
        return new BigDecimal(response);
    }

}


================================================
FILE: src/main/java/info/blockchain/api/pushtx/PushTx.java
================================================
package info.blockchain.api.pushtx;

import info.blockchain.api.APIException;
import info.blockchain.api.HttpClient;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * This class reflects the functionality provided at
 * https://blockchain.info/pushtx. It allows users to broadcast hex encoded
 * transactions to the bitcoin network.
 */
public class PushTx {
    /**
     * Pushes a hex encoded transaction to the network.
     *
     * @param tx Hex encoded transaction
     * @throws APIException If the server returns an error (malformed tx etc.)
     */
    public static void pushTx (String tx) throws APIException, IOException {
        pushTx(tx, null);
    }

    /**
     * Pushes a hex encoded transaction to the network.
     *
     * @param tx      Hex encoded transaction
     * @param apiCode Blockchain.info API code (optional, nullable)
     * @throws APIException If the server returns an error (malformed tx etc.)
     */
    public static void pushTx (String tx, String apiCode) throws APIException, IOException {
        Map<String, String> params = new HashMap<String, String>();
        params.put("tx", tx);

        if (apiCode != null) {
            params.put("api_code", apiCode);
        }

        HttpClient.getInstance().post("pushtx", params);
    }
}


================================================
FILE: src/main/java/info/blockchain/api/receive/CallbackLog.java
================================================
package info.blockchain.api.receive;

import com.google.gson.JsonObject;

public class CallbackLog {
    private String callback;
    private String callbackTime;
    private String response;
    private int responseCode;

    public CallbackLog (JsonObject callbackJson) {
        this(
                callbackJson.has("callback") ? callbackJson.get("callback").getAsString() : "",
                callbackJson.has("called_at") ? callbackJson.get("called_at").getAsString() : "",
                callbackJson.has("raw_response") ? callbackJson.get("raw_response").getAsString() : "",
                callbackJson.has("response_code") ? callbackJson.get("response_code").getAsInt() : 0
        );
    }

    public CallbackLog (String callback, String callbackTime, String response, int responseCode) {
        this.callback = callback;
        this.callbackTime = callbackTime;
        this.response = response;
        this.responseCode = responseCode;
    }

    public String getCallback () {
        return callback;
    }

    public void setCallback (String callback) {
        this.callback = callback;
    }

    public String getCallbackTime () {
        return callbackTime;
    }

    public void setCallbackTime (String callbackTime) {
        this.callbackTime = callbackTime;
    }

    public String getResponse () {
        return response;
    }

    public void setResponse (String response) {
        this.response = response;
    }

    public int getResponseCode () {
        return responseCode;
    }

    public void setResponseCode (int responseCode) {
        this.responseCode = responseCode;
    }

}


================================================
FILE: src/main/java/info/blockchain/api/receive/Receive.java
================================================
package info.blockchain.api.receive;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import info.blockchain.api.APIException;
import info.blockchain.api.HttpClient;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * This class reflects the functionality necessary for using the receive-payments-api v2.
 * Passing on a xPUB, callbackUrl and the apiCode will return an address for receiving a payment.
 * <p>
 * Upon receiving a payment on this address, the merchant will be notified using the callback URL.
 */
public class Receive {
    private final String apiCode;

    /**
     * @param apiCode     Blockchain.info API code for the receive-payments v2 API (different from normal API key)
     */
    public Receive (String apiCode) {
        this.apiCode = apiCode;
    }

    /**
     * Calls the receive-payments-api v2 and returns an address for the payment.
     *
     * @param xPUB        Destination address where the payment should be sent
     * @param callbackUrl Callback URI that will be called upon payment
     * @return An instance of the ReceiveV2Response class
     * @throws APIException If the server returns an error
     */
    public ReceiveResponse receive (String xPUB, String callbackUrl) throws APIException, IOException {
        Map<String, String> params = new HashMap<String, String>();
        params.put("xpub", xPUB);
        params.put("callback", callbackUrl);
        params.put("key", apiCode);

        String response = HttpClient.getInstance().get("https://api.blockchain.info/", "v2/receive", params);
        JsonParser parser = new JsonParser();
        JsonObject obj = parser.parse(response).getAsJsonObject();

        return new ReceiveResponse(obj.get("index").getAsInt(), obj.get("address").getAsString(), obj.get("callback").getAsString());
    }

    /**
     * Calls the receive-payments-api v2 and returns the xpub gap of an xpub.
     *
     * @param xPUB        Destination address where the payment should be sent
     * @return An instance of the ReceiveV2Response class
     * @throws APIException If the server returns an error
     */
    public int checkGap (String xPUB) throws APIException, IOException {
        Map<String, String> params = new HashMap<String, String>();
        params.put("xpub", xPUB);
        params.put("key", apiCode);

        String response = HttpClient.getInstance().get("https://api.blockchain.info/", "v2/receive/checkgap", params);
        JsonObject obj = new JsonParser().parse(response).getAsJsonObject();

        return obj.get("gap").getAsInt();
    }

    /**
     * Calls the receive-payments-api v2 and returns the callback log based on url.
     *
     * @param callbackUrl Callback URI that will be called upon payment
     * @return An instance of the ReceiveV2Response class
     * @throws APIException If the server returns an error
     */
    public CallbackLog getCallbackLog (String callbackUrl) throws APIException, IOException {
        Map<String, String> params = new HashMap<String, String>();
        params.put("callback", callbackUrl);
        params.put("key", apiCode);

        String response = HttpClient.getInstance().get("https://api.blockchain.info/", "v2/receive/callback", params);
        JsonObject obj = new JsonParser().parse(response).getAsJsonObject();

        return new CallbackLog(obj);
    }
}


================================================
FILE: src/main/java/info/blockchain/api/receive/ReceiveResponse.java
================================================
package info.blockchain.api.receive;

/**
 * This class is used as a response object to the `ReceiveV2.receive` method.
 */
public class ReceiveResponse {
    private int index;
    private String receivingAddress;
    private String callbackUrl;

    public ReceiveResponse (int index, String receivingAddress, String callbackUrl) {
        this.index = index;
        this.receivingAddress = receivingAddress;
        this.callbackUrl = callbackUrl;
    }

    /**
     * @return Index of the address in the account
     */
    public int getIndex () {
        return index;
    }

    /**
     * @return Address to be displayed for the customer at checkout.
     */
    public String getReceivingAddress () {
        return receivingAddress;
    }

    /**
     * @return Callback URI that will be called upon payment
     */
    public String getCallbackUrl () {
        return callbackUrl;
    }
}

================================================
FILE: src/main/java/info/blockchain/api/statistics/Chart.java
================================================
package info.blockchain.api.statistics;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

import java.util.ArrayList;
import java.util.List;

public class Chart {

    private String status;
    private String name;
    private String unit;
    private String period;
    private String description;
    private List<Point> values;

    public Chart (JsonObject chartJson) {
        this(
                chartJson.has("status") ? chartJson.get("status").getAsString() : "",
                chartJson.has("name") ? chartJson.get("name").getAsString() : "",
                chartJson.has("unit") ? chartJson.get("unit").getAsString() : "",
                chartJson.has("period") ? chartJson.get("period").getAsString() : "",
                chartJson.has("description") ? chartJson.get("description").getAsString() : "",
                null
        );
        values = getPoints(chartJson);
    }

    public Chart (String status, String name, String unit, String period, String description, List<Point> values) {
        this.status = status;
        this.name = name;
        this.unit = unit;
        this.period = period;
        this.description = description;
        this.values = values;
    }

    private List<Point> getPoints(JsonObject chartJson) {
        List<Point> points = new ArrayList<Point>();
        for (JsonElement pointElement : chartJson.getAsJsonArray()) {
            JsonObject pointJson = pointElement.getAsJsonObject();
            points.add(new Point(pointJson));
        }

        return points;
    }

    public String getStatus() {
        return status;
    }

    public String getName() {
        return name;
    }

    public String getUnit() {
        return unit;
    }

    public String getPeriod() {
        return period;
    }

    public String getDescription() {
        return description;
    }

    public List<Point> getValues() {
        return values;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/statistics/Point.java
================================================
package info.blockchain.api.statistics;

import com.google.gson.JsonObject;

public class Point {

    private float x;
    private float y;

    public Point (JsonObject pointJson) {
        this(
                pointJson.has("x") ? pointJson.get("x").getAsFloat() : 0.0F,
                pointJson.has("y") ? pointJson.get("y").getAsFloat() : 0.0F
        );
    }

    public Point (float x, float y) {
        this.x = x;
        this.y = y;
    }

    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/statistics/Statistics.java
================================================
package info.blockchain.api.statistics;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import info.blockchain.api.APIException;
import info.blockchain.api.HttpClient;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

/**
 * This class reflects the functionality documented
 * at https://blockchain.info/api/charts_api
 */
public class Statistics {
    private final String apiCode;

    public Statistics () {
        this(null);
    }

    public Statistics (String apiCode) {
        this.apiCode = apiCode;
    }

    /**
     * Gets the network statistics.
     *
     * @return An instance of the StatisticsResponse class
     * @throws APIException If the server returns an error
     */
    public StatisticsResponse getStats () throws APIException, IOException {
        Map<String, String> params = new HashMap<String, String>();
        params.put("format", "json");
        if (apiCode != null) {
            params.put("api_code", apiCode);
        }

        String response = HttpClient.getInstance().get("stats", params);
        return new StatisticsResponse(response);
    }

    /**
     * This method can be used to get and manipulate data behind all Blockchain.info's charts.
     *
     * @param type of chart (Example: transactions-per-second, total-bitcoins)
     * @param timeSpan (Example: 5weeks)
     * @param rollingAverage (Example: 8hours)
     * @return {@code Chart} represents the series of data of the chart
     * @throws APIException If the server returns an error
     */
    public Chart getChart(String type, String timeSpan, String rollingAverage) throws APIException, IOException {
        Map<String, String> params = new HashMap<String, String>();
        params.put("format", "json");
        if (apiCode != null) {
            params.put("api_code", apiCode);
        }
        if (timeSpan != null) {
            params.put("timespan", timeSpan);
        }
        if (rollingAverage != null) {
            params.put("rollingAverage", rollingAverage);
        }

        String response = HttpClient.getInstance().get("charts/" + type, params);
        JsonObject chartJson = new JsonParser().parse(response).getAsJsonObject();

        return new Chart(chartJson);
    }

    /**
     * This method can be used to get the data behind Blockchain.info's pools information.
     *
     * @param timeSpan (Example: 5weeks)
     * @return a map of pool name and the number of blocks it mined
     * @throws APIException If the server returns an error
     */
    public Map<String, Integer> getPools(String timeSpan) throws APIException, IOException {
        Map<String, String> params = new HashMap<String, String>();
        params.put("format", "json");
        if (apiCode != null) {
            params.put("api_code", apiCode);
        }
        if (timeSpan != null) {
            params.put("timespan", timeSpan);
        }

        String response = HttpClient.getInstance().get("pools", params);
        Type type = new TypeToken<Map<String, String>>(){}.getType();
        Gson gson = new Gson();
        Map<String, Integer> pools = gson.fromJson(response, type);

        return pools;
    }

}


================================================
FILE: src/main/java/info/blockchain/api/statistics/StatisticsResponse.java
================================================
package info.blockchain.api.statistics;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.math.BigDecimal;

/**
 * This class is used as a response object to the 'get' method in the 'Statistics' class
 */
public class StatisticsResponse {
    private BigDecimal tradeVolumeBTC;
    private BigDecimal tradeVolumeUSD;
    private BigDecimal minersRevenueBTC;
    private BigDecimal minersRevenueUSD;
    private BigDecimal marketPriceUSD;
    private BigDecimal estimatedTransactionVolumeUSD;
    private long totalFeesBTC;
    private long totalBTCSent;
    private long estimatedBTCSent;
    private long btcMined;
    private double difficulty;
    private double minutesBetweenBlocks;
    private long numberOfTransactions;
    private double hashRate;
    private long timestamp;
    private long minedBlocks;
    private long blocksSize;
    private long totalBTC;
    private long totalBlocks;
    private long nextRetarget;

    public StatisticsResponse (String jsonString) {
        JsonObject s = new JsonParser().parse(jsonString).getAsJsonObject();

        this.tradeVolumeBTC = new BigDecimal(s.get("trade_volume_btc").getAsString());
        this.tradeVolumeUSD = new BigDecimal(s.get("trade_volume_usd").getAsString());
        this.minersRevenueBTC = new BigDecimal(s.get("miners_revenue_btc").getAsString());
        this.minersRevenueUSD = new BigDecimal(s.get("miners_revenue_usd").getAsString());
        this.marketPriceUSD = new BigDecimal(s.get("market_price_usd").getAsString());
        this.estimatedTransactionVolumeUSD = new BigDecimal(s.get("estimated_transaction_volume_usd").getAsString());
        this.totalFeesBTC = s.get("total_fees_btc").getAsLong();
        this.totalBTCSent = s.get("total_btc_sent").getAsLong();
        this.estimatedBTCSent = s.get("estimated_btc_sent").getAsLong();
        this.btcMined = s.get("n_btc_mined").getAsLong();
        this.difficulty = s.get("difficulty").getAsDouble();
        this.minutesBetweenBlocks = s.get("minutes_between_blocks").getAsDouble();
        this.numberOfTransactions = s.get("n_tx").getAsLong();
        this.hashRate = s.get("hash_rate").getAsDouble();
        this.timestamp = s.get("timestamp").getAsLong();
        this.minedBlocks = s.get("n_blocks_mined").getAsLong();
        this.blocksSize = s.get("blocks_size").getAsLong();
        this.totalBTC = s.get("totalbc").getAsLong();
        this.totalBlocks = s.get("n_blocks_total").getAsLong();
        this.nextRetarget = s.get("nextretarget").getAsLong();
    }

    /**
     * @return Trade volume in the past 24 hours
     */
    public BigDecimal getTradeVolumeBTC () {
        return tradeVolumeBTC;
    }

    /**
     * @return Trade volume in the past 24 hours
     */
    public BigDecimal getTradeVolumeUSD () {
        return tradeVolumeUSD;
    }

    /**
     * @return Miners' revenue in BTC
     */
    public BigDecimal getMinersRevenueBTC () {
        return minersRevenueBTC;
    }

    /**
     * @return Miners' revenue in USD
     */
    public BigDecimal getMinersRevenueUSD () {
        return minersRevenueUSD;
    }

    /**
     * @return Current market price in USD
     */
    public BigDecimal getMarketPriceUSD () {
        return marketPriceUSD;
    }

    /**
     * @return Estimated transaction volume in the past 24 hours
     */
    public BigDecimal getEstimatedTransactionVolumeUSD () {
        return estimatedTransactionVolumeUSD;
    }

    /**
     * @return Total fees in the past 24 hours (in satoshi)
     */
    public long getTotalFeesBTC () {
        return totalFeesBTC;
    }

    /**
     * @return Total BTC sent in the past 24 hours (in satoshi)
     */
    public long getTotalBTCSent () {
        return totalBTCSent;
    }

    /**
     * @return Estimated BTC sent in the past 24 hours (in satoshi)
     */
    public long getEstimatedBTCSent () {
        return estimatedBTCSent;
    }

    /**
     * @return Number of BTC mined in the past 24 hours (in satoshi)
     */
    public long getBTCMined () {
        return btcMined;
    }

    /**
     * @return Current difficulty
     */
    public double getDifficulty () {
        return difficulty;
    }

    /**
     * @return Minutes between blocks
     */
    public double getMinutesBetweenBlocks () {
        return minutesBetweenBlocks;
    }

    /**
     * @return Number of transactions in the past 24 hours
     */
    public long getNumberOfTransactions () {
        return numberOfTransactions;
    }

    /**
     * @return Current hashrate in GH/s
     */
    public double getHashRate () {
        return hashRate;
    }

    /**
     * @return Timestamp of when this report was compiled (in ms)
     */
    public long getTimestamp () {
        return timestamp;
    }

    /**
     * @return Number of blocks mined in the past 24 hours
     */
    public long getMinedBlocks () {
        return minedBlocks;
    }

    /**
     * @return the blocksSize
     */
    public long getBlocksSize () {
        return blocksSize;
    }

    /**
     * @return Total BTC in existence (in satoshi)
     */
    public long getTotalBTC () {
        return totalBTC;
    }

    /**
     * @return Total number of blocks in existence (in satoshi)
     */
    public long getTotalBlocks () {
        return totalBlocks;
    }

    /**
     * @return The next block height where the difficulty retarget will occur
     */
    public long getNextRetarget () {
        return nextRetarget;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/wallet/Wallet.java
================================================
package info.blockchain.api.wallet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import info.blockchain.api.APIException;
import info.blockchain.api.HttpClient;
import info.blockchain.api.wallet.entity.Address;
import info.blockchain.api.wallet.entity.CreateWalletResponse;
import info.blockchain.api.wallet.entity.PaymentResponse;

/**
 * This class reflects the functionality documented
 * at https://blockchain.info/api/blockchain_wallet_api. It allows users to interact
 * with their Blockchain.info wallet. If you have an API code, please set it via the
 * `setApiCode` method.
 */
public class Wallet {
    private JsonParser jsonParser;

    private String serviceURL;
    private String identifier;
    private String password;
    private String secondPassword;
    private String apiCode;

    /**
     * Creates a new Blockchain.info wallet.
     *
     * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash)
     * @param password Password for the new wallet. At least 10 characters.
     * @param apiCode  API code with create wallets permission
     * @return An instance of the CreateWalletResponse class
     * @throws APIException If the server returns an error
     */
    public static CreateWalletResponse create (String serviceURL, String password,
                                               String apiCode) throws IOException, APIException {
        return create(serviceURL, password, apiCode, null, null, null);
    }

    /**
     * Creates a new Blockchain.info wallet. It can be created containing a pre-generated
     * private key or will otherwise generate a new private key.
     *
     * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash)
     * @param password   Password for the new wallet. At least 10 characters.
     * @param apiCode    API code with create wallets permission
     * @param privateKey Private key to add to the wallet (optional, nullable)
     * @param label      Label for the first address in the wallet (optional, nullable)
     * @param email      Email to associate with the new wallet (optional, nullable)
     * @return An instance of the CreateWalletResponse class
     * @throws APIException If the server returns an error
     */
    public static CreateWalletResponse create (String serviceURL, String password,String apiCode,
                                               String privateKey, String label, String email) throws IOException, APIException {
        Map<String, String> params = new HashMap<String, String>();

        params.put("password", password);
        params.put("api_code", apiCode);
        if (privateKey != null) {
            params.put("priv", privateKey);
        }
        if (label != null) {
            params.put("label", label);
        }
        if (email != null) {
            params.put("email", email);
        }

        String response = HttpClient.getInstance().post(serviceURL, "api/v2/create", params);
        JsonObject jsonObj = new JsonParser().parse(response).getAsJsonObject();

        return new CreateWalletResponse(
                jsonObj.get("guid").getAsString(),
                jsonObj.get("address").getAsString(),
                jsonObj.get("label").getAsString());
    }

    /**
     * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash)
     * @param apiCode    API Code
     * @param identifier Wallet identifier (GUID)
     * @param password   Decryption password
     */
    public Wallet (String serviceURL, String apiCode, String identifier, String password) {
        this(serviceURL, apiCode, identifier, password, null);
    }

    /**
     * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash)
     * @param apiCode    API Code
     * @param identifier     Wallet identifier (GUID)
     * @param password       Decryption password
     * @param secondPassword Second password
     */
    public Wallet (String serviceURL, String apiCode, String identifier, String password, String secondPassword) {
        this.serviceURL = serviceURL;
        this.apiCode = apiCode;
        this.identifier = identifier;
        this.password = password;
        this.secondPassword = secondPassword;
        this.jsonParser = new JsonParser();
    }

    /**
     * Sends bitcoin from your wallet to a single address.
     *
     * @param toAddress   Recipient bitcoin address
     * @param amount      Amount to send (in satoshi)
     * @param fromAddress Specific address to send from (optional, nullable)
     * @param fee         Transaction fee in satoshi. Must be greater than the default fee (optional, nullable).
     * @return An instance of the PaymentResponse class
     * @throws APIException If the server returns an error
     */
    public PaymentResponse send (String toAddress, long amount, String fromAddress, Long fee) throws APIException, IOException {
        Map<String, Long> recipient = new HashMap<String, Long>();
        recipient.put(toAddress, amount);

        return sendMany(recipient, fromAddress, fee);
    }

    /**
     * Sends bitcoin from your wallet to multiple addresses.
     *
     * @param recipients  Map with the structure of 'address':amount in satoshi (String:long)
     * @param fromAddress Specific address to send from (optional, nullable)
     * @param fee         Transaction fee in satoshi. Must be greater than the default fee (optional, nullable).
     * @return An instance of the PaymentResponse class
     * @throws APIException If the server returns an error
     * @throws IllegalArgumentException If no recipients is null or empty
     */
    public PaymentResponse sendMany (Map<String, Long> recipients, String fromAddress, Long fee) throws APIException, IOException {
        Map<String, String> params = buildBasicRequestWithSecondPassword();
        String method = null;

       if (recipients.size() == 1) {
            method = "payment";
            Entry<String, Long> e = recipients.entrySet().iterator().next();
            params.put("to", e.getKey());
            params.put("amount", e.getValue().toString());
        } else {
            method = "sendmany";
            params.put("recipients", new Gson().toJson(recipients));
        }

        if (fromAddress != null) {
            params.put("from", fromAddress);
        }
        if (fee != null) {
            params.put("fee", fee.toString());
        }

        String response = HttpClient.getInstance().post(serviceURL, String.format("merchant/%s/%s", identifier, method), params);
        JsonObject topElem = parseResponse(response);

        return new PaymentResponse(topElem.get("message").getAsString(), topElem.get("tx_hash").getAsString(), topElem.has("notice") ? topElem.get("notice").getAsString() : null);
    }

    /**
     * Fetches the wallet balance. Includes unconfirmed transactions and
     * possibly double spends.
     *
     * @return Wallet balance in satoshi
     * @throws APIException If the server returns an error
     */
    public long getBalance () throws APIException, IOException {
        String response = HttpClient.getInstance().get(serviceURL, String.format("merchant/%s/balance", identifier), buildBasicRequest());
        JsonObject topElem = parseResponse(response);

        return topElem.get("balance").getAsLong();
    }

    /**
     * Lists all active addresses in the wallet.
     *
     * @return A list of Address objects
     * @throws APIException If the server returns an error
     */
    public List<Address> listAddresses () throws APIException, IOException {
        Map<String, String> params = buildBasicRequest();

        String response = HttpClient.getInstance().get(serviceURL, String.format("merchant/%s/list", identifier), params);
        JsonObject topElem = parseResponse(response);

        List<Address> addresses = new ArrayList<Address>();
        for (JsonElement jAddr : topElem.get("addresses").getAsJsonArray()) {
            JsonObject a = jAddr.getAsJsonObject();
            Address address = new Address(a.get("balance").getAsLong(), a.get("address").getAsString(), a.has("label") && !a.get("label").isJsonNull() ? a.get("label").getAsString() : null, a.get("total_received").getAsLong());

            addresses.add(address);
        }

        return addresses;
    }

    /**
     * Retrieves an address from the wallet.
     *
     * @param address       Address in the wallet to look up
     * @return An instance of the Address class
     * @throws APIException If the server returns an error
     */
    public Address getAddress (String address) throws APIException, IOException {
        Map<String, String> params = buildBasicRequest();
        params.put("address", address);

        String response = HttpClient.getInstance().get(serviceURL, String.format("merchant/%s/address_balance", identifier), params);
        JsonObject topElem = parseResponse(response);

        return new Address(topElem.get("balance").getAsLong(), topElem.get("address").getAsString(), topElem.has("label") && !topElem.get("label").isJsonNull() ? topElem.get("label").getAsString() : null, topElem.get("total_received").getAsLong());
    }

    /**
     * Generates a new address and adds it to the wallet.
     *
     * @param label Label to attach to this address (optional, nullable)
     * @return An instance of the Address class
     * @throws APIException If the server returns an error
     */
    public Address newAddress (String label) throws APIException, IOException {
        Map<String, String> params = buildBasicRequest();
        if (label != null) {
            params.put("label", label);
        }

        String response = HttpClient.getInstance().post(serviceURL, String.format("merchant/%s/new_address", identifier), params);
        JsonObject topElem = parseResponse(response);

        return new Address(0L, topElem.get("address").getAsString(), topElem.has("label") && !topElem.get("label").isJsonNull() ? topElem.get("label").getAsString() : null, 0L);
    }

    /**
     * Archives an address.
     *
     * @param address Address to archive
     * @return String representation of the archived address
     * @throws APIException If the server returns an error
     */
    public String archiveAddress (String address) throws APIException, IOException {
        Map<String, String> params = buildBasicRequest();
        params.put("address", address);

        String response = HttpClient.getInstance().post(serviceURL, String.format("merchant/%s/archive_address", identifier), params);
        JsonObject topElem = parseResponse(response);

        return topElem.get("archived").getAsString();
    }

    /**
     * Unarchives an address.
     *
     * @param address Address to unarchive
     * @return String representation of the unarchived address
     * @throws APIException If the server returns an error
     */
    public String unarchiveAddress (String address) throws APIException, IOException {
        Map<String, String> params = buildBasicRequest();
        params.put("address", address);

        String response = HttpClient.getInstance().post(serviceURL, String.format("merchant/%s/unarchive_address", identifier), params);
        JsonObject topElem = parseResponse(response);

        return topElem.get("active").getAsString();
    }

    private Map<String, String> buildBasicRequest () {
        Map<String, String> params = new HashMap<String, String>();

        params.put("password", password);
        if (apiCode != null) {
            params.put("api_code", apiCode);
        }

        return params;
    }

    private Map<String, String> buildBasicRequestWithSecondPassword () {
        Map<String, String> params = buildBasicRequest();
        if (secondPassword != null) {
            params.put("second_password", secondPassword);
        }

        return params;
    }

    private JsonObject parseResponse (String response) throws APIException {
        JsonObject topElem = jsonParser.parse(response).getAsJsonObject();
        if (topElem.has("error")) {
            throw new APIException(topElem.get("error").getAsString());
        }

        return topElem;
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Wallet wallet = (Wallet) o;

        return identifier == null ? wallet.identifier == null : identifier.equals(wallet.identifier);
    }

    @Override
    public int hashCode () {
        return identifier != null ? identifier.hashCode() : 0;
    }

}


================================================
FILE: src/main/java/info/blockchain/api/wallet/entity/Address.java
================================================
package info.blockchain.api.wallet.entity;

/**
 * Used in combination with the `Wallet` class
 */
public class Address {
    private long balance;
    private String address;
    private String label;
    private long totalReceived;

    public Address (long balance, String address, String label, long totalReceived) {
        this.balance = balance;
        this.address = address;
        this.label = label;
        this.totalReceived = totalReceived;
    }

    @Override
    public boolean equals (Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        Address address1 = (Address) o;

        if (balance != address1.balance) {
            return false;
        }
        if (totalReceived != address1.totalReceived) {
            return false;
        }
        if (address != null ? !address.equals(address1.address) : address1.address != null) {
            return false;
        }
        return !(label != null ? !label.equals(address1.label) : address1.label != null);

    }

    @Override
    public int hashCode () {
        int result = (int) (balance ^ (balance >>> 32));
        result = 31 * result + (address != null ? address.hashCode() : 0);
        result = 31 * result + (label != null ? label.hashCode() : 0);
        result = 31 * result + (int) (totalReceived ^ (totalReceived >>> 32));
        return result;
    }

    /**
     * @return Balance in satoshi
     */
    public long getBalance () {
        return balance;
    }

    /**
     * @return String representation of the address
     */
    public String getAddress () {
        return address;
    }

    /**
     * @return Label attached to the address
     */
    public String getLabel () {
        return label;
    }

    /**
     * @return Total received amount in satoshi
     */
    public long getTotalReceived () {
        return totalReceived;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/wallet/entity/CreateWalletResponse.java
================================================
package info.blockchain.api.wallet.entity;

/**
 * Used in response to the `create` method in the `CreateWallet` class.
 */
public class CreateWalletResponse {
    private String identifier;
    private String address;
    private String label;

    public CreateWalletResponse (String identifier, String address, String label) {
        this.identifier = identifier;
        this.address = address;
        this.label = label;
    }

    /**
     * @return Wallet identifier (GUID)
     */
    public String getIdentifier () {
        return identifier;
    }

    /**
     * @return First address in the wallet
     */
    public String getAddress () {
        return address;
    }

    /**
     * @return Label of first address in the wallet
     */
    public String getLable () {
        return label;
    }
}


================================================
FILE: src/main/java/info/blockchain/api/wallet/entity/PaymentResponse.java
================================================
package info.blockchain.api.wallet.entity;

/**
 * Used in response to the `send` and `sendMany` methods in the `Wallet` class.
 */
public class PaymentResponse {
    private String message;
    private String txHash;
    private String notice;

    public PaymentResponse (String message, String txHash, String notice) {
        this.message = message;
        this.txHash = txHash;
        this.notice = notice;
    }

    @Override
    public boolean equals (Object o) {
        if (o == null) {
            return false;
        }
        if (o instanceof PaymentResponse) {
            PaymentResponse that = (PaymentResponse) o;
            return (this.getMessage().equals(that.getMessage()) && this.getTxHash().equals(that.getTxHash()) && this.getNotice().equals(that.getNotice()));
        }
        return false;
    }

    /**
     * @return Response message from the server
     */
    public String getMessage () {
        return message;
    }

    /**
     * @return Transaction hash
     */
    public String getTxHash () {
        return txHash;
    }

    /**
     * @return Additional response message from the server
     */
    public String getNotice () {
        return notice;
    }

}


================================================
FILE: src/test/java/info/blockchain/api/AppTest.java
================================================
package info.blockchain.api;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 * Unit test for simple App.
 */
public class AppTest extends TestCase {
    /**
     * Create the test case
     *
     * @param testName name of the test case
     */
    public AppTest (String testName) {
        super(testName);
    }

    /**
     * @return the suite of tests being tested
     */
    public static Test suite () {
        return new TestSuite(AppTest.class);
    }

    /**
     * Rigourous Test :-)
     */
    public void testApp () {
        assertTrue(true);
    }
}


================================================
FILE: src/test/java/info/blockchain/api/blockexplorer/BlockExplorerTest.java
================================================
package info.blockchain.api.blockexplorer;

import info.blockchain.api.blockexplorer.entity.*;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.*;

/**
 * Created by ray on 10/05/2017.
 */
public class BlockExplorerTest {

    BlockExplorer client;

    @Before
    public void setUp () throws Exception {
        client = new BlockExplorer();
    }

    @Test
    public void getAddress () throws Exception {
        Address address = client.getAddress("1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW", FilterType.All, 10, null);

        assertEquals("1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW", address.getAddress());
        assertEquals("07feead7f9fb7d16a0251421ac9fa090169cc169",
                address.getHash160());
        assertEquals(0, address.getFinalBalance());
        assertEquals(16, address.getTxCount());
        assertEquals(605204 , address.getTotalReceived());
        assertEquals(605204 , address.getTotalSent());
        assertEquals(10, address.getTransactions().size());
    }

    @Test
    public void getUnspentOutputs () throws Exception {
        String address1 = "1FrWWFJ95Jq7EDgpkeBwVLAtoJMPwmYS7T";
        String address2 = "xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn";
        List<UnspentOutput> unspentOutputs = client.getUnspentOutputs(Arrays.asList(address1, address2), 6, 10);

        assertTrue(unspentOutputs != null && unspentOutputs.size() != 0);
        assertEquals("2e7ab41818ee0ab987d393d4c8bf5e436b6e8c15ef3535a2b3eac581e33c7472", unspentOutputs.get(0).getTransactionHash());
        assertEquals(20000, unspentOutputs.get(0).getValue());
    }

    @Test
    public void getBalance () throws Exception {
        String address1 = "1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW";
        String address2 = "xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn";

        List<String> list = Arrays.asList(address1, address2);

        Map<String, Balance> balances = client.getBalance(list, FilterType.All);

        assertEquals(0, balances.get(address1).getFinalBalance());
        assertEquals(16, balances.get(address1).getTxCount());
        assertEquals(605204, balances.get(address1).getTotalReceived());
        assertEquals(20000, balances.get(address2).getFinalBalance());
        assertEquals(1, balances.get(address2).getTxCount());
        assertEquals(20000, balances.get(address2).getTotalReceived());
    }

    @Test
    public void getXpub () throws Exception {
        String address = "xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn";
        XpubFull xpub = client.getXpub(address, null, null, null);

        assertEquals(xpub.getAddress(),
                "xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn");
        assertEquals(1, xpub.getTxCount());
        assertEquals(20000, xpub.getTotalReceived());
        assertEquals(0, xpub.getTotalSent());
        assertEquals(20000, xpub.getFinalBalance());
        assertEquals(0, xpub.getChangeIndex());
        assertEquals(1, xpub.getAccountIndex());
        assertEquals(0, xpub.getGapLimit());
    }

}
Download .txt
gitextract_nic81c8s/

├── .gitignore
├── CHANGELOG.md
├── LICENSE.txt
├── README.md
├── docs/
│   ├── blockexplorer.md
│   ├── exchangerates.md
│   ├── pushtx.md
│   ├── receive.md
│   ├── statistics.md
│   └── wallet.md
├── pom.xml
└── src/
    ├── main/
    │   └── java/
    │       └── info/
    │           └── blockchain/
    │               └── api/
    │                   ├── APIException.java
    │                   ├── HttpClient.java
    │                   ├── HttpClientInterface.java
    │                   ├── OkClient.java
    │                   ├── Util.java
    │                   ├── blockexplorer/
    │                   │   ├── BlockExplorer.java
    │                   │   └── entity/
    │                   │       ├── Address.java
    │                   │       ├── AddressSummary.java
    │                   │       ├── Balance.java
    │                   │       ├── Block.java
    │                   │       ├── FilterType.java
    │                   │       ├── Input.java
    │                   │       ├── LatestBlock.java
    │                   │       ├── MultiAddress.java
    │                   │       ├── MultiAddressBalance.java
    │                   │       ├── Output.java
    │                   │       ├── SimpleBlock.java
    │                   │       ├── Transaction.java
    │                   │       ├── UnspentOutput.java
    │                   │       └── XpubFull.java
    │                   ├── etc/
    │                   │   └── Base58.java
    │                   ├── exchangerates/
    │                   │   ├── Currency.java
    │                   │   └── ExchangeRates.java
    │                   ├── pushtx/
    │                   │   └── PushTx.java
    │                   ├── receive/
    │                   │   ├── CallbackLog.java
    │                   │   ├── Receive.java
    │                   │   └── ReceiveResponse.java
    │                   ├── statistics/
    │                   │   ├── Chart.java
    │                   │   ├── Point.java
    │                   │   ├── Statistics.java
    │                   │   └── StatisticsResponse.java
    │                   └── wallet/
    │                       ├── Wallet.java
    │                       └── entity/
    │                           ├── Address.java
    │                           ├── CreateWalletResponse.java
    │                           └── PaymentResponse.java
    └── test/
        └── java/
            └── info/
                └── blockchain/
                    └── api/
                        ├── AppTest.java
                        └── blockexplorer/
                            └── BlockExplorerTest.java
Download .txt
SYMBOL INDEX (355 symbols across 37 files)

FILE: src/main/java/info/blockchain/api/APIException.java
  class APIException (line 8) | public class APIException extends Exception {
    method APIException (line 11) | public APIException (String message) {

FILE: src/main/java/info/blockchain/api/HttpClient.java
  class HttpClient (line 15) | public class HttpClient implements HttpClientInterface {
    method getInstance (line 22) | public synchronized static HttpClientInterface getInstance () {
    method setCustomHttpClient (line 34) | public static void setCustomHttpClient (HttpClientInterface httpClient) {
    method get (line 46) | public String get (String resource, Map<String, String> params) throws...
    method get (line 50) | public String get (String baseURL, String resource, Map<String, String...
    method post (line 63) | public String post (String resource, Map<String, String> params) throw...
    method post (line 67) | public String post (String baseURL, String resource, Map<String, Strin...
    method openURL (line 71) | private static String openURL (String baseURL, String resource, Map<St...
    method urlEncodeParams (line 133) | private static String urlEncodeParams (Map<String, String> params) {
    method inputStreamToString (line 156) | private static String inputStreamToString (InputStream is) throws IOEx...

FILE: src/main/java/info/blockchain/api/HttpClientInterface.java
  type HttpClientInterface (line 11) | public interface HttpClientInterface {
    method get (line 13) | String get (String resource, Map<String, String> params) throws APIExc...
    method get (line 15) | String get (String baseURL, String resource, Map<String, String> param...
    method post (line 17) | String post (String resource, Map<String, String> params) throws APIEx...
    method post (line 19) | String post (String baseURL, String resource, Map<String, String> para...

FILE: src/main/java/info/blockchain/api/OkClient.java
  class OkClient (line 16) | public class OkClient implements HttpClientInterface {
    method OkClient (line 28) | private OkClient() {
    method getOkClientInstance (line 38) | public synchronized static OkClient getOkClientInstance() {
    method getOkHttpClientInstance (line 48) | private synchronized static OkHttpClient getOkHttpClientInstance() {
    method get (line 66) | @Override
    method get (line 86) | @Override
    method getInternal (line 97) | private String getInternal(HttpUrl url) throws IOException {
    method post (line 116) | @Override
    method post (line 133) | @Override
    method postInternal (line 140) | private String postInternal(HttpUrl url, Map<String, String> params) t...
    method makeRequest (line 154) | private String makeRequest(Request request) throws IOException {
    method getHttpUrlBuilder (line 164) | private HttpUrl.Builder getHttpUrlBuilder(String resource) {
    method getHttpUrlBuilder (line 173) | private HttpUrl.Builder getHttpUrlBuilder(String baseURL, String resou...
    method isNotSuccessfulResponse (line 185) | private boolean isNotSuccessfulResponse(Response response) {

FILE: src/main/java/info/blockchain/api/Util.java
  class Util (line 3) | public class Util {

FILE: src/main/java/info/blockchain/api/blockexplorer/BlockExplorer.java
  class BlockExplorer (line 19) | public class BlockExplorer {
    method BlockExplorer (line 22) | public BlockExplorer () {
    method BlockExplorer (line 29) | public BlockExplorer (String apiCode) {
    method getTransaction (line 41) | @Deprecated
    method getTransaction (line 53) | public Transaction getTransaction (String txHash) throws APIException,...
    method getBlock (line 67) | @Deprecated
    method getBlock (line 79) | public Block getBlock (String blockHash) throws APIException, IOExcept...
    method getAddress (line 95) | public Address getAddress (String address, FilterType filter, Integer ...
    method getAddress (line 119) | public Address getAddress (String address) throws APIException, IOExce...
    method getBlocksAtHeight (line 131) | public List<Block> getBlocksAtHeight (long height) throws APIException...
    method getUnspentOutputs (line 153) | public List<UnspentOutput> getUnspentOutputs (List<String> addressList...
    method getUnspentOutputs (line 191) | public List<UnspentOutput> getUnspentOutputs (String address) throws A...
    method getLatestBlock (line 201) | public LatestBlock getLatestBlock () throws APIException, IOException {
    method getUnconfirmedTransactions (line 213) | public List<Transaction> getUnconfirmedTransactions () throws APIExcep...
    method getBlocks (line 233) | public List<SimpleBlock> getBlocks () throws APIException, IOException {
    method getBlocks (line 244) | public List<SimpleBlock> getBlocks (long timestamp) throws APIExceptio...
    method getBlocks (line 255) | public List<SimpleBlock> getBlocks (String poolName) throws APIExcepti...
    method getBalance (line 276) | public Map<String, Balance> getBalance(List<String> addressList, Filte...
    method getMultiAddress (line 306) | public MultiAddress getMultiAddress(List<String> addressList, FilterTy...
    method getXpub (line 335) | public XpubFull getXpub(String xpub, FilterType filter, Integer limit,...
    method buildBasicRequest (line 341) | private Map<String, String> buildBasicRequest () {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Address.java
  class Address (line 12) | public class Address {
    method Address (line 21) | public Address (String hash160, String address, long totalReceived, lo...
    method Address (line 31) | public Address (JsonObject a) {
    method equals (line 49) | @Override
    method hashCode (line 76) | @Override
    method getHash160 (line 89) | public String getHash160 () {
    method getAddress (line 96) | public String getAddress () {
    method getTotalReceived (line 103) | public long getTotalReceived () {
    method getTotalSent (line 110) | public long getTotalSent () {
    method getFinalBalance (line 117) | public long getFinalBalance () {
    method getTxCount (line 124) | public int getTxCount () {
    method getTransactions (line 131) | public List<Transaction> getTransactions () {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/AddressSummary.java
  class AddressSummary (line 5) | public class AddressSummary {
    method AddressSummary (line 16) | public AddressSummary (String address, int txCount, long totalReceived...
    method AddressSummary (line 28) | public AddressSummary (JsonObject json) {
    method getAddress (line 41) | public String getAddress() {
    method getTxCount (line 45) | public int getTxCount() {
    method setTxCount (line 49) | public void setTxCount(int txCount) {
    method getTotalReceived (line 53) | public long getTotalReceived() {
    method getTotalSent (line 57) | public long getTotalSent() {
    method getFinalBalance (line 61) | public long getFinalBalance() {
    method getChangeIndex (line 65) | public int getChangeIndex() {
    method getAccountIndex (line 69) | public int getAccountIndex() {
    method setFinalBalance (line 73) | public void setFinalBalance(long final_balance) {
    method setAddress (line 77) | public void setAddress(String address) {
    method setTotalReceived (line 81) | public void setTotalReceived(long total_received) {
    method setTotalSent (line 85) | public void setTotalSent(long total_sent) {
    method setChangeIndex (line 89) | public void setChangeIndex(int change_index) {
    method setAccountIndex (line 93) | public void setAccountIndex(int account_index) {
    method getGapLimit (line 97) | public int getGapLimit () {
    method setGapLimit (line 101) | public void setGapLimit (int gapLimit) {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Balance.java
  class Balance (line 5) | public class Balance {
    method Balance (line 11) | public Balance (long finalBalance, long txCount, long totalReceived) {
    method Balance (line 17) | public Balance (JsonObject json) {
    method getFinalBalance (line 26) | public long getFinalBalance() {
    method getTxCount (line 30) | public long getTxCount() {
    method getTotalReceived (line 34) | public long getTotalReceived() {
    method setFinalBalance (line 38) | public void setFinalBalance(long final_balance) {
    method setTxCount (line 42) | public void setTxCount(long n_tx) {
    method setTotalReceived (line 46) | public void setTotalReceived(long total_received) {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Block.java
  class Block (line 13) | public class Block extends SimpleBlock {
    method Block (line 26) | public Block (long height, String hash, long time, boolean mainChain, ...
    method Block (line 41) | public Block (JsonObject b) {
    method equals (line 50) | @Override
    method hashCode (line 86) | @Override
    method getVersion (line 101) | public int getVersion () {
    method getPreviousBlockHash (line 108) | public String getPreviousBlockHash () {
    method getMerkleRoot (line 115) | public String getMerkleRoot () {
    method getBits (line 122) | public long getBits () {
    method getFees (line 129) | public long getFees () {
    method getNonce (line 136) | public long getNonce () {
    method getSize (line 143) | public long getSize () {
    method getIndex (line 150) | public long getIndex () {
    method getReceivedTime (line 157) | public long getReceivedTime () {
    method getRelayedBy (line 164) | public String getRelayedBy () {
    method getTransactions (line 171) | public List<Transaction> getTransactions () {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/FilterType.java
  type FilterType (line 3) | public enum FilterType {
    method FilterType (line 10) | FilterType (Integer filterInt) {
    method getFilterInt (line 14) | public Integer getFilterInt () {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Input.java
  class Input (line 9) | public class Input {
    method Input (line 14) | public Input (Output previousOutput, long sequence, String scriptSigna...
    method Input (line 20) | public Input (JsonObject i) {
    method getPreviousOutput (line 32) | public Output getPreviousOutput () {
    method getSequence (line 39) | public long getSequence () {
    method getScriptSignature (line 46) | public String getScriptSignature () {
    method equals (line 50) | @Override
    method hashCode (line 76) | @Override

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/LatestBlock.java
  class LatestBlock (line 12) | public class LatestBlock extends SimpleBlock {
    method LatestBlock (line 16) | public LatestBlock (long height, String hash, long time, boolean mainC...
    method LatestBlock (line 22) | public LatestBlock (JsonObject b) {
    method getTransactionIndexes (line 34) | public List<Long> getTransactionIndexes () {
    method getIndex (line 41) | public long getIndex () {
    method equals (line 45) | @Override
    method hashCode (line 66) | @Override

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/MultiAddress.java
  class MultiAddress (line 9) | public class MultiAddress {
    method MultiAddress (line 17) | public MultiAddress (MultiAddressBalance multiAddressBalance, List<Add...
    method MultiAddress (line 23) | public MultiAddress (JsonObject json) {
    method getMultiAddressBalance (line 41) | public MultiAddressBalance getMultiAddressBalance () {
    method getAddresses (line 45) | public List<AddressSummary> getAddresses() {
    method getTxs (line 49) | public List<Transaction> getTxs() {
    method setMultiAddressBalance (line 53) | public void setMultiAddressBalance (MultiAddressBalance multiAddressBa...
    method setAddresses (line 57) | public void setAddresses(List<AddressSummary> addresses) {
    method setTxs (line 61) | public void setTxs(List<Transaction> txs) {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/MultiAddressBalance.java
  class MultiAddressBalance (line 5) | public class MultiAddressBalance {
    method MultiAddressBalance (line 13) | public MultiAddressBalance (int txCount, int txCountFiltered, long tot...
    method MultiAddressBalance (line 21) | public MultiAddressBalance (JsonObject json) {
    method getTxCount (line 31) | public int getTxCount() {
    method setTxCount (line 35) | public void setTxCount(int txCount) {
    method getTotalReceived (line 39) | public long getTotalReceived() {
    method getTotalSent (line 43) | public long getTotalSent() {
    method getFinalBalance (line 47) | public long getFinalBalance() {
    method setFinalBalance (line 51) | public void setFinalBalance(long final_balance) {
    method setTotalReceived (line 55) | public void setTotalReceived(long total_received) {
    method setTotalSent (line 59) | public void setTotalSent(long total_sent) {
    method getTxCountFiltered (line 63) | public int getTxCountFiltered () {
    method setTxCountFiltered (line 67) | public void setTxCountFiltered (int txCountFiltered) {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Output.java
  class Output (line 8) | public class Output {
    method Output (line 17) | public Output (int n, long value, String address, long txIndex, String...
    method equals (line 29) | @Override
    method hashCode (line 54) | @Override
    method Output (line 63) | public Output (JsonObject o) {
    method Output (line 67) | public Output (JsonObject o, boolean spent) {
    method getN (line 74) | public int getN () {
    method getValue (line 81) | public long getValue () {
    method getAddress (line 88) | public String getAddress () {
    method getTxIndex (line 95) | public long getTxIndex () {
    method getScript (line 102) | public String getScript () {
    method isSpent (line 109) | public boolean isSpent () {
    method isSpentToAddress (line 116) | public boolean isSpentToAddress () {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/SimpleBlock.java
  class SimpleBlock (line 8) | public class SimpleBlock {
    method SimpleBlock (line 14) | public SimpleBlock (long height, String hash, long time, boolean mainC...
    method SimpleBlock (line 21) | public SimpleBlock (JsonObject b) {
    method equals (line 25) | @Override
    method hashCode (line 49) | @Override
    method getHeight (line 61) | public long getHeight () {
    method getHash (line 68) | public String getHash () {
    method getTime (line 75) | public long getTime () {
    method isMainChain (line 82) | public boolean isMainChain () {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/Transaction.java
  class Transaction (line 12) | public class Transaction {
    method Transaction (line 25) | public Transaction (boolean doubleSpend, long blockHeight, long time, ...
    method Transaction (line 40) | public Transaction (JsonObject t) {
    method Transaction (line 45) | public Transaction (JsonObject t, long blockHeight, boolean doubleSpen...
    method equals (line 61) | @Override
    method hashCode (line 91) | @Override
    method isDoubleSpend (line 105) | public boolean isDoubleSpend () {
    method getBlockHeight (line 112) | public long getBlockHeight () {
    method getTime (line 119) | public long getTime () {
    method getLockTime (line 126) | public long getLockTime () {
    method getRelayedBy (line 133) | public String getRelayedBy () {
    method getHash (line 140) | public String getHash () {
    method getIndex (line 147) | public long getIndex () {
    method getVersion (line 154) | public int getVersion () {
    method getSize (line 161) | public long getSize () {
    method getInputs (line 168) | public List<Input> getInputs () {
    method getOutputs (line 175) | public List<Output> getOutputs () {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/UnspentOutput.java
  class UnspentOutput (line 8) | public class UnspentOutput {
    method UnspentOutput (line 16) | public UnspentOutput (int n, String transactionHash, long transactionI...
    method UnspentOutput (line 25) | public UnspentOutput (JsonObject o) {
    method equals (line 29) | @Override
    method hashCode (line 53) | @Override
    method getN (line 65) | public int getN () {
    method getTransactionHash (line 72) | public String getTransactionHash () {
    method getTransactionIndex (line 79) | public long getTransactionIndex () {
    method getScript (line 86) | public String getScript () {
    method getValue (line 93) | public long getValue () {
    method getConfirmations (line 100) | public long getConfirmations () {

FILE: src/main/java/info/blockchain/api/blockexplorer/entity/XpubFull.java
  class XpubFull (line 9) | public class XpubFull {
    method XpubFull (line 21) | public XpubFull (String address, int txCount, long totalReceived, long...
    method XpubFull (line 34) | public XpubFull (AddressSummary addressSummary, List<Transaction> txs) {
    method XpubFull (line 46) | public XpubFull (JsonObject json) {
    method getAddress (line 66) | public String getAddress() {
    method getTxCount (line 70) | public int getTxCount() {
    method setTxCount (line 74) | public void setTxCount(int txCount) {
    method getTotalReceived (line 78) | public long getTotalReceived() {
    method getTotalSent (line 82) | public long getTotalSent() {
    method getFinalBalance (line 86) | public long getFinalBalance() {
    method getChangeIndex (line 90) | public int getChangeIndex() {
    method getAccountIndex (line 94) | public int getAccountIndex() {
    method setFinalBalance (line 98) | public void setFinalBalance(long final_balance) {
    method setAddress (line 102) | public void setAddress(String address) {
    method setTotalReceived (line 106) | public void setTotalReceived(long total_received) {
    method setTotalSent (line 110) | public void setTotalSent(long total_sent) {
    method setChangeIndex (line 114) | public void setChangeIndex(int change_index) {
    method setAccountIndex (line 118) | public void setAccountIndex(int account_index) {
    method getGapLimit (line 122) | public int getGapLimit () {
    method setGapLimit (line 126) | public void setGapLimit (int gapLimit) {
    method getTxs (line 130) | public List<Transaction> getTxs () {
    method setTxs (line 134) | public void setTxs (List<Transaction> txs) {

FILE: src/main/java/info/blockchain/api/etc/Base58.java
  class Base58 (line 46) | public class Base58 {
    method encode (line 64) | public static String encode (byte[] input) {
    method decode (line 101) | public static byte[] decode (String input) throws ParseException {
    method decodeToBigInteger (line 137) | public static BigInteger decodeToBigInteger (String input) throws Pars...
    method decodeChecked (line 149) | public static byte[] decodeChecked (String input) throws ParseException {
    method divmod (line 175) | private static byte divmod (byte[] number, int firstDigit, int base, i...
    method hashTwice (line 187) | public static byte[] hashTwice (byte[] input) {

FILE: src/main/java/info/blockchain/api/exchangerates/Currency.java
  class Currency (line 8) | public class Currency {
    method Currency (line 15) | public Currency (double buy, double sell, double last, double price15m...
    method getBuy (line 26) | public BigDecimal getBuy () {
    method getSell (line 33) | public BigDecimal getSell () {
    method getLast (line 40) | public BigDecimal getLast () {
    method getPrice15m (line 47) | public BigDecimal getPrice15m () {
    method getSymbol (line 54) | public String getSymbol () {

FILE: src/main/java/info/blockchain/api/exchangerates/ExchangeRates.java
  class ExchangeRates (line 20) | public class ExchangeRates {
    method ExchangeRates (line 24) | public ExchangeRates () {
    method ExchangeRates (line 28) | public ExchangeRates (String apiCode) {
    method getTicker (line 39) | public Map<String, Currency> getTicker () throws APIException, IOExcep...
    method toBTC (line 67) | public BigDecimal toBTC (String currency, BigDecimal value) throws API...
    method toFiat (line 87) | public BigDecimal toFiat (String currency, BigDecimal value) throws AP...

FILE: src/main/java/info/blockchain/api/pushtx/PushTx.java
  class PushTx (line 15) | public class PushTx {
    method pushTx (line 22) | public static void pushTx (String tx) throws APIException, IOException {
    method pushTx (line 33) | public static void pushTx (String tx, String apiCode) throws APIExcept...

FILE: src/main/java/info/blockchain/api/receive/CallbackLog.java
  class CallbackLog (line 5) | public class CallbackLog {
    method CallbackLog (line 11) | public CallbackLog (JsonObject callbackJson) {
    method CallbackLog (line 20) | public CallbackLog (String callback, String callbackTime, String respo...
    method getCallback (line 27) | public String getCallback () {
    method setCallback (line 31) | public void setCallback (String callback) {
    method getCallbackTime (line 35) | public String getCallbackTime () {
    method setCallbackTime (line 39) | public void setCallbackTime (String callbackTime) {
    method getResponse (line 43) | public String getResponse () {
    method setResponse (line 47) | public void setResponse (String response) {
    method getResponseCode (line 51) | public int getResponseCode () {
    method setResponseCode (line 55) | public void setResponseCode (int responseCode) {

FILE: src/main/java/info/blockchain/api/receive/Receive.java
  class Receive (line 18) | public class Receive {
    method Receive (line 24) | public Receive (String apiCode) {
    method receive (line 36) | public ReceiveResponse receive (String xPUB, String callbackUrl) throw...
    method checkGap (line 56) | public int checkGap (String xPUB) throws APIException, IOException {
    method getCallbackLog (line 74) | public CallbackLog getCallbackLog (String callbackUrl) throws APIExcep...

FILE: src/main/java/info/blockchain/api/receive/ReceiveResponse.java
  class ReceiveResponse (line 6) | public class ReceiveResponse {
    method ReceiveResponse (line 11) | public ReceiveResponse (int index, String receivingAddress, String cal...
    method getIndex (line 20) | public int getIndex () {
    method getReceivingAddress (line 27) | public String getReceivingAddress () {
    method getCallbackUrl (line 34) | public String getCallbackUrl () {

FILE: src/main/java/info/blockchain/api/statistics/Chart.java
  class Chart (line 9) | public class Chart {
    method Chart (line 18) | public Chart (JsonObject chartJson) {
    method Chart (line 30) | public Chart (String status, String name, String unit, String period, ...
    method getPoints (line 39) | private List<Point> getPoints(JsonObject chartJson) {
    method getStatus (line 49) | public String getStatus() {
    method getName (line 53) | public String getName() {
    method getUnit (line 57) | public String getUnit() {
    method getPeriod (line 61) | public String getPeriod() {
    method getDescription (line 65) | public String getDescription() {
    method getValues (line 69) | public List<Point> getValues() {

FILE: src/main/java/info/blockchain/api/statistics/Point.java
  class Point (line 5) | public class Point {
    method Point (line 10) | public Point (JsonObject pointJson) {
    method Point (line 17) | public Point (float x, float y) {
    method getX (line 22) | public float getX() {
    method getY (line 26) | public float getY() {

FILE: src/main/java/info/blockchain/api/statistics/Statistics.java
  class Statistics (line 19) | public class Statistics {
    method Statistics (line 22) | public Statistics () {
    method Statistics (line 26) | public Statistics (String apiCode) {
    method getStats (line 36) | public StatisticsResponse getStats () throws APIException, IOException {
    method getChart (line 56) | public Chart getChart(String type, String timeSpan, String rollingAver...
    method getPools (line 82) | public Map<String, Integer> getPools(String timeSpan) throws APIExcept...

FILE: src/main/java/info/blockchain/api/statistics/StatisticsResponse.java
  class StatisticsResponse (line 11) | public class StatisticsResponse {
    method StatisticsResponse (line 33) | public StatisticsResponse (String jsonString) {
    method getTradeVolumeBTC (line 61) | public BigDecimal getTradeVolumeBTC () {
    method getTradeVolumeUSD (line 68) | public BigDecimal getTradeVolumeUSD () {
    method getMinersRevenueBTC (line 75) | public BigDecimal getMinersRevenueBTC () {
    method getMinersRevenueUSD (line 82) | public BigDecimal getMinersRevenueUSD () {
    method getMarketPriceUSD (line 89) | public BigDecimal getMarketPriceUSD () {
    method getEstimatedTransactionVolumeUSD (line 96) | public BigDecimal getEstimatedTransactionVolumeUSD () {
    method getTotalFeesBTC (line 103) | public long getTotalFeesBTC () {
    method getTotalBTCSent (line 110) | public long getTotalBTCSent () {
    method getEstimatedBTCSent (line 117) | public long getEstimatedBTCSent () {
    method getBTCMined (line 124) | public long getBTCMined () {
    method getDifficulty (line 131) | public double getDifficulty () {
    method getMinutesBetweenBlocks (line 138) | public double getMinutesBetweenBlocks () {
    method getNumberOfTransactions (line 145) | public long getNumberOfTransactions () {
    method getHashRate (line 152) | public double getHashRate () {
    method getTimestamp (line 159) | public long getTimestamp () {
    method getMinedBlocks (line 166) | public long getMinedBlocks () {
    method getBlocksSize (line 173) | public long getBlocksSize () {
    method getTotalBTC (line 180) | public long getTotalBTC () {
    method getTotalBlocks (line 187) | public long getTotalBlocks () {
    method getNextRetarget (line 194) | public long getNextRetarget () {

FILE: src/main/java/info/blockchain/api/wallet/Wallet.java
  class Wallet (line 27) | public class Wallet {
    method create (line 45) | public static CreateWalletResponse create (String serviceURL, String p...
    method create (line 63) | public static CreateWalletResponse create (String serviceURL, String p...
    method Wallet (line 94) | public Wallet (String serviceURL, String apiCode, String identifier, S...
    method Wallet (line 105) | public Wallet (String serviceURL, String apiCode, String identifier, S...
    method send (line 124) | public PaymentResponse send (String toAddress, long amount, String fro...
    method sendMany (line 141) | public PaymentResponse sendMany (Map<String, Long> recipients, String ...
    method getBalance (line 175) | public long getBalance () throws APIException, IOException {
    method listAddresses (line 188) | public List<Address> listAddresses () throws APIException, IOException {
    method getAddress (line 212) | public Address getAddress (String address) throws APIException, IOExce...
    method newAddress (line 229) | public Address newAddress (String label) throws APIException, IOExcept...
    method archiveAddress (line 248) | public String archiveAddress (String address) throws APIException, IOE...
    method unarchiveAddress (line 265) | public String unarchiveAddress (String address) throws APIException, I...
    method buildBasicRequest (line 275) | private Map<String, String> buildBasicRequest () {
    method buildBasicRequestWithSecondPassword (line 286) | private Map<String, String> buildBasicRequestWithSecondPassword () {
    method parseResponse (line 295) | private JsonObject parseResponse (String response) throws APIException {
    method equals (line 304) | @Override
    method hashCode (line 318) | @Override

FILE: src/main/java/info/blockchain/api/wallet/entity/Address.java
  class Address (line 6) | public class Address {
    method Address (line 12) | public Address (long balance, String address, String label, long total...
    method equals (line 19) | @Override
    method hashCode (line 43) | @Override
    method getBalance (line 55) | public long getBalance () {
    method getAddress (line 62) | public String getAddress () {
    method getLabel (line 69) | public String getLabel () {
    method getTotalReceived (line 76) | public long getTotalReceived () {

FILE: src/main/java/info/blockchain/api/wallet/entity/CreateWalletResponse.java
  class CreateWalletResponse (line 6) | public class CreateWalletResponse {
    method CreateWalletResponse (line 11) | public CreateWalletResponse (String identifier, String address, String...
    method getIdentifier (line 20) | public String getIdentifier () {
    method getAddress (line 27) | public String getAddress () {
    method getLable (line 34) | public String getLable () {

FILE: src/main/java/info/blockchain/api/wallet/entity/PaymentResponse.java
  class PaymentResponse (line 6) | public class PaymentResponse {
    method PaymentResponse (line 11) | public PaymentResponse (String message, String txHash, String notice) {
    method equals (line 17) | @Override
    method getMessage (line 32) | public String getMessage () {
    method getTxHash (line 39) | public String getTxHash () {
    method getNotice (line 46) | public String getNotice () {

FILE: src/test/java/info/blockchain/api/AppTest.java
  class AppTest (line 10) | public class AppTest extends TestCase {
    method AppTest (line 16) | public AppTest (String testName) {
    method suite (line 23) | public static Test suite () {
    method testApp (line 30) | public void testApp () {

FILE: src/test/java/info/blockchain/api/blockexplorer/BlockExplorerTest.java
  class BlockExplorerTest (line 17) | public class BlockExplorerTest {
    method setUp (line 21) | @Before
    method getAddress (line 26) | @Test
    method getUnspentOutputs (line 40) | @Test
    method getBalance (line 51) | @Test
    method getXpub (line 68) | @Test
Condensed preview — 48 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (149K chars).
[
  {
    "path": ".gitignore",
    "chars": 1043,
    "preview": "*.class\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.jar\n*.war\n*.ear\n\n# virtual machine crash logs, se"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 1448,
    "preview": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changel"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1077,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Blockchain\n\nPermission is hereby granted, free of charge, to any person obtain"
  },
  {
    "path": "README.md",
    "chars": 3745,
    "preview": "# Blockchain API library (Java, v2.0.0)\n\nAn official Java library for interacting with the Blockchain.info API (Java 1.6"
  },
  {
    "path": "docs/blockexplorer.md",
    "chars": 3977,
    "preview": "## `info.blockchain.api.blockexplorer` package\n\nThe `blockexplorer` package contains the `BlockExplorer` class that refl"
  },
  {
    "path": "docs/exchangerates.md",
    "chars": 984,
    "preview": "## `info.blockchain.api.exchangerates` package\n\nThe `exchangerates` package contains the `ExchangeRates` class that refl"
  },
  {
    "path": "docs/pushtx.md",
    "chars": 893,
    "preview": "## `info.blockchain.api.pushtx` package\n\nThe `pushtx` package contains the `PushTx` class that reflects the functionalit"
  },
  {
    "path": "docs/receive.md",
    "chars": 1209,
    "preview": "## `info.blockchain.api.receive` package\n\nThe `receive` package contains the `Receive` class that reflects the functiona"
  },
  {
    "path": "docs/statistics.md",
    "chars": 1063,
    "preview": "## `info.blockchain.api.statistics` package\n\nThe `statistics` package contains the `Statistics` class that reflects the "
  },
  {
    "path": "docs/wallet.md",
    "chars": 2220,
    "preview": "## `info.blockchain.api.wallet` package\n\nThe `wallet` package contains the `Wallet` class that reflects the functionalit"
  },
  {
    "path": "pom.xml",
    "chars": 3764,
    "preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:sc"
  },
  {
    "path": "src/main/java/info/blockchain/api/APIException.java",
    "chars": 448,
    "preview": "package info.blockchain.api;\n\n/**\n * The class `APIException` represents a failed call to the Blockchain API. Whenever\n "
  },
  {
    "path": "src/main/java/info/blockchain/api/HttpClient.java",
    "chars": 5958,
    "preview": "package info.blockchain.api;\n\nimport java.io.*;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.net."
  },
  {
    "path": "src/main/java/info/blockchain/api/HttpClientInterface.java",
    "chars": 748,
    "preview": "package info.blockchain.api;\n\nimport java.io.IOException;\nimport java.util.Map;\n\n/**\n * This is a utility class for perf"
  },
  {
    "path": "src/main/java/info/blockchain/api/OkClient.java",
    "chars": 6589,
    "preview": "package info.blockchain.api;\n\nimport com.squareup.okhttp.*;\n\nimport java.io.IOException;\nimport java.net.MalformedURLExc"
  },
  {
    "path": "src/main/java/info/blockchain/api/Util.java",
    "chars": 108,
    "preview": "package info.blockchain.api;\n\npublic class Util {\n    public static final int SATOSHI_IN_BTC = 100000000;\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/BlockExplorer.java",
    "chars": 14142,
    "preview": "package info.blockchain.api.blockexplorer;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimpor"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Address.java",
    "chars": 3704,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/AddressSummary.java",
    "chars": 2873,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\npublic class AddressSummary {\n\n  "
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Balance.java",
    "chars": 1214,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\npublic class Balance {\n\n    priva"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Block.java",
    "chars": 4898,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/FilterType.java",
    "chars": 326,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\npublic enum FilterType {\n    All(4),\n    ConfirmedOnly(5),\n    Remove"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Input.java",
    "chars": 2193,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Represents a transaction i"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/LatestBlock.java",
    "chars": 1947,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/MultiAddress.java",
    "chars": 1897,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/MultiAddressBalance.java",
    "chars": 1941,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\npublic class MultiAddressBalance "
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Output.java",
    "chars": 2768,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Represents a transaction o"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/SimpleBlock.java",
    "chars": 2009,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Simple representation of a"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Transaction.java",
    "chars": 4690,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/UnspentOutput.java",
    "chars": 2731,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Represents an unspent tran"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/XpubFull.java",
    "chars": 3990,
    "preview": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject"
  },
  {
    "path": "src/main/java/info/blockchain/api/etc/Base58.java",
    "chars": 8528,
    "preview": "package info.blockchain.api.etc;\n\n/**\n * Copyright 2011 Google Inc.\n * <p>\n * Licensed under the Apache License, Version"
  },
  {
    "path": "src/main/java/info/blockchain/api/exchangerates/Currency.java",
    "chars": 1236,
    "preview": "package info.blockchain.api.exchangerates;\n\nimport java.math.BigDecimal;\n\n/**\n * Used in response to the `getTicker` met"
  },
  {
    "path": "src/main/java/info/blockchain/api/exchangerates/ExchangeRates.java",
    "chars": 3508,
    "preview": "package info.blockchain.api.exchangerates;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimpor"
  },
  {
    "path": "src/main/java/info/blockchain/api/pushtx/PushTx.java",
    "chars": 1319,
    "preview": "package info.blockchain.api.pushtx;\n\nimport info.blockchain.api.APIException;\nimport info.blockchain.api.HttpClient;\n\nim"
  },
  {
    "path": "src/main/java/info/blockchain/api/receive/CallbackLog.java",
    "chars": 1631,
    "preview": "package info.blockchain.api.receive;\n\nimport com.google.gson.JsonObject;\n\npublic class CallbackLog {\n    private String "
  },
  {
    "path": "src/main/java/info/blockchain/api/receive/Receive.java",
    "chars": 3396,
    "preview": "package info.blockchain.api.receive;\n\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\nimport info."
  },
  {
    "path": "src/main/java/info/blockchain/api/receive/ReceiveResponse.java",
    "chars": 902,
    "preview": "package info.blockchain.api.receive;\n\n/**\n * This class is used as a response object to the `ReceiveV2.receive` method.\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/statistics/Chart.java",
    "chars": 1942,
    "preview": "package info.blockchain.api.statistics;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\nimport "
  },
  {
    "path": "src/main/java/info/blockchain/api/statistics/Point.java",
    "chars": 557,
    "preview": "package info.blockchain.api.statistics;\n\nimport com.google.gson.JsonObject;\n\npublic class Point {\n\n    private float x;\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/statistics/Statistics.java",
    "chars": 3305,
    "preview": "package info.blockchain.api.statistics;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonObject;\nimport com.goog"
  },
  {
    "path": "src/main/java/info/blockchain/api/statistics/StatisticsResponse.java",
    "chars": 5497,
    "preview": "package info.blockchain.api.statistics;\n\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\n\nimport j"
  },
  {
    "path": "src/main/java/info/blockchain/api/wallet/Wallet.java",
    "chars": 12931,
    "preview": "package info.blockchain.api.wallet;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\ni"
  },
  {
    "path": "src/main/java/info/blockchain/api/wallet/entity/Address.java",
    "chars": 1971,
    "preview": "package info.blockchain.api.wallet.entity;\n\n/**\n * Used in combination with the `Wallet` class\n */\npublic class Address "
  },
  {
    "path": "src/main/java/info/blockchain/api/wallet/entity/CreateWalletResponse.java",
    "chars": 816,
    "preview": "package info.blockchain.api.wallet.entity;\n\n/**\n * Used in response to the `create` method in the `CreateWallet` class.\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/wallet/entity/PaymentResponse.java",
    "chars": 1210,
    "preview": "package info.blockchain.api.wallet.entity;\n\n/**\n * Used in response to the `send` and `sendMany` methods in the `Wallet`"
  },
  {
    "path": "src/test/java/info/blockchain/api/AppTest.java",
    "chars": 625,
    "preview": "package info.blockchain.api;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestS"
  },
  {
    "path": "src/test/java/info/blockchain/api/blockexplorer/BlockExplorerTest.java",
    "chars": 3383,
    "preview": "package info.blockchain.api.blockexplorer;\n\nimport info.blockchain.api.blockexplorer.entity.*;\nimport org.junit.Assert;\n"
  }
]

About this extraction

This page contains the full source code of the blockchain/api-v1-client-java GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 48 files (136.1 KB), approximately 33.5k tokens, and a symbol index with 355 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!