[
  {
    "path": ".gitignore",
    "content": "*.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, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n\n# Maven\ntarget/\npom.xml.tag\npom.xml.releaseBackup\npom.xml.versionsBackup\npom.xml.next\nrelease.properties\ndependency-reduced-pom.xml\nbuildNumber.properties\n.mvn/timing.properties\n\n# Eclipse\n.metadata\nbin/\ntmp/\n*.tmp\n*.bak\n*.swp\n*~.nib\nlocal.properties\n.settings/\n.loadpath\n.recommenders\n\n# Eclipse Core\n.project\n\n# External tool builders\n.externalToolBuilders/\n\n# Locally stored \"Eclipse launch configurations\"\n*.launch\n\n# PyDev specific (Python IDE for Eclipse)\n*.pydevproject\n\n# CDT-specific (C/C++ Development Tooling)\n.cproject\n\n# JDT-specific (Eclipse Java Development Tools)\n.classpath\n\n# Java annotation processor (APT)\n.factorypath\n\n# PDT-specific (PHP Development Tools)\n.buildpath\n\n# sbteclipse plugin\n.target\n\n# Tern plugin\n.tern-project\n\n# TeXlipse plugin\n.texlipse\n\n# STS (Spring Tool Suite)\n.springBeans\n\n# Code Recommenders\n.recommenders/\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](http://keepachangelog.com/)\nand this project adheres to [Semantic Versioning](http://semver.org/).\n\n## [2.0.0] - 2017-06-16\n### Added\n- This CHANGELOG file to record all the changes in current and following version of this project.\n- Filter, limit, and offset optional parameters to getAddress in BlockExplorer.\n- Confirms and limit optional parameters to getUnspentOutputs in BlockExplorer.\n- A new getBalance method in BlockExplorer.\n- A new getMultiAddress method in BlockExplorer.\n- A new getXpub method in BlockExplorer.\n- A new toFiat method in ExchangeRates.\n- A new checkGap method in Receive.\n- A new getCallbackLog method in Receive.\n- A new getChart method in Statistics.\n- A new getPools method in Statistics.\n\n### Changed\n- ExchangeRate series interfaces to allow API be used for all methods.\n- Receive series interfaces to allow API be used for all methods.\n- Statistics series interfaces to allow API be used for all methods.\n- Moved createWallet functions from CreateWallet class to Wallet class.\n\n### Deprecated\n- “Get transaction and block by index” interfaces in BlockExplorer.\n\n### Removed\n- Method getInventoryData in BlockExplorer\n- Parameter “note” from send and sendMany methods in Wallet.\n- Parameter “confirmations” from listAddress and getAddress method in Wallet.\n- Method consolidate in Wallet.\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2014 Blockchain\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Blockchain API library (Java, v2.0.0)\n\nAn official Java library for interacting with the Blockchain.info API (Java 1.6 required).\n\n### Getting started\n\nIf you prefer building from source:\n\n```\n$ git clone https://github.com/blockchain/api-v1-client-java\n$ cd api-v1-client-java\n$ mvn install\n```\n\nWe also provide a snapshot Maven repository for users who prefer managing dependencies that way.\n\nAdd the following to your pom.xml:\n```xml\n<project>\n    ...\n    <repositories>\n        ...\n        <repository>\n            <id>api-v1-client-java-mvn-repo</id>\n            <url>https://raw.githubusercontent.com/blockchain/api-v1-client-java/mvn-repo/</url>\n            <snapshots>\n                <enabled>true</enabled>\n                <updatePolicy>always</updatePolicy>\n            </snapshots>\n        </repository>\n    </repositories>\n    ...\n    <dependencies>\n        ...\n        <dependency>\n  \t        <groupId>info.blockchain</groupId>\n  \t        <artifactId>api</artifactId>\n  \t        <version>LATEST</version> <!-- for a specific version see the list of tags -->\n        </dependency>\n    <dependencies>\n    ...\n</project>\n```\n\nThe above Maven repository also works with Gradle.\n\nAdd this to your `build.gradle`:\n```\n...\nrepositories {\n    ...\n    maven {\n        url = 'https://raw.githubusercontent.com/blockchain/api-v1-client-java/mvn-repo'\n    }\n}\n...\ndependencies {\n    ...\n    compile 'info.blockchain:api:2.0.2'\n}\n...\n```\n\nNote that the above procedures require Maven or Gradle. If you do not wish to use Maven or Gradle, please compile the source manually.\n\nThe library consists of the following packages:\n\n* `info.blockchain.api.blockexplorer` ([docs](docs/blockexplorer.md)) ([api/blockchain_api][api1])\n* `info.blockchain.api.createwallet` ([docs](docs/createwallet.md)) ([api/create_wallet][api2])\n* `info.blockchain.api.exchangerates` ([docs](docs/exchangerates.md)) ([api/exchange\\_rates\\_api][api3])\n* `info.blockchain.api.pushtx` ([docs](docs/pushtx.md)) ([pushtx][api7])\n* `info.blockchain.api.receive` ([docs](docs/receive.md)) ([api/api_receive][api4])\n* `info.blockchain.api.statistics` ([docs](docs/statistics.md)) ([api/charts_api][api5])\n* `info.blockchain.api.wallet` ([docs](docs/wallet.md)) ([api/blockchain\\_wallet\\_api][api6])\n\nIn 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).\n\n### Error handling\n\nAll 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.\n\n### Connection timeouts\n\nIt is possible to set arbitrary connection timeouts.\n\n```java\ninfo.blockchain.api.HttpClient.TIMEOUT_MS = 2000; // time out after 2000 milliseconds\n```\n\n### Request limits and API keys\n\nIn 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).\n\nThe same API key can be used to bypass the request limiter.\n\n### Code Coverage Report generation\n\nTo generate the code coverage report, execute the following command:\n```sh\nmvn clean verify\n```\n\nThis will generate a code coverage report in `target/site/jacoco/index.html`.\n\n\n[api1]: https://blockchain.info/api/blockchain_api\n[api2]: https://blockchain.info/api/create_wallet\n[api3]: https://blockchain.info/api/exchange_rates_api\n[api4]: https://blockchain.info/api/api_receive\n[api5]: https://blockchain.info/api/charts_api\n[api6]: https://blockchain.info/api/blockchain_wallet_api\n[api7]: https://blockchain.info/pushtx\n"
  },
  {
    "path": "docs/blockexplorer.md",
    "content": "## `info.blockchain.api.blockexplorer` package\n\nThe `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.\n\nExample usage:\n\n```java\n\npackage test;\nimport info.blockchain.api.blockexplorer.*;\n\npublic class App\n{\n    public static void main(String[] args) throws Exception\n    {\n    \t// instantiate a block explorer\n    \tBlockExplorer blockExplorer = new BlockExplorer();\n\n    \t// get a transaction by hash and list the value of all its inputs\n    \tTransaction tx = blockExplorer.getTransaction(\"df67414652722d38b43dcbcac6927c97626a65bd4e76a2e2787e22948a7c5c47\");\n    \tfor (Input i : tx.getInputs())\n    \t{\n    \t\tSystem.out.println(i.getPreviousOutput().getValue());\n    \t}\n\n    \t// get a block by hash and read the number of transactions in the block\n    \tBlock block = blockExplorer.getBlock(\"0000000000000000050fe18c9b961fc7c275f02630309226b15625276c714bf1\");\n    \tint numberOfTxsInBlock = block.getTransactions().size();\n\n    \t// get an address and read its final balance\n    \tAddress address = blockExplorer.getAddress(\"1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw\");\n    \tlong finalBalance = address.getFinalBalance();\n    \t\n    \t// get an address and read its final balance with filter, limit, and offset\n        Address address = client.getAddress(\"1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW\", FilterType.All, 10, 5);    \t\n        long finalBalance = address.getFinalBalance();\n\n\n    \t// get a list of currently unconfirmed transactions and print the relay IP address for each\n    \tList<Transaction> unconfirmedTxs = blockExplorer.getUnconfirmedTransactions();\n    \tfor (Transaction unconfTx : unconfirmedTxs)\n    \t\tSystem.out.println(tx.getRelayedBy());\n\n    \t// calculate the balanace of an address by fetching a list of all its unspent outputs\n    \tList<UnspentOutput> outs = blockExplorer.getUnspentOutputs(\"1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw\");\n    \tlong totalUnspentValue = 0;\n    \tfor (UnspentOutput out : outs)\n    \t\ttotalUnspentValue += out.getValue();\n\n    \t// calculate the balanace of an address by fetching a list of all its unspent outputs with confirmations and limit\n    \tList<UnspentOutput> outs = blockExplorer.getUnspentOutputs(Arrays.asList(\"1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw\"), 5, 10);\n    \tlong totalUnspentValue = 0;\n    \tfor (UnspentOutput out : outs)\n    \t\ttotalUnspentValue += out.getValue();\n    \t\t\n    \t// returns the address balance summary for each address provided\n    \tMap<String, Balance> balances = blockExplorer.getBalance(Arrays.asList(\"1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw\"), FilterType.All);\n    \t\n    \t// returns an aggregated summary on all addresses provided.\n    \tMultiAddress multiAddr = blockExplorer.getMultiAddress(Arrays.asList(\"1EjmmDULiZT2GCbJSeXRbjbJVvAPYkSDBw\"), FilterType.All, 10, 5);\n    \t\n    \t// returns xpub summary on a xpub provided, with its overall balance and its transactions.\n    \tXpubFull xpub = blockExplorer.getXpub(\"xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn\", FilterType.All, 10, 5);\n    \t\n    \t// get the latest block on the main chain and read its height\n    \tLatestBlock latestBlock = blockExplorer.getLatestBlock();\n    \tlong latestBlockHeight = latestBlock.getHeight();\n\n    \t// use the previous block height to get a list of blocks at that height\n\t\t// and detect a potential chain fork\n    \tList<Block> blocksAtHeight = blockExplorer.getBlocksAtHeight(latestBlockHeight);\n    \tif (blocksAtHeight.size() > 1)\n    \t\tSystem.out.println(\"The chain has forked!\");\n    \telse\n    \t\tSystem.out.println(\"The chain is still in one piece :)\");\n\n    \t// get a list of all blocks that were mined today since 00:00 UTC\n    \tList<SimpleBlock> todaysBlocks = blockExplorer.getBlocks();\n    \tSystem.out.println(todaysBlocks.size());\n    }\n}\n\n```"
  },
  {
    "path": "docs/exchangerates.md",
    "content": "## `info.blockchain.api.exchangerates` package\n\nThe `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.\n\nExample usage:\n\n```java\n\npackage test;\nimport info.blockchain.api.exchangerates.*;\n\npublic class App\n{\n    public static void main(String[] args) throws Exception\n    {\n        // get the Exchange object\n        ExchangeRates exchange = new ExchangeRates();\n        \n\t\t// get the ticker map\n        Map<String, Currency> ticker = exchange.getTicker();\n        BigDecimal BTCUSDsell = ticker.get(\"USD\").getSell();\n\n        // convert 5362 EUR to BTC\n        BigDecimal EURToBTC = exchange.toBTC(\"EUR\", new BigDecimal(53620));\n        \n        // convert 100,000,000 satoshi to USD\n        BigDecimal BTCToUSD = exchange.toFiat(\"USD\", new BigDecimal(100000000));\n    }\n}\n\n```"
  },
  {
    "path": "docs/pushtx.md",
    "content": "## `info.blockchain.api.pushtx` package\n\nThe `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.\n\nExample usage:\n\n```java\n\npackage test;\nimport info.blockchain.api.pushtx.*;\n\npublic class App\n{\n    public static void main(String[] args) throws Exception\n    {\n\t\tPushTx.pushTx( \"0100000001fd468e431cf5797b108e4d22724e1e055b3ecec59af4ef17b063afd36d3c5cf6010000008c4930460221009918eee8be186035be8ca573b7a4ef7bc672c59430785e5390cc375329a2099702210085b86387e3e15d68c847a1bdf786ed0fdbc87ab3b7c224f3c5490ac19ff4e756014104fe2cfcf0733e559cbf28d7b1489a673c0d7d6de8470d7ff3b272e7221afb051b777b5f879dd6a8908f459f950650319f0e83a5cf1d7c1dfadf6458f09a84ba80ffffffff01185d2033000000001976a9144be9a6a5f6fb75765145d9c54f1a4929e407d2ec88ac00000000\");\n    }\n}\n\n```\n"
  },
  {
    "path": "docs/receive.md",
    "content": "## `info.blockchain.api.receive` package\n\nThe `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.\n\nExample usage:\n\n```java\n\npackage test;\nimport info.blockchain.api.receive.*;\n\npublic class App\n{\n    public static void main(String[] args) throws Exception\n    {\n        Receive receive = new Receive(\"YOUR_API_CODE\");\n        \n    \tReceiveResponse response = receive.receive(\n    \t        \"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8\",\n    \t\t\tURLEncoder.encode(\"https://your.url.com?secret=foo\", \"UTF-8\"));\n\n    \tSystem.out.println(String.format(\"The receiving address is %s. \"\n    \t\t\t+ \"The address index is %d\",\n    \t\t\tresponse.getReceivingAddress(),\n    \t\t\tresponse.getIndex()));\n    \t\t\t\n    \tint xpubGap = receive.checkGap(\"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8\");\n    \tCallbackLog cbLog = receive.getCallbackLog(URLEncoder.encode(\"https://your.url.com?secret=foo\", \"UTF-8\"));\n    }\n}\n\n```"
  },
  {
    "path": "docs/statistics.md",
    "content": "## `info.blockchain.api.statistics` package\n\nThe `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.\n\nExample usage:\n\n```java\n\npackage test;\nimport info.blockchain.api.statistics.*;\n\npublic class App\n{\n    public static void main(String[] args) throws Exception\n    {\n        Statistics stats = new Statistics();\n        \n    \tStatisticsResponse stats = stats.get();\n    \tSystem.out.println(String.format(\"The current difficulty is %s. \"\n    \t\t\t+ \"The next retarget will happen in %s hours.\",\n    \t\t\tstats.getDifficulty(),\n    \t\t\t(stats.getNextRetarget() - stats.getTotalBlocks()) * stats.getMinutesBetweenBlocks() / 60));\n    \t\t\t\n        Chart txPerSec = stats.getChart(\"transactions-per-second\", \"5weeks\", \"8hours\");\n        \n        Map<String, Integer> pools = stats.getPools(\"5weeks\");\n    }\n}\n\n```"
  },
  {
    "path": "docs/wallet.md",
    "content": "## `info.blockchain.api.wallet` package\n\nThe `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.\n\nExample usage:\n\n```java\n\npackage test;\nimport info.blockchain.api.wallet.*;\n\npublic class App\n{\n    public static void main(String[] args) throws Exception\n    {\n    \n        CreateWalletResponse wallet = Wallet.create(\n        \t    \"http://localhost:3000/\",\n        \t    \"YOUR_SUPER_SECURE_PASSWORD\",\n        \t    \"YOUR_API_CODE\");\n    \n        System.out.println(wallet.getIdentifier());\n        \t\n    \tWallet wallet = new Wallet(\n    \t        \"http://localhost:3000/\",\n    \t        \"YOU_API_CODE\",\n    \t        \"YOUR_GUID\",\n    \t        \"YOUR_SUPER_SECURE_PASSWORD\");\n\n    \t// get an address from your wallet and include only transactions with up to 3\n    \t// confirmations in the balance\n    \tAddress addr = wallet.getAddress(\"1JzSZFs2DQke2B3S4pBxaNaMzzVZaG4Cqh\", 3);\n    \tSystem.out.println(String.format(\"The balance is %s\", addr.getBalance()));\n\n    \t// send 0.2 bitcoins with a custom fee of 0.01 BTC and a note\n        // public notes require a minimum transaction size of 0.005 BTC\n    \tPaymentResponse payment = wallet.send(\"1dice6YgEVBf88erBFra9BHf6ZMoyvG88\", 20000000L, null, 1000000L);\n    \tSystem.out.println(String.format(\"The payment TX hash is %s\", payment.getTxHash()));\n\n    \tlong totalBalance = wallet.getBalance();\n    \tSystem.out.println(String.format(\"The total wallet balance is %s\", totalBalance));\n\n    \t// list all addresses and their balances \n    \tList<Address> addresses = wallet.listAddresses();\n    \tfor (Address a : addresses)\n    \t{\n    \t\tSystem.out.println(String.format(\"The address %s has a balance of %s satoshi\",\n    \t\t\t\ta.getAddress(), a.getBalance()));\n    \t}\n\n    \t// archive an old address\n    \twallet.archiveAddress(\"1JzSZFs2DQke2B3S4pBxaNaMzzVZaG4Cqh\");\n\n    \t// create a new address and attach a label to it\n    \tAddress newAddr = wallet.newAddress(\"test label 123\");\n    \tSystem.out.println(String.format(\"The new address is %s\", newAddr.getAddress()));\n    }\n}\n\n```"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n  <modelVersion>4.0.0</modelVersion>\n\n  <groupId>info.blockchain</groupId>\n  <artifactId>api</artifactId>\n  <version>2.0.2</version>\n  <packaging>jar</packaging>\n\n  <name>api</name>\n  <url>https://blockchain.info/api</url>\n\n  <properties>\n    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n    <github.global.server>github</github.global.server>\n    <maven.compiler.source>1.6</maven.compiler.source>\n    <maven.compiler.target>1.6</maven.compiler.target>\n  </properties>\n\n  <distributionManagement>\n    <repository>\n      <id>internal.repo</id>\n      <name>Temporary Staging Repository</name>\n      <url>file://${project.build.directory}/mvn-repo</url>\n    </repository>\n  </distributionManagement>\n\n  <dependencies>\n\n    <!-- JSON marshalling/unmarshalling -->\n    <dependency>\n      <groupId>com.google.code.gson</groupId>\n      <artifactId>gson</artifactId>\n      <version>2.3</version>\n    </dependency>\n\n    <dependency>\n      <groupId>com.squareup.okhttp</groupId>\n      <artifactId>okhttp</artifactId>\n      <version>2.6.0</version>\n    </dependency>\n\n    <dependency>\n      <groupId>org.apache.commons</groupId>\n      <artifactId>commons-lang3</artifactId>\n      <version>3.4</version>\n    </dependency>\n\n    <dependency>\n      <groupId>junit</groupId>\n      <artifactId>junit</artifactId>\n      <version>4.12</version>\n      <scope>test</scope>\n    </dependency>\n  </dependencies>\n\n  <build>\n    <plugins>\n      <plugin>\n        <artifactId>maven-deploy-plugin</artifactId>\n        <version>2.8.2</version>\n        <configuration>\n          <altDeploymentRepository>internal.repo::default::file://${project.build.directory}/mvn-repo</altDeploymentRepository>\n        </configuration>\n      </plugin>\n\n      <plugin>\n        <groupId>org.jacoco</groupId>\n        <artifactId>jacoco-maven-plugin</artifactId>\n        <version>0.7.5.201505241946</version>\n        <executions>\n          <execution>\n            <goals>\n              <goal>prepare-agent</goal>\n            </goals>\n          </execution>\n          <execution>\n            <id>report</id>\n            <phase>prepare-package</phase>\n            <goals>\n              <goal>report</goal>\n            </goals>\n          </execution>\n        </executions>\n      </plugin>\n\n      <plugin>\n        <groupId>com.github.github</groupId>\n        <artifactId>site-maven-plugin</artifactId>\n        <version>0.12</version>\n        <configuration>\n          <!-- git commit message -->\n          <message>Maven artifacts for ${project.version}</message>\n          <!-- disable webpage processing -->\n          <noJekyll>true</noJekyll>\n          <!-- matches distribution management repository url above -->\n          <outputDirectory>${project.build.directory}/mvn-repo</outputDirectory>\n          <!-- Keep older versions around -->\n          <merge>true</merge>\n          <!-- remote branch name -->\n          <branch>refs/heads/mvn-repo</branch>\n          <includes><include>**/*</include></includes>\n          <!-- github repo name -->\n          <repositoryName>api-v1-client-java</repositoryName>\n          <!-- github username -->\n          <repositoryOwner>blockchain</repositoryOwner>\n        </configuration>\n        <executions>\n          <!-- run site-maven-plugin's 'site' target as part of the build's normal 'deploy' phase -->\n          <execution>\n            <goals>\n              <goal>site</goal>\n            </goals>\n            <phase>deploy</phase>\n          </execution>\n        </executions>\n      </plugin>\n    </plugins>\n  </build>\n\n</project>\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/APIException.java",
    "content": "package info.blockchain.api;\n\n/**\n * The class `APIException` represents a failed call to the Blockchain API. Whenever\n * the server is unable to process a request (usually due to parameter validation errors),\n * an instance of this class is thrown.\n */\npublic class APIException extends Exception {\n    private static final long serialVersionUID = -7731961787745059713L;\n\n    public APIException (String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/HttpClient.java",
    "content": "package info.blockchain.api;\n\nimport java.io.*;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.net.URLEncoder;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\n/**\n * This is a utility class for performing API calls using GET and POST requests. It is\n * possible to override these calls by implementing HttpClientInterface. Add the new\n * implementation via setCustomHttpClient(...), such that it will get used globally.\n */\npublic class HttpClient implements HttpClientInterface {\n    private static final String BASE_URL = \"https://blockchain.info/\";\n\n    public volatile static int TIMEOUT_MS = 10000;\n\n    private static HttpClientInterface instance;\n\n    public synchronized static HttpClientInterface getInstance () {\n        if (instance == null) {\n            // Thread Safe. Might be costly operation in some case\n            synchronized (HttpClient.class) {\n                if (instance == null) {\n                    instance = new HttpClient();\n                }\n            }\n        }\n        return instance;\n    }\n\n    public static void setCustomHttpClient (HttpClientInterface httpClient) {\n        instance = httpClient;\n    }\n\n    /**\n     * Perform a GET request on a Blockchain.info API resource.\n     *\n     * @param resource Resource path after https://blockchain.info/api/\n     * @param params   Map containing request parameters\n     * @return String response\n     * @throws APIException If the server returns an error\n     */\n    public String get (String resource, Map<String, String> params) throws APIException, IOException {\n        return openURL(BASE_URL, resource, params, \"GET\");\n    }\n\n    public String get (String baseURL, String resource, Map<String, String> params) throws APIException, IOException {\n        return openURL(baseURL, resource, params, \"GET\");\n    }\n\n    /**\n     * Perform a POST request on a Blockchain.info API resource.\n     *\n     * @param resource Resource path after https://blockchain.info/api/\n     * @param params   Map containing request parameters\n     * @return String response\n     * @throws APIException If the server returns an error\n     * @throws IOException  If the server is not reachable\n     */\n    public String post (String resource, Map<String, String> params) throws APIException, IOException {\n        return openURL(BASE_URL, resource, params, \"POST\");\n    }\n\n    public String post (String baseURL, String resource, Map<String, String> params) throws APIException, IOException {\n        return openURL(baseURL, resource, params, \"POST\");\n    }\n\n    private static String openURL (String baseURL, String resource, Map<String, String> params, String requestMethod) throws APIException, IOException {\n        String encodedParams = urlEncodeParams(params);\n        URL url = null;\n        APIException apiException = null;\n        IOException ioException = null;\n\n        String responseStr = null;\n\n        if (requestMethod.equals(\"GET\")) {\n            if (encodedParams.isEmpty()) {\n                url = new URL(baseURL + resource);\n            } else {\n                url = new URL(baseURL + resource + '?' + encodedParams);\n            }            \n        } else if (requestMethod.equals(\"POST\")) {\n            url = new URL(baseURL + resource);\n        }\n\n        HttpURLConnection conn = null;\n\n        try {\n            conn = (HttpURLConnection) url.openConnection();\n            conn.setRequestMethod(requestMethod);\n            conn.setConnectTimeout(TIMEOUT_MS);\n\n            if (requestMethod.equals(\"POST\")) {\n                byte[] postBytes = encodedParams.getBytes(\"UTF-8\");\n                conn.setDoOutput(true);\n                conn.setRequestProperty(\"Content-Type\", \"application/x-www-form-urlencoded\");\n                conn.setRequestProperty(\"Content-Length\", String.valueOf(postBytes.length));\n                conn.getOutputStream().write(postBytes);\n                conn.getOutputStream().close();\n            }\n\n            if (conn.getResponseCode() != 200) {\n                apiException = new APIException(inputStreamToString(conn.getErrorStream()));\n            } else {\n                responseStr = inputStreamToString(conn.getInputStream());\n            }\n        } catch (IOException e) {\n            ioException = e;\n        } finally {\n            try {\n                if (apiException != null) {\n                    conn.getErrorStream().close();\n                }\n                conn.getInputStream().close();\n            } catch (Exception ex) {\n            }\n\n            if (ioException != null) {\n                throw ioException;\n            }\n\n            if (apiException != null) {\n                throw apiException;\n            }\n        }\n\n        return responseStr;\n    }\n\n    private static String urlEncodeParams (Map<String, String> params) {\n        String result = \"\";\n\n        if (params != null && params.size() > 0) {\n            try {\n                StringBuilder data = new StringBuilder();\n                for (Entry<String, String> kvp : params.entrySet()) {\n                    if (data.length() > 0) {\n                        data.append('&');\n                    }\n\n                    data.append(URLEncoder.encode(kvp.getKey(), \"UTF-8\"));\n                    data.append('=');\n                    data.append(URLEncoder.encode(kvp.getValue(), \"UTF-8\"));\n                }\n                result = data.toString();\n            } catch (UnsupportedEncodingException e) {\n            }\n        }\n\n        return result;\n    }\n\n    private static String inputStreamToString (InputStream is) throws IOException {\n        BufferedReader reader = new BufferedReader(new InputStreamReader(is));\n\n        StringBuilder responseStringBuilder = new StringBuilder();\n        String line = \"\";\n\n        while ((line = reader.readLine()) != null) {\n            responseStringBuilder.append(line);\n        }\n\n        reader.close();\n\n        return responseStringBuilder.toString();\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/HttpClientInterface.java",
    "content": "package info.blockchain.api;\n\nimport java.io.IOException;\nimport java.util.Map;\n\n/**\n * This is a utility class for performing API calls using GET and POST requests. It is\n * possible to override these calls with your own implementation by overriding the 'get'\n * and 'post' methods.\n */\npublic interface HttpClientInterface {\n\n    String get (String resource, Map<String, String> params) throws APIException, IOException;\n\n    String get (String baseURL, String resource, Map<String, String> params) throws APIException, IOException;\n\n    String post (String resource, Map<String, String> params) throws APIException, IOException;\n\n    String post (String baseURL, String resource, Map<String, String> params) throws APIException, IOException;\n\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/OkClient.java",
    "content": "package info.blockchain.api;\n\nimport com.squareup.okhttp.*;\n\nimport java.io.IOException;\nimport java.net.MalformedURLException;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * This class implements the {@link HttpClientInterface} using OkHttp.\n * OkHttp handles Gzip, connection pooling, caching and retries out of the box.\n * The library also provides a set of fluent builders for creating requests and responses.\n * For more information about OkHttp visit http://square.github.io/okhttp/\n */\npublic class OkClient implements HttpClientInterface {\n\n    public static final String URL_SCHEME = \"https\";\n    public static final String URL_HOST = \"blockchain.info\";\n    public static final int TIMEOUT_MS = 10000;\n\n    private static OkHttpClient okHttpClientInstance = null;\n    private static OkClient okClientInstance = null;\n\n    /**\n     * Private constructor to prevent instantiation.\n     */\n    private OkClient() {\n        //prevent instantiation through reflection.\n        if (okClientInstance != null) {\n            throw new IllegalStateException(\"Already initialized.\");\n        }\n    }\n\n    /**\n     * OkClient singleton\n     */\n    public synchronized static OkClient getOkClientInstance() {\n        if (okClientInstance == null) {\n            okClientInstance = new OkClient();\n        }\n        return okClientInstance;\n    }\n\n    /**\n     * OkHttpClient singleton\n     */\n    private synchronized static OkHttpClient getOkHttpClientInstance() {\n        if (okHttpClientInstance == null) {\n            okHttpClientInstance = new OkHttpClient();\n            okHttpClientInstance.setConnectTimeout(TIMEOUT_MS, TimeUnit.MILLISECONDS);\n        }\n        return okHttpClientInstance;\n    }\n\n    /**\n     * This method makes a get request to a specific API resource.\n     * Request is built using the OkHttp {@link HttpUrl} builder.\n     *\n     * @param baseURL  The API base URL\n     * @param resource The API resource being requested.\n     * @param params   A set of params sent to the resource\n     * @return Returns a response as a {@link String}\n     * @throws IOException Thrown if the request was unsuccessful.\n     */\n    @Override\n    public String get(String baseURL, String resource, Map<String, String> params) throws IOException {\n        HttpUrl.Builder url = getHttpUrlBuilder(baseURL, resource);\n\n        for (String paramName : params.keySet()) {\n            url.addEncodedQueryParameter(paramName, params.get(paramName));\n        }\n\n        return getInternal(url.build());\n    }\n\n    /**\n     * This method makes a get request to a specific API resource.\n     * Request is built using the OkHttp {@link HttpUrl} builder.\n     *\n     * @param resource The API resource being requested.\n     * @param params   A set of params sent to the resource\n     * @return Returns a response as a {@link String}\n     * @throws IOException Thrown if the request was unsuccessful.\n     */\n    @Override\n    public String get(String resource, Map<String, String> params) throws IOException {\n        HttpUrl.Builder url = getHttpUrlBuilder(resource);\n\n        for (String paramName : params.keySet()) {\n            url.addEncodedQueryParameter(paramName, params.get(paramName));\n        }\n\n        return getInternal(url.build());\n    }\n\n    private String getInternal(HttpUrl url) throws IOException {\n        Request request = new Request.Builder()\n                .url(url)\n                .build();\n\n        return makeRequest(request);\n    }\n\n    /**\n     * This method makes a post request to a specific API resource.\n     * Request is built using the OkHttp {@link HttpUrl} builder.\n     * Request body is built using the OkHttp {@link FormEncodingBuilder} builder .\n     *\n     * @param baseURL  The API base URL\n     * @param resource The API resource being requested.\n     * @param params   A set of params sent to the resource\n     * @return Returns a response as a {@link String}\n     * @throws IOException Thrown if the request was unsuccessful.\n     */\n    @Override\n    public String post(String baseURL, String resource, Map<String, String> params) throws IOException {\n          HttpUrl.Builder url = getHttpUrlBuilder(baseURL, resource);\n\n          return postInternal(url.build(), params);\n    }\n\n    /**\n     * This method makes a post request to a specific API resource.\n     * Request is built using the OkHttp {@link HttpUrl} builder.\n     * Request body is built using the OkHttp {@link FormEncodingBuilder} builder .\n     *\n     * @param resource The API resource being requested.\n     * @param params   A set of params sent to the resource\n     * @return Returns a response as a {@link String}\n     * @throws IOException Thrown if the request was unsuccessful.\n     */\n    @Override\n    public String post(String resource, Map<String, String> params) throws IOException {\n        HttpUrl.Builder url = getHttpUrlBuilder(resource);\n\n        return postInternal(url.build(), params);\n    }\n\n    private String postInternal(HttpUrl url, Map<String, String> params) throws IOException {\n        FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder();\n        for (String paramName : params.keySet()) {\n            formEncodingBuilder.addEncoded(paramName, params.get(paramName));\n        }\n\n        Request request = new Request.Builder()\n                .url(url)\n                .post(formEncodingBuilder.build())\n                .build();\n\n        return makeRequest(request);\n  }\n\n    private String makeRequest(Request request) throws IOException {\n      Response response = getOkHttpClientInstance().newCall(request).execute();\n      if (isNotSuccessfulResponse(response))\n          throw new IOException(String.format(\"Unsuccessful call to %s Response: %s\",\n                  request.urlString(),\n                  response));\n\n      return response.body().string();\n    }\n\n    private HttpUrl.Builder getHttpUrlBuilder(String resource) {\n        HttpUrl.Builder url = new HttpUrl.Builder();\n        url.scheme(URL_SCHEME)\n                .host(URL_HOST)\n                .addPathSegment(resource);\n\n        return url;\n    }\n\n    private HttpUrl.Builder getHttpUrlBuilder(String baseURL, String resource) throws MalformedURLException {\n        HttpUrl url = HttpUrl.parse(baseURL);\n        if (url == null) {\n          throw new MalformedURLException();\n        }\n\n        HttpUrl.Builder urlBuilder = url.newBuilder();\n        urlBuilder.addPathSegment(resource);\n\n        return urlBuilder;\n    }\n\n    private boolean isNotSuccessfulResponse(Response response) {\n        return !response.isSuccessful();\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/Util.java",
    "content": "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",
    "content": "package info.blockchain.api.blockexplorer;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\nimport info.blockchain.api.APIException;\nimport info.blockchain.api.HttpClient;\nimport info.blockchain.api.blockexplorer.entity.*;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.io.IOException;\nimport java.util.*;\n\n/**\n * The BlockExplorer class reflects the functionality documented at\n * https://blockchain.info/api/blockchain_api. It can be used to query the block chain,\n * fetch block, transaction and address data, get unspent outputs for an address etc.\n */\npublic class BlockExplorer {\n    private final String apiCode;\n\n    public BlockExplorer () {\n        this(null);\n    }\n\n    /**\n     * @param apiCode Blockchain.info API code (optional, nullable)\n     */\n    public BlockExplorer (String apiCode) {\n        this.apiCode = apiCode;\n    }\n\n    /**\n     * Gets a single transaction based on a transaction index.\n     *\n     * @param txIndex Transaction index\n     * @return An instance of the {@link Transaction} class\n     * @throws APIException If the server returns an error\n     * @deprecated As of 1.1.5, will be removed in future releases\n     */\n    @Deprecated\n    public Transaction getTransaction (long txIndex) throws APIException, IOException {\n        return getTransaction(String.valueOf(txIndex));\n    }\n\n    /**\n     * Gets a single transaction based on a transaction hash.\n     *\n     * @param txHash Transaction hash\n     * @return An instance of the {@link Transaction} class\n     * @throws APIException If the server returns an error\n     */\n    public Transaction getTransaction (String txHash) throws APIException, IOException {\n        String response = HttpClient.getInstance().get(\"rawtx/\" + txHash, buildBasicRequest());\n        JsonObject txJson = new JsonParser().parse(response).getAsJsonObject();\n        return new Transaction(txJson);\n    }\n\n    /**\n     * Gets a single block based on a block index.\n     *\n     * @param blockIndex Block index\n     * @return An instance of the {@link Block} class\n     * @throws APIException If the server returns an error\n     * @deprecated As of 1.1.5, will be removed in future releases\n     */\n    @Deprecated\n    public Block getBlock (long blockIndex) throws APIException, IOException {\n        return getBlock(String.valueOf(blockIndex));\n    }\n\n    /**\n     * Gets a single block based on a block hash.\n     *\n     * @param blockHash Block hash\n     * @return An instance of the {@link Block} class\n     * @throws APIException If the server returns an error\n     */\n    public Block getBlock (String blockHash) throws APIException, IOException {\n        String response = HttpClient.getInstance().get(\"rawblock/\" + blockHash, buildBasicRequest());\n        JsonObject blockJson = new JsonParser().parse(response).getAsJsonObject();\n        return new Block(blockJson);\n    }\n\n    /**\n     * Gets data for a single address.\n     *\n     * @param address Base58check or hash160 address string\n     * @param filter the filter for transactions selection, use null to indicate default\n     * @param limit an integer to limit number of transactions to display, use null to indicate default\n     * @param offset an integer to set number of transactions to skip when fetch, use null to indicate default\n     * @return An instance of the {@link Address} class\n     * @throws APIException If the server returns an error\n     */\n    public Address getAddress (String address, FilterType filter, Integer limit, Integer offset) throws APIException, IOException {\n        Map<String, String> params = buildBasicRequest();\n        if (filter != null) {\n            params.put(\"filter\", filter.getFilterInt().toString());\n        }\n        if (limit != null) {\n            params.put(\"limit\", limit.toString());\n        }\n        if (offset != null) {\n            params.put(\"offset\", offset.toString());\n        }\n        String response = HttpClient.getInstance().get(\"rawaddr/\" + address, params);\n        JsonObject addrJson = new JsonParser().parse(response).getAsJsonObject();\n\n        return new Address(addrJson);\n    }\n\n    /**\n     * Gets data for a single address.\n     *\n     * @param address Base58check or hash160 address string\n     * @return An instance of the {@link Address} class\n     * @throws APIException If the server returns an error\n     */\n    public Address getAddress (String address) throws APIException, IOException {\n        return getAddress(address, null, null, null);\n    }\n\n    /**\n     * Gets a list of blocks at the specified height. Normally, only one block will be\n     * returned, but in case of a chain fork, multiple blocks may be present.\n     *\n     * @param height Block height\n     * @return A list of blocks at the specified height\n     * @throws APIException If the server returns an error\n     */\n    public List<Block> getBlocksAtHeight (long height) throws APIException, IOException {\n        List<Block> blocks = new ArrayList<Block>();\n\n        String response = HttpClient.getInstance().get(\"block-height/\" + height, buildBasicRequest());\n        JsonObject blocksJson = new JsonParser().parse(response).getAsJsonObject();\n\n        for (JsonElement blockElem : blocksJson.get(\"blocks\").getAsJsonArray()) {\n            blocks.add(new Block(blockElem.getAsJsonObject()));\n        }\n\n        return blocks;\n    }\n\n    /**\n     * Returns list of unspent outputs.\n     *\n     * @param addressList a list of Base58 or xpub addresses\n     * @param confirms an integer for minimum confirms of the outputs, use null to indicate default\n     * @param limit an integer to limit number of transactions to display, use null to indicate default\n     * @return A list of unspent outputs for the specified address\n     * @throws APIException If the server returns an error\n     */\n    public List<UnspentOutput> getUnspentOutputs (List<String> addressList, Integer confirms, Integer limit) throws APIException, IOException {\n        List<UnspentOutput> outputs = new ArrayList<UnspentOutput>();\n\n        Map<String, String> params = buildBasicRequest();\n        String pipedAddresses = StringUtils.join(addressList, \"|\");\n        params.put(\"active\", pipedAddresses);\n        if (confirms != null) {\n            params.put(\"confirmations\", confirms.toString());\n        }\n        if (limit != null) {\n            params.put(\"limit\", limit.toString());\n        }\n\n        String response = null;\n        try {\n            response = HttpClient.getInstance().get(\"unspent\", params);\n        } catch (APIException e) {\n            // The server endpoint will return error if no output, however this should be a valid situation so we return an empty result.\n            if (e.getMessage().equals(\"No free outputs to spend\")) {\n                return outputs;\n            }\n        }\n\n        JsonObject outsJson = new JsonParser().parse(response).getAsJsonObject();\n        for (JsonElement outElem : outsJson.get(\"unspent_outputs\").getAsJsonArray()) {\n            outputs.add(new UnspentOutput(outElem.getAsJsonObject()));\n        }\n\n        return outputs;\n    }\n\n    /**\n     * Gets unspent outputs for a single address.\n     *\n     * @param address Base58check or hash160 address string\n     * @return A list of unspent outputs for the specified address\n     * @throws APIException If the server returns an error\n     */\n    public List<UnspentOutput> getUnspentOutputs (String address) throws APIException, IOException {\n        return getUnspentOutputs(Arrays.asList(address), null, null);\n    }\n\n    /**\n     * Gets the latest block on the main chain (simplified representation).\n     *\n     * @return An instance of the {@link LatestBlock} class\n     * @throws APIException If the server returns an error\n     */\n    public LatestBlock getLatestBlock () throws APIException, IOException {\n        String response = HttpClient.getInstance().get(\"latestblock\", buildBasicRequest());\n        JsonObject blockObj = new JsonParser().parse(response).getAsJsonObject();\n        return new LatestBlock(blockObj);\n    }\n\n    /**\n     * Gets a list of currently unconfirmed transactions.\n     *\n     * @return A list of unconfirmed {@link Transaction} objects\n     * @throws APIException If the server returns an error\n     */\n    public List<Transaction> getUnconfirmedTransactions () throws APIException, IOException {\n        List<Transaction> transactions = new ArrayList<Transaction>();\n\n        String response = HttpClient.getInstance().get(\"unconfirmed-transactions\", buildBasicRequest());\n        JsonObject txList = new JsonParser().parse(response).getAsJsonObject();\n\n        for (JsonElement txElem : txList.get(\"txs\").getAsJsonArray()) {\n            JsonObject txObj = txElem.getAsJsonObject();\n            transactions.add(new Transaction(txObj, -1, txObj.get(\"double_spend\").getAsBoolean()));\n        }\n\n        return transactions;\n    }\n\n    /**\n     * Gets a list of blocks mined today by all pools since 00:00 UTC.\n     *\n     * @return A list of {@link SimpleBlock} objects\n     * @throws APIException APIException If the server returns an error\n     */\n    public List<SimpleBlock> getBlocks () throws APIException, IOException {\n        return getBlocks(null);\n    }\n\n    /**\n     * Gets a list of blocks mined on a specific day.\n     *\n     * @param timestamp Unix timestamp (without milliseconds) that falls between\n     *                  00:00 UTC and 23:59 UTC of the desired day.\n     * @return A list of {@link SimpleBlock} objects\n     */\n    public List<SimpleBlock> getBlocks (long timestamp) throws APIException, IOException {\n        return getBlocks(String.valueOf(timestamp * 1000));\n    }\n\n    /**\n     * Gets a list of recent blocks by a specific mining pool.\n     *\n     * @param poolName Name of the mining pool\n     * @return A list of {@link SimpleBlock} objects\n     * @throws APIException If the server returns an error\n     */\n    public List<SimpleBlock> getBlocks (String poolName) throws APIException, IOException {\n        List<SimpleBlock> blocks = new ArrayList<SimpleBlock>();\n        poolName = poolName == null ? \"\" : poolName;\n\n        String response = HttpClient.getInstance().get(\"blocks/\" + poolName, buildBasicRequest());\n        JsonObject blockList = new JsonParser().parse(response).getAsJsonObject();\n\n        for (JsonElement blockElem : blockList.get(\"blocks\").getAsJsonArray()) {\n            blocks.add(new SimpleBlock(blockElem.getAsJsonObject()));\n        }\n\n        return blocks;\n    }\n\n    /**\n     * Returns the address balance summary for each address provided\n     *\n     * @param addressList base58 or xpub addresses\n     * @param filter the filter for transactions selection, use null to indicate default\n     * @return a map of (address, {@link Balance})\n     */\n    public Map<String, Balance> getBalance(List<String> addressList, FilterType filter) throws APIException, IOException {\n        Map<String, String> params = buildBasicRequest();\n        String pipedAddresses = StringUtils.join(addressList, \"|\");\n        params.put(\"active\", pipedAddresses);\n        if (filter != null) {\n            params.put(\"filter\", filter.getFilterInt().toString());\n        }\n\n        String response = HttpClient.getInstance().get(\"balance\", params);\n        JsonObject balanceMap = new JsonParser().parse(response).getAsJsonObject();\n\n        Map<String, Balance> balances = new HashMap<String, Balance>();\n        for (String address : addressList) {\n            JsonObject balance = balanceMap.getAsJsonObject(address);\n            balances.put(address, new Balance(balance));\n        }\n\n        return balances;\n    }\n\n    /**\n     * Returns an aggregated summary on all addresses provided.\n     *\n     * @param addressList a list of Base58 or xpub addresses\n     * @param filter the filter for transactions selection, use null to indicate default\n     * @param limit an integer to limit number of transactions to display, use null to indicate default\n     * @param offset an integer to set number of transactions to skip when fetch, use null to indicate default\n     * @return An instance of the {@link MultiAddress} class\n     * @throws APIException If the server returns an error\n     */\n    public MultiAddress getMultiAddress(List<String> addressList, FilterType filter, Integer limit, Integer offset) throws APIException, IOException {\n        Map<String, String> params = buildBasicRequest();\n        String pipedAddresses = StringUtils.join(addressList, \"|\");\n        params.put(\"active\", pipedAddresses);\n        if (filter != null) {\n            params.put(\"filter\", filter.getFilterInt().toString());\n        }\n        if (limit != null) {\n            params.put(\"n\", limit.toString());\n        }\n        if (offset != null) {\n            params.put(\"offset\", offset.toString());\n        }\n\n        String response = HttpClient.getInstance().get(\"multiaddr\", params);\n        JsonObject addrJson = new JsonParser().parse(response).getAsJsonObject();\n\n        return new MultiAddress(addrJson);\n    }\n\n    /**\n     * Returns xpub summary on a xpub provided, with its overall balance and its transactions.\n     *\n     * @param xpub a xpub address\n     * @param filter the filter for transactions selection, use null to indicate default\n     * @param limit an integer to limit number of transactions to display, use null to indicate default\n     * @param offset an integer to set number of transactions to skip when fetch\n     * @return {@link XpubFull} an object to represent the xpub summary\n     */\n    public XpubFull getXpub(String xpub, FilterType filter, Integer limit, Integer offset) throws APIException, IOException {\n        MultiAddress multiAddress = getMultiAddress(Arrays.asList(xpub), filter, limit, offset);\n\n        return new XpubFull(multiAddress.getAddresses().get(0), multiAddress.getTxs());\n    }\n\n    private Map<String, String> buildBasicRequest () {\n        Map<String, String> params = new HashMap<String, String>();\n\n        params.put(\"format\", \"json\");\n        if (apiCode != null) {\n            params.put(\"api_code\", apiCode);\n        }\n\n        return params;\n  }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Address.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Representation of an address.\n */\npublic class Address {\n    private String hash160;\n    private String address;\n    private long totalReceived;\n    private long totalSent;\n    private long finalBalance;\n    private int txCount;\n    private List<Transaction> transactions;\n\n    public Address (String hash160, String address, long totalReceived, long totalSent, long finalBalance, int txCount, List<Transaction> transactions) {\n        this.hash160 = hash160;\n        this.address = address;\n        this.totalReceived = totalReceived;\n        this.totalSent = totalSent;\n        this.finalBalance = finalBalance;\n        this.txCount = txCount;\n        this.transactions = transactions;\n    }\n\n    public Address (JsonObject a) {\n\n        this(\n                a.has(\"hash160\") ? a.get(\"hash160\").getAsString() : \"\",\n                a.has(\"address\") ? a.get(\"address\").getAsString() : \"\",\n                a.has(\"total_received\") ? a.get(\"total_received\").getAsLong() : 0,\n                a.has(\"total_sent\") ? a.get(\"total_sent\").getAsLong() : 0,\n                a.has(\"final_balance\") ? a.get(\"final_balance\").getAsLong() : 0,\n                a.has(\"n_tx\") ? a.get(\"n_tx\").getAsInt() : 0,\n                null);\n\n        transactions = new ArrayList<Transaction>();\n        for (JsonElement txElem : a.get(\"txs\").getAsJsonArray()) {\n            JsonObject addrObj = txElem.getAsJsonObject();\n            transactions.add(new Transaction(addrObj));\n        }\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        Address address1 = (Address) o;\n\n        if (totalReceived != address1.totalReceived) {\n            return false;\n        }\n        if (totalSent != address1.totalSent) {\n            return false;\n        }\n        if (finalBalance != address1.finalBalance) {\n            return false;\n        }\n        if (!hash160.equals(address1.hash160)) {\n            return false;\n        }\n        return address.equals(address1.address);\n\n    }\n\n    @Override\n    public int hashCode () {\n        int result = hash160.hashCode();\n        result = 31 * result + address.hashCode();\n        result = 31 * result + (int) (totalReceived ^ (totalReceived >>> 32));\n        result = 31 * result + (int) (totalSent ^ (totalSent >>> 32));\n        result = 31 * result + (int) (finalBalance ^ (finalBalance >>> 32));\n        return result;\n    }\n\n    /**\n     * @return Hash160 representation of the address\n     */\n    public String getHash160 () {\n        return hash160;\n    }\n\n    /**\n     * @return Base58Check representation of the address\n     */\n    public String getAddress () {\n        return address;\n    }\n\n    /**\n     * @return Total amount received (in satoshi)\n     */\n    public long getTotalReceived () {\n        return totalReceived;\n    }\n\n    /**\n     * @return Total amount sent (in satoshi)\n     */\n    public long getTotalSent () {\n        return totalSent;\n    }\n\n    /**\n     * @return Final balance of the address (in satoshi)\n     */\n    public long getFinalBalance () {\n        return finalBalance;\n    }\n\n    /**\n     * @return Original number of transactions before filtering and limiting\n     */\n    public int getTxCount () {\n        return txCount;\n    }\n\n    /**\n     * @return List of transactions associated with this address\n     */\n    public List<Transaction> getTransactions () {\n        return transactions;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/AddressSummary.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\npublic class AddressSummary {\n\n    private String address;\n    private int txCount;\n    private long totalReceived;\n    private long totalSent;\n    private long finalBalance;\n    private int changeIndex;\n    private int accountIndex;\n    private int gapLimit;\n\n    public AddressSummary (String address, int txCount, long totalReceived, long totalSent, long finalBalance,\n                           int changeIndex, int accountIndex, int gapLimit) {\n        this.address = address;\n        this.txCount = txCount;\n        this.totalReceived = totalReceived;\n        this.totalSent = totalSent;\n        this.finalBalance = finalBalance;\n        this.changeIndex = changeIndex;\n        this.accountIndex = accountIndex;\n        this.gapLimit = gapLimit;\n    }\n\n    public AddressSummary (JsonObject json) {\n        this(\n                json.has(\"address\") ? json.get(\"address\").getAsString() : \"\",\n                json.has(\"n_tx\") ? json.get(\"n_tx\").getAsInt() : 0,\n                json.has(\"total_received\") ? json.get(\"total_received\").getAsLong() : 0,\n                json.has(\"total_sent\") ? json.get(\"total_sent\").getAsLong() : 0,\n                json.has(\"final_balance\") ? json.get(\"final_balance\").getAsLong() : 0,\n                json.has(\"change_index\") ? json.get(\"change_index\").getAsInt() : 0,\n                json.has(\"account_index\") ? json.get(\"account_index\").getAsInt() : 0,\n                json.has(\"gap_limit\") ? json.get(\"gap_limit\").getAsInt() : 0\n        );\n    }\n\n    public String getAddress() {\n        return address;\n    }\n\n    public int getTxCount() {\n        return txCount;\n    }\n\n    public void setTxCount(int txCount) {\n        this.txCount = txCount;\n    }\n\n    public long getTotalReceived() {\n        return totalReceived;\n    }\n\n    public long getTotalSent() {\n        return totalSent;\n    }\n\n    public long getFinalBalance() {\n        return finalBalance;\n    }\n\n    public int getChangeIndex() {\n        return changeIndex;\n    }\n\n    public int getAccountIndex() {\n        return accountIndex;\n    }\n\n    public void setFinalBalance(long final_balance) {\n        this.finalBalance = final_balance;\n    }\n\n    public void setAddress(String address) {\n        this.address = address;\n    }\n\n    public void setTotalReceived(long total_received) {\n        this.totalReceived = total_received;\n    }\n\n    public void setTotalSent(long total_sent) {\n        this.totalSent = total_sent;\n    }\n\n    public void setChangeIndex(int change_index) {\n        this.changeIndex = change_index;\n    }\n\n    public void setAccountIndex(int account_index) {\n        this.accountIndex = account_index;\n    }\n\n    public int getGapLimit () {\n        return gapLimit;\n    }\n\n    public void setGapLimit (int gapLimit) {\n        this.gapLimit = gapLimit;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Balance.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\npublic class Balance {\n\n    private long finalBalance;\n    private long txCount;\n    private long totalReceived;\n\n    public Balance (long finalBalance, long txCount, long totalReceived) {\n        this.finalBalance = finalBalance;\n        this.txCount = txCount;\n        this.totalReceived = totalReceived;\n    }\n\n    public Balance (JsonObject json) {\n\n        this(\n                json.has(\"final_balance\") ? json.get(\"final_balance\").getAsLong() : 0,\n                json.has(\"n_tx\") ? json.get(\"n_tx\").getAsLong() : 0,\n                json.has(\"total_received\") ? json.get(\"total_received\").getAsLong() : 0\n        );\n    }\n\n    public long getFinalBalance() {\n        return finalBalance;\n    }\n\n    public long getTxCount() {\n        return txCount;\n    }\n\n    public long getTotalReceived() {\n        return totalReceived;\n    }\n\n    public void setFinalBalance(long final_balance) {\n        this.finalBalance = final_balance;\n    }\n\n    public void setTxCount(long n_tx) {\n        this.txCount = n_tx;\n    }\n\n    public void setTotalReceived(long total_received) {\n        this.totalReceived = total_received;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Block.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * This class is a full representation of a block. For simpler representations, see\n * `SimpleBlock` and `LatestBlock`.\n */\npublic class Block extends SimpleBlock {\n    private int version;\n    private String previousBlockHash;\n    private String merkleRoot;\n    private long bits;\n    private long fees;\n    private long nonce;\n    private long size;\n    private long index;\n    private long receivedTime;\n    private String relayedBy;\n    private List<Transaction> transactions;\n\n    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) {\n        super(height, hash, time, mainChain);\n        this.version = version;\n        this.previousBlockHash = previousBlockHash;\n        this.merkleRoot = merkleRoot;\n        this.bits = bits;\n        this.fees = fees;\n        this.nonce = nonce;\n        this.size = size;\n        this.index = index;\n        this.receivedTime = receivedTime;\n        this.relayedBy = relayedBy;\n        this.transactions = transactions;\n    }\n\n    public Block (JsonObject b) {\n        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);\n\n        transactions = new ArrayList<Transaction>();\n        for (JsonElement txElem : b.get(\"tx\").getAsJsonArray()) {\n            transactions.add(new Transaction(txElem.getAsJsonObject(), super.getHeight(), false));\n        }\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n        if (!super.equals(o)) {\n            return false;\n        }\n\n        Block block = (Block) o;\n\n        if (version != block.version) {\n            return false;\n        }\n        if (fees != block.fees) {\n            return false;\n        }\n        if (nonce != block.nonce) {\n            return false;\n        }\n        if (size != block.size) {\n            return false;\n        }\n        if (index != block.index) {\n            return false;\n        }\n        if (!previousBlockHash.equals(block.previousBlockHash)) {\n            return false;\n        }\n        return merkleRoot.equals(block.merkleRoot);\n\n    }\n\n    @Override\n    public int hashCode () {\n        int result = version;\n        result = 31 * result + previousBlockHash.hashCode();\n        result = 31 * result + merkleRoot.hashCode();\n        result = 31 * result + (int) (fees ^ (fees >>> 32));\n        result = 31 * result + (int) (nonce ^ (nonce >>> 32));\n        result = 31 * result + (int) (size ^ (size >>> 32));\n        result = 31 * result + (int) (index ^ (index >>> 32));\n        return result;\n    }\n\n    /**\n     * @return Block version as specified by the protocol\n     */\n    public int getVersion () {\n        return version;\n    }\n\n    /**\n     * @return Hash of the previous block\n     */\n    public String getPreviousBlockHash () {\n        return previousBlockHash;\n    }\n\n    /**\n     * @return Merkle root of the block\n     */\n    public String getMerkleRoot () {\n        return merkleRoot;\n    }\n\n    /**\n     * @return Representation of the difficulty target for this block\n     */\n    public long getBits () {\n        return bits;\n    }\n\n    /**\n     * @return Total transaction fees from this block (in satoshi)\n     */\n    public long getFees () {\n        return fees;\n    }\n\n    /**\n     * @return Block nonce\n     */\n    public long getNonce () {\n        return nonce;\n    }\n\n    /**\n     * @return Serialized size of this block\n     */\n    public long getSize () {\n        return size;\n    }\n\n    /**\n     * @return Index of this block\n     */\n    public long getIndex () {\n        return index;\n    }\n\n    /**\n     * @return The time this block was received by Blockchain.info\n     */\n    public long getReceivedTime () {\n        return receivedTime;\n    }\n\n    /**\n     * @return IP address that relayed the block\n     */\n    public String getRelayedBy () {\n        return relayedBy;\n    }\n\n    /**\n     * @return Transactions in the block\n     */\n    public List<Transaction> getTransactions () {\n        return transactions;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/FilterType.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\npublic enum FilterType {\n    All(4),\n    ConfirmedOnly(5),\n    RemoveUnspendable(6);\n\n    private final Integer filterInt;\n\n    FilterType (Integer filterInt) {\n        this.filterInt = filterInt;\n    }\n\n    public Integer getFilterInt () {\n        return filterInt;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Input.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Represents a transaction input. If the `previousOutput` object is null, this is a\n * coinbase input.\n */\npublic class Input {\n    private Output previousOutput;\n    private long sequence;\n    private String scriptSignature;\n\n    public Input (Output previousOutput, long sequence, String scriptSignature) {\n        this.previousOutput = previousOutput;\n        this.sequence = sequence;\n        this.scriptSignature = scriptSignature;\n    }\n\n    public Input (JsonObject i) {\n        if (i.has(\"prev_out\") && !i.get(\"prev_out\").isJsonNull()) {\n            this.previousOutput = new Output(i.get(\"prev_out\").getAsJsonObject(), true);\n        }\n\n        this.sequence = i.get(\"sequence\").getAsLong();\n        this.scriptSignature = i.get(\"script\").getAsString();\n    }\n\n    /**\n     * @return Previous output. If null, this is a coinbase input.\n     */\n    public Output getPreviousOutput () {\n        return previousOutput;\n    }\n\n    /**\n     * @return Sequence number of the input\n     */\n    public long getSequence () {\n        return sequence;\n    }\n\n    /**\n     * @return Script signature\n     */\n    public String getScriptSignature () {\n        return scriptSignature;\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        Input input = (Input) o;\n\n        if (sequence != input.sequence) {\n            return false;\n        }\n        if (previousOutput == null) {\n            if (input.previousOutput != null) {\n                return false;\n            }\n        }\n        else if (!previousOutput.equals(input.previousOutput)) {\n            return false;\n        }\n        \n        return scriptSignature.equals(input.scriptSignature);\n    }\n\n    @Override\n    public int hashCode () {\n        int result = previousOutput == null ? 1 : previousOutput.hashCode();\n        result = 31 * result + (int) (sequence ^ (sequence >>> 32));\n        result = 31 * result + scriptSignature.hashCode();\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/LatestBlock.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Used as a response to the `getLatestBlock` method in the `BlockExplorer` class.\n */\npublic class LatestBlock extends SimpleBlock {\n    private long index;\n    private List<Long> transactionIndexes;\n\n    public LatestBlock (long height, String hash, long time, boolean mainChain, long index, List<Long> transactionIndexes) {\n        super(height, hash, time, mainChain);\n        this.index = index;\n        this.transactionIndexes = transactionIndexes;\n    }\n\n    public LatestBlock (JsonObject b) {\n        this(b.get(\"height\").getAsLong(), b.get(\"hash\").getAsString(), b.get(\"time\").getAsLong(), true, b.get(\"block_index\").getAsLong(), null);\n\n        transactionIndexes = new ArrayList<Long>();\n        for (JsonElement idxElem : b.get(\"txIndexes\").getAsJsonArray()) {\n            transactionIndexes.add(idxElem.getAsLong());\n        }\n    }\n\n    /**\n     * @return Transaction indexes included in this block\n     */\n    public List<Long> getTransactionIndexes () {\n        return transactionIndexes;\n    }\n\n    /**\n     * @return Block index\n     */\n    public long getIndex () {\n        return index;\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n        if (!super.equals(o)) {\n            return false;\n        }\n\n        LatestBlock that = (LatestBlock) o;\n\n        if (index != that.index) {\n            return false;\n        }\n        return transactionIndexes.equals(that.transactionIndexes);\n\n    }\n\n    @Override\n    public int hashCode () {\n        int result = (int) (index ^ (index >>> 32));\n        result = 31 * result + transactionIndexes.hashCode();\n        return result;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/MultiAddress.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MultiAddress {\n\n    private MultiAddressBalance multiAddressBalance;\n\n    private List<AddressSummary> addresses;\n\n    private List<Transaction> txs;\n\n    public MultiAddress (MultiAddressBalance multiAddressBalance, List<AddressSummary> addresses, List<Transaction> txs) {\n        this.multiAddressBalance = multiAddressBalance;\n        this.addresses = addresses;\n        this.txs = txs;\n    }\n\n    public MultiAddress (JsonObject json) {\n        List<AddressSummary> addresses = new ArrayList<AddressSummary>();\n        for (JsonElement txElem : json.get(\"addresses\").getAsJsonArray()) {\n            JsonObject addrObj = txElem.getAsJsonObject();\n            addresses.add(new AddressSummary(addrObj));\n        }\n        this.addresses = addresses;\n\n        List<Transaction> txs = new ArrayList<Transaction>();\n        for (JsonElement txElem : json.get(\"txs\").getAsJsonArray()) {\n            JsonObject addrObj = txElem.getAsJsonObject();\n            txs.add(new Transaction(addrObj));\n        }\n        this.txs = txs;\n\n        this.multiAddressBalance = new MultiAddressBalance(json.getAsJsonObject(\"wallet\"));\n    }\n\n    public MultiAddressBalance getMultiAddressBalance () {\n        return multiAddressBalance;\n    }\n\n    public List<AddressSummary> getAddresses() {\n        return addresses;\n    }\n\n    public List<Transaction> getTxs() {\n        return txs;\n    }\n\n    public void setMultiAddressBalance (MultiAddressBalance multiAddressBalance) {\n        this.multiAddressBalance = multiAddressBalance;\n    }\n\n    public void setAddresses(List<AddressSummary> addresses) {\n        this.addresses = addresses;\n    }\n\n    public void setTxs(List<Transaction> txs) {\n        this.txs = txs;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/MultiAddressBalance.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\npublic class MultiAddressBalance {\n\n    private int txCount;\n    private int txCountFiltered;\n    private long totalReceived;\n    private long totalSent;\n    private long finalBalance;\n\n    public MultiAddressBalance (int txCount, int txCountFiltered, long totalReceived, long totalSent, long finalBalance) {\n        this.txCount = txCount;\n        this.txCountFiltered = txCountFiltered;\n        this.totalReceived = totalReceived;\n        this.totalSent = totalSent;\n        this.finalBalance = finalBalance;\n    }\n\n    public MultiAddressBalance (JsonObject json) {\n        this(\n                json.has(\"n_tx\") ? json.get(\"n_tx\").getAsInt() : 0,\n                json.has(\"n_tx_filtered\") ? json.get(\"n_tx\").getAsInt() : 0,\n                json.has(\"total_received\") ? json.get(\"total_received\").getAsLong() : 0,\n                json.has(\"total_sent\") ? json.get(\"total_sent\").getAsLong() : 0,\n                json.has(\"final_balance\") ? json.get(\"final_balance\").getAsLong() : 0\n        );\n    }\n\n    public int getTxCount() {\n        return txCount;\n    }\n\n    public void setTxCount(int txCount) {\n        this.txCount = txCount;\n    }\n\n    public long getTotalReceived() {\n        return totalReceived;\n    }\n\n    public long getTotalSent() {\n        return totalSent;\n    }\n\n    public long getFinalBalance() {\n        return finalBalance;\n    }\n\n    public void setFinalBalance(long final_balance) {\n        this.finalBalance = final_balance;\n    }\n\n    public void setTotalReceived(long total_received) {\n        this.totalReceived = total_received;\n    }\n\n    public void setTotalSent(long total_sent) {\n        this.totalSent = total_sent;\n    }\n\n    public int getTxCountFiltered () {\n        return txCountFiltered;\n    }\n\n    public void setTxCountFiltered (int txCountFiltered) {\n        this.txCountFiltered = txCountFiltered;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Output.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Represents a transaction output.\n */\npublic class Output {\n    private int n;\n    private long value;\n    private String address;\n    private long txIndex;\n    private String script;\n    private boolean spent;\n    private boolean spentToAddress;\n\n    public Output (int n, long value, String address, long txIndex, String script, boolean spent) {\n        this.n = n;\n        this.value = value;\n        this.address = address;\n        this.txIndex = txIndex;\n        this.script = script;\n        this.spent = spent;\n        if (address != \"\") {\n            spentToAddress = true;\n        }\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        Output output = (Output) o;\n\n        if (value != output.value) {\n            return false;\n        }\n        if (txIndex != output.txIndex) {\n            return false;\n        }\n        if (n != output.n) {\n          return false;\n        }\n        \n        return !(script != null ? !script.equals(output.script) : output.script != null);\n\n    }\n\n    @Override\n    public int hashCode () {\n        int result = (int) (value ^ (value >>> 32));\n        result = 31 * result + (int) (txIndex ^ (txIndex >>> 32));\n        result = 31 * result + (int) (n ^ (n >>> 32));\n        result = 31 * result + (script != null ? script.hashCode() : 0);\n        return result;\n    }\n\n    public Output (JsonObject o) {\n        this(o, o.get(\"spent\").getAsBoolean());\n    }\n\n    public Output (JsonObject o, boolean spent) {\n        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);\n    }\n\n    /**\n     * @return Index of the output in a transaction\n     */\n    public int getN () {\n        return n;\n    }\n\n    /**\n     * @return Value of the output (in satoshi)\n     */\n    public long getValue () {\n        return value;\n    }\n\n    /**\n     * @return Address that the output belongs to\n     */\n    public String getAddress () {\n        return address;\n    }\n\n    /**\n     * @return Transaction index\n     */\n    public long getTxIndex () {\n        return txIndex;\n    }\n\n    /**\n     * @return Output script\n     */\n    public String getScript () {\n        return script;\n    }\n\n    /**\n     * @return Whether the output is spent\n     */\n    public boolean isSpent () {\n        return spent;\n    }\n\n    /**\n     * @return Whether the output pays to an address.\n     */\n    public boolean isSpentToAddress () {\n        return spentToAddress;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/SimpleBlock.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Simple representation of a block\n */\npublic class SimpleBlock {\n    private long height;\n    private String hash;\n    private long time;\n    private boolean mainChain;\n\n    public SimpleBlock (long height, String hash, long time, boolean mainChain) {\n        this.height = height;\n        this.hash = hash;\n        this.time = time;\n        this.mainChain = mainChain;\n    }\n\n    public SimpleBlock (JsonObject b) {\n        this(b.get(\"height\").getAsLong(), b.get(\"hash\").getAsString(), b.get(\"time\").getAsLong(), b.get(\"main_chain\").getAsBoolean());\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        SimpleBlock that = (SimpleBlock) o;\n\n        if (height != that.height) {\n            return false;\n        }\n        if (time != that.time) {\n            return false;\n        }\n        if (mainChain != that.mainChain) {\n            return false;\n        }\n        return !(hash != null ? !hash.equals(that.hash) : that.hash != null);\n\n    }\n\n    @Override\n    public int hashCode () {\n        int result = (int) (height ^ (height >>> 32));\n        result = 31 * result + (hash != null ? hash.hashCode() : 0);\n        result = 31 * result + (int) (time ^ (time >>> 32));\n        result = 31 * result + (mainChain ? 1 : 0);\n        return result;\n    }\n\n    /**\n     * @return Block height\n     */\n    public long getHeight () {\n        return height;\n    }\n\n    /**\n     * @return Block hash\n     */\n    public String getHash () {\n        return hash;\n    }\n\n    /**\n     * @return Block timestamp set by the miner (unix time in seconds)\n     */\n    public long getTime () {\n        return time;\n    }\n\n    /**\n     * @return Whether the block is on the main chain\n     */\n    public boolean isMainChain () {\n        return mainChain;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/Transaction.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Represents a transaction.\n */\npublic class Transaction {\n    private boolean doubleSpend;\n    private long blockHeight;\n    private long time;\n    private long lockTime;\n    private String relayedBy;\n    private String hash;\n    private long index;\n    private int version;\n    private long size;\n    private List<Input> inputs;\n    private List<Output> outputs;\n\n    public Transaction (boolean doubleSpend, long blockHeight, long time, long lockTime, String relayedBy,\n        String hash, long index, int version, long size, List<Input> inputs, List<Output> outputs) {\n        this.doubleSpend = doubleSpend;\n        this.blockHeight = blockHeight;\n        this.time = time;\n        this.lockTime = lockTime;\n        this.relayedBy = relayedBy;\n        this.hash = hash;\n        this.index = index;\n        this.version = version;\n        this.size = size;\n        this.inputs = inputs;\n        this.outputs = outputs;\n    }\n\n    public Transaction (JsonObject t) {\n        this(t, t.has(\"block_height\") ? t.get(\"block_height\").getAsLong() : -1,\n            t.has(\"double_spend\") ? t.get(\"double_spend\").getAsBoolean() : false);\n    }\n\n    public Transaction (JsonObject t, long blockHeight, boolean doubleSpend) {\n        this(doubleSpend, blockHeight, t.get(\"time\").getAsLong(), t.get(\"lock_time\").getAsLong(),\n            t.get(\"relayed_by\").getAsString(), t.get(\"hash\").getAsString(), t.get(\"tx_index\").getAsLong(),\n            t.get(\"ver\").getAsInt(), t.get(\"size\").getAsLong(), null, null);\n\n        inputs = new ArrayList<Input>();\n        for (JsonElement inputElem : t.get(\"inputs\").getAsJsonArray()) {\n            inputs.add(new Input(inputElem.getAsJsonObject()));\n        }\n\n        outputs = new ArrayList<Output>();\n        for (JsonElement outputElem : t.get(\"out\").getAsJsonArray()) {\n            outputs.add(new Output(outputElem.getAsJsonObject()));\n        }\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        Transaction that = (Transaction) o;\n\n        if (index != that.index) {\n            return false;\n        }\n        if (version != that.version) {\n            return false;\n        }\n        if (size != that.size) {\n            return false;\n        }\n        if (hash != null ? !hash.equals(that.hash) : that.hash != null) {\n            return false;\n        }\n        if (inputs != null ? !inputs.equals(that.inputs) : that.inputs != null) {\n            return false;\n        }\n        return !(outputs != null ? !outputs.equals(that.outputs) : that.outputs != null);\n\n    }\n\n    @Override\n    public int hashCode () {\n        int result = hash != null ? hash.hashCode() : 0;\n        result = 31 * result + (int) (index ^ (index >>> 32));\n        result = 31 * result + version;\n        result = 31 * result + (int) (size ^ (size >>> 32));\n        result = 31 * result + (inputs != null ? inputs.hashCode() : 0);\n        result = 31 * result + (outputs != null ? outputs.hashCode() : 0);\n        return result;\n    }\n\n    /**\n     * @return Whether the transaction is a double spend\n     */\n    public boolean isDoubleSpend () {\n        return doubleSpend;\n    }\n\n    /**\n     * @return Block height of the parent block. -1 for unconfirmed transactions.\n     */\n    public long getBlockHeight () {\n        return blockHeight;\n    }\n\n    /**\n     * @return Timestamp of the transaction\n     */\n    public long getTime () {\n        return time;\n    }\n\n    /**\n     * @return Locktime of the transaction\n     */\n    public long getLockTime () {\n        return lockTime;\n    }\n\n    /**\n     * @return IP address that relayed the transaction\n     */\n    public String getRelayedBy () {\n        return relayedBy;\n    }\n\n    /**\n     * @return Transaction hash\n     */\n    public String getHash () {\n        return hash;\n    }\n\n    /**\n     * @return Transaction index\n     */\n    public long getIndex () {\n        return index;\n    }\n\n    /**\n     * @return Transaction format version\n     */\n    public int getVersion () {\n        return version;\n    }\n\n    /**\n     * @return Serialized size of the transaction\n     */\n    public long getSize () {\n        return size;\n    }\n\n    /**\n     * @return List of inputs\n     */\n    public List<Input> getInputs () {\n        return inputs;\n    }\n\n    /**\n     * @return List of outputs\n     */\n    public List<Output> getOutputs () {\n        return outputs;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/UnspentOutput.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonObject;\n\n/**\n * Represents an unspent transaction output.\n */\npublic class UnspentOutput {\n    private int n;\n    private String transactionHash;\n    private long transactionIndex;\n    private String script;\n    private long value;\n    private long confirmations;\n\n    public UnspentOutput (int n, String transactionHash, long transactionIndex, String script, long value, long confirmations) {\n        this.n = n;\n        this.transactionHash = transactionHash;\n        this.transactionIndex = transactionIndex;\n        this.script = script;\n        this.value = value;\n        this.confirmations = confirmations;\n    }\n\n    public UnspentOutput (JsonObject o) {\n        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());\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        UnspentOutput that = (UnspentOutput) o;\n\n        if (transactionIndex != that.transactionIndex) {\n            return false;\n        }\n        if (value != that.value) {\n            return false;\n        }\n        if (transactionHash != null ? !transactionHash.equals(that.transactionHash) : that.transactionHash != null) {\n            return false;\n        }\n        return !(script != null ? !script.equals(that.script) : that.script != null);\n\n    }\n\n    @Override\n    public int hashCode () {\n        int result = transactionHash != null ? transactionHash.hashCode() : 0;\n        result = 31 * result + (int) (transactionIndex ^ (transactionIndex >>> 32));\n        result = 31 * result + (script != null ? script.hashCode() : 0);\n        result = 31 * result + (int) (value ^ (value >>> 32));\n        return result;\n    }\n\n    /**\n     * @return Index of the output in a transaction\n     */\n    public int getN () {\n        return n;\n    }\n\n    /**\n     * @return Transaction hash\n     */\n    public String getTransactionHash () {\n        return transactionHash;\n    }\n\n    /**\n     * @return Transaction index\n     */\n    public long getTransactionIndex () {\n        return transactionIndex;\n    }\n\n    /**\n     * @return Output script\n     */\n    public String getScript () {\n        return script;\n    }\n\n    /**\n     * @return Value of the output (in satoshi)\n     */\n    public long getValue () {\n        return value;\n    }\n\n    /**\n     * @return Number of confirmations\n     */\n    public long getConfirmations () {\n        return confirmations;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/blockexplorer/entity/XpubFull.java",
    "content": "package info.blockchain.api.blockexplorer.entity;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class XpubFull {\n\n    private String address;\n    private int txCount;\n    private long totalReceived;\n    private long totalSent;\n    private long finalBalance;\n    private int changeIndex;\n    private int accountIndex;\n    private int gapLimit;\n    private List<Transaction> txs;\n\n    public XpubFull (String address, int txCount, long totalReceived, long totalSent, long finalBalance, int changeIndex, int accountIndex, int gapLimit,\n                     List<Transaction> txs) {\n        this.address = address;\n        this.txCount = txCount;\n        this.totalReceived = totalReceived;\n        this.totalSent = totalSent;\n        this.finalBalance = finalBalance;\n        this.changeIndex = changeIndex;\n        this.accountIndex = accountIndex;\n        this.gapLimit = gapLimit;\n        this.txs = txs;\n    }\n\n    public XpubFull (AddressSummary addressSummary, List<Transaction> txs) {\n        this.address = addressSummary.getAddress();\n        this.txCount = addressSummary.getTxCount();\n        this.accountIndex = addressSummary.getAccountIndex();\n        this.changeIndex = addressSummary.getChangeIndex();\n        this.finalBalance = addressSummary.getFinalBalance();\n        this.gapLimit = addressSummary.getGapLimit();\n        this.totalReceived = addressSummary.getTotalReceived();\n        this.totalSent = addressSummary.getTotalSent();\n        this.txs = txs;\n    }\n\n    public XpubFull (JsonObject json) {\n        this(\n                json.has(\"address\") ? json.get(\"address\").getAsString() : \"\",\n                json.has(\"n_tx\") ? json.get(\"n_tx\").getAsInt() : 0,\n                json.has(\"total_received\") ? json.get(\"total_received\").getAsLong() : 0,\n                json.has(\"total_sent\") ? json.get(\"total_sent\").getAsLong() : 0,\n                json.has(\"final_balance\") ? json.get(\"final_balance\").getAsLong() : 0,\n                json.has(\"change_index\") ? json.get(\"change_index\").getAsInt() : 0,\n                json.has(\"account_index\") ? json.get(\"account_index\").getAsInt() : 0,\n                json.has(\"gap_limit\") ? json.get(\"gap_limit\").getAsInt() : 0,\n                null\n        );\n\n        txs = new ArrayList<Transaction>();\n        for (JsonElement txElem : json.get(\"txs\").getAsJsonArray()) {\n            JsonObject addrObj = txElem.getAsJsonObject();\n            txs.add(new Transaction(addrObj));\n        }\n    }\n\n    public String getAddress() {\n        return address;\n    }\n\n    public int getTxCount() {\n        return txCount;\n    }\n\n    public void setTxCount(int txCount) {\n        this.txCount = txCount;\n    }\n\n    public long getTotalReceived() {\n        return totalReceived;\n    }\n\n    public long getTotalSent() {\n        return totalSent;\n    }\n\n    public long getFinalBalance() {\n        return finalBalance;\n    }\n\n    public int getChangeIndex() {\n        return changeIndex;\n    }\n\n    public int getAccountIndex() {\n        return accountIndex;\n    }\n\n    public void setFinalBalance(long final_balance) {\n        this.finalBalance = final_balance;\n    }\n\n    public void setAddress(String address) {\n        this.address = address;\n    }\n\n    public void setTotalReceived(long total_received) {\n        this.totalReceived = total_received;\n    }\n\n    public void setTotalSent(long total_sent) {\n        this.totalSent = total_sent;\n    }\n\n    public void setChangeIndex(int change_index) {\n        this.changeIndex = change_index;\n    }\n\n    public void setAccountIndex(int account_index) {\n        this.accountIndex = account_index;\n    }\n\n    public int getGapLimit () {\n        return gapLimit;\n    }\n\n    public void setGapLimit (int gapLimit) {\n        this.gapLimit = gapLimit;\n    }\n\n    public List<Transaction> getTxs () {\n        return txs;\n    }\n\n    public void setTxs (List<Transaction> txs) {\n        this.txs = txs;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/etc/Base58.java",
    "content": "package info.blockchain.api.etc;\n\n/**\n * Copyright 2011 Google Inc.\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.text.ParseException;\nimport java.util.Arrays;\n\n/**\n * Base58 is a way to encode Bitcoin addresses (or arbitrary data) as alphanumeric strings.\n * <p>\n * Note that this is not the same base58 as used by Flickr, which you may find referenced around the Internet.\n * <p>\n * Satoshi explains: why base-58 instead of standard base-64 encoding?\n * <ul>\n * <li>Don't want 0OIl characters that look the same in some fonts and\n * could be used to create visually identical looking account numbers.</li>\n * <li>A string with non-alphanumeric characters is not as easily accepted as an account number.</li>\n * <li>E-mail usually won't line-break if there's no punctuation to break at.</li>\n * <li>Doubleclicking selects the whole number as one word if it's all alphanumeric.</li>\n * </ul>\n * <p>\n * However, note that the encoding/decoding runs in O(n&sup2;) time, so it is not useful for large data.\n * <p>\n * The basic idea of the encoding is to treat the data bytes as a large number represented using\n * base-256 digits, convert the number to be represented using base-58 digits, preserve the exact\n * number of leading zeros (which are otherwise lost during the mathematical operations on the\n * numbers), and finally represent the resulting base-58 digits as alphanumeric ASCII characters.\n */\npublic class Base58 {\n    public static final char[] ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\".toCharArray();\n    private static final char ENCODED_ZERO = ALPHABET[0];\n    private static final int[] INDEXES = new int[128];\n\n    static {\n        Arrays.fill(INDEXES, -1);\n        for (int i = 0; i < ALPHABET.length; i++) {\n            INDEXES[ALPHABET[i]] = i;\n        }\n    }\n\n    /**\n     * Encodes the given bytes as a base58 string (no checksum is appended).\n     *\n     * @param input the bytes to encode\n     * @return the base58-encoded string\n     */\n    public static String encode (byte[] input) {\n        if (input.length == 0) {\n            return \"\";\n        }\n        // Count leading zeros.\n        int zeros = 0;\n        while (zeros < input.length && input[zeros] == 0) {\n            ++zeros;\n        }\n        // Convert base-256 digits to base-58 digits (plus conversion to ASCII characters)\n        input = Arrays.copyOf(input, input.length); // since we modify it in-place\n        char[] encoded = new char[input.length * 2]; // upper bound\n        int outputStart = encoded.length;\n        for (int inputStart = zeros; inputStart < input.length; ) {\n            encoded[--outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)];\n            if (input[inputStart] == 0) {\n                ++inputStart; // optimization - skip leading zeros\n            }\n        }\n        // Preserve exactly as many leading encoded zeros in output as there were leading zeros in input.\n        while (outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {\n            ++outputStart;\n        }\n        while (--zeros >= 0) {\n            encoded[--outputStart] = ENCODED_ZERO;\n        }\n        // Return encoded string (including encoded leading zeros).\n        return new String(encoded, outputStart, encoded.length - outputStart);\n    }\n\n    /**\n     * Decodes the given base58 string into the original data bytes.\n     *\n     * @param input the base58-encoded string to decode\n     * @return the decoded data bytes\n     * @throws ParseException if the given string is not a valid base58 string\n     */\n    public static byte[] decode (String input) throws ParseException {\n        if (input.length() == 0) {\n            return new byte[0];\n        }\n        // Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).\n        byte[] input58 = new byte[input.length()];\n        for (int i = 0; i < input.length(); ++i) {\n            char c = input.charAt(i);\n            int digit = c < 128 ? INDEXES[c] : -1;\n            if (digit < 0) {\n                throw new ParseException(\"Illegal character \" + c + \" at position \" + i, i);\n            }\n            input58[i] = (byte) digit;\n        }\n        // Count leading zeros.\n        int zeros = 0;\n        while (zeros < input58.length && input58[zeros] == 0) {\n            ++zeros;\n        }\n        // Convert base-58 digits to base-256 digits.\n        byte[] decoded = new byte[input.length()];\n        int outputStart = decoded.length;\n        for (int inputStart = zeros; inputStart < input58.length; ) {\n            decoded[--outputStart] = divmod(input58, inputStart, 58, 256);\n            if (input58[inputStart] == 0) {\n                ++inputStart; // optimization - skip leading zeros\n            }\n        }\n        // Ignore extra leading zeroes that were added during the calculation.\n        while (outputStart < decoded.length && decoded[outputStart] == 0) {\n            ++outputStart;\n        }\n        // Return decoded data (including original number of leading zeros).\n        return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length);\n    }\n\n    public static BigInteger decodeToBigInteger (String input) throws ParseException {\n        return new BigInteger(1, decode(input));\n    }\n\n    /**\n     * Decodes the given base58 string into the original data bytes, using the checksum in the\n     * last 4 bytes of the decoded data to verify that the rest are correct. The checksum is\n     * removed from the returned data.\n     *\n     * @param input the base58-encoded string to decode (which should include the checksum)\n     * @throws ParseException if the input is not base 58 or the checksum does not validate.\n     */\n    public static byte[] decodeChecked (String input) throws ParseException {\n        byte[] decoded = decode(input);\n        if (decoded.length < 4) {\n            throw new ParseException(\"Input too short\", 0);\n        }\n        byte[] data = Arrays.copyOfRange(decoded, 0, decoded.length - 4);\n        byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length);\n        byte[] actualChecksum = Arrays.copyOfRange(hashTwice(data), 0, 4);\n        if (!Arrays.equals(checksum, actualChecksum)) {\n            throw new ParseException(\"Checksum does not validate\", 0);\n        }\n        return data;\n    }\n\n    /**\n     * Divides a number, represented as an array of bytes each containing a single digit\n     * in the specified base, by the given divisor. The given number is modified in-place\n     * to contain the quotient, and the return value is the remainder.\n     *\n     * @param number     the number to divide\n     * @param firstDigit the index within the array of the first non-zero digit\n     *                   (this is used for optimization by skipping the leading zeros)\n     * @param base       the base in which the number's digits are represented (up to 256)\n     * @param divisor    the number to divide by (up to 256)\n     * @return the remainder of the division operation\n     */\n    private static byte divmod (byte[] number, int firstDigit, int base, int divisor) {\n        // this is just long division which accounts for the base of the input digits\n        int remainder = 0;\n        for (int i = firstDigit; i < number.length; i++) {\n            int digit = (int) number[i] & 0xFF;\n            int temp = remainder * base + digit;\n            number[i] = (byte) (temp / divisor);\n            remainder = temp % divisor;\n        }\n        return (byte) remainder;\n    }\n\n    public static byte[] hashTwice (byte[] input) {\n        MessageDigest digest = null;\n        try {\n            digest = MessageDigest.getInstance(\"SHA-256\");\n            digest.update(input, 0, input.length);\n            return digest.digest(digest.digest());\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(e);\n        }\n\n    }\n}"
  },
  {
    "path": "src/main/java/info/blockchain/api/exchangerates/Currency.java",
    "content": "package info.blockchain.api.exchangerates;\n\nimport java.math.BigDecimal;\n\n/**\n * Used in response to the `getTicker` method in the `ExchangeRates` class.\n */\npublic class Currency {\n    private BigDecimal buy;\n    private BigDecimal sell;\n    private BigDecimal last;\n    private BigDecimal price15m;\n    private String symbol;\n\n    public Currency (double buy, double sell, double last, double price15m, String symbol) {\n        this.buy = BigDecimal.valueOf(buy);\n        this.sell = BigDecimal.valueOf(sell);\n        this.last = BigDecimal.valueOf(last);\n        this.price15m = BigDecimal.valueOf(price15m);\n        this.symbol = symbol;\n    }\n\n    /**\n     * @return Current buy price\n     */\n    public BigDecimal getBuy () {\n        return buy;\n    }\n\n    /**\n     * @return Current sell price\n     */\n    public BigDecimal getSell () {\n        return sell;\n    }\n\n    /**\n     * @return Most recent market price\n     */\n    public BigDecimal getLast () {\n        return last;\n    }\n\n    /**\n     * @return 15 minutes delayed market price\n     */\n    public BigDecimal getPrice15m () {\n        return price15m;\n    }\n\n    /**\n     * @return Currency symbol\n     */\n    public String getSymbol () {\n        return symbol;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/exchangerates/ExchangeRates.java",
    "content": "package info.blockchain.api.exchangerates;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\nimport info.blockchain.api.APIException;\nimport info.blockchain.api.HttpClient;\n\nimport java.io.IOException;\nimport java.math.BigDecimal;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\n/**\n * This class reflects the functionality documented\n * at https://blockchain.info/api/exchange_rates_api. It allows users to fetch the latest\n * ticker data and convert amounts between BTC and fiat currencies.\n */\npublic class ExchangeRates {\n\n    private final String apiCode;\n\n    public ExchangeRates () {\n        this(null);\n    }\n\n    public ExchangeRates (String apiCode) {\n        this.apiCode = apiCode;\n    }\n\n    /**\n     * Gets the price ticker from https://blockchain.info/ticker\n     *\n     * @return A map of currencies where the key is a 3-letter currency symbol and the\n     * value is the `Currency` class\n     * @throws APIException If the server returns an error\n     */\n    public Map<String, Currency> getTicker () throws APIException, IOException {\n        Map<String, String> params = new HashMap<String, String>();\n        if (apiCode != null) {\n            params.put(\"api_code\", apiCode);\n        }\n\n        String response = HttpClient.getInstance().get(\"ticker\", params);\n        JsonObject ticker = new JsonParser().parse(response).getAsJsonObject();\n\n        Map<String, Currency> resultMap = new HashMap<String, Currency>();\n        for (Entry<String, JsonElement> ccyKVP : ticker.entrySet()) {\n            JsonObject ccy = ccyKVP.getValue().getAsJsonObject();\n            Currency currency = new Currency(ccy.get(\"buy\").getAsDouble(), ccy.get(\"sell\").getAsDouble(), ccy.get(\"last\").getAsDouble(), ccy.get(\"15m\").getAsDouble(), ccy.get(\"symbol\").getAsString());\n\n            resultMap.put(ccyKVP.getKey(), currency);\n        }\n\n        return resultMap;\n    }\n\n    /**\n     * Converts x value in the provided currency to BTC.\n     *\n     * @param currency Currency code\n     * @param value    Value to convert\n     * @return Converted value in BTC\n     * @throws APIException If the server returns an error\n     */\n    public BigDecimal toBTC (String currency, BigDecimal value) throws APIException, IOException {\n        Map<String, String> params = new HashMap<String, String>();\n        params.put(\"currency\", currency);\n        params.put(\"value\", String.valueOf(value));\n        if (apiCode != null) {\n            params.put(\"api_code\", apiCode);\n        }\n\n        String response = HttpClient.getInstance().get(\"tobtc\", params);\n        return new BigDecimal(response);\n    }\n\n    /**\n     * Converts x value in BTC to the provided currency.\n     *\n     * @param currency Currency code\n     * @param value    Value to convert\n     * @return Converted value in the provided currency\n     * @throws APIException If the server returns an error\n     */\n    public BigDecimal toFiat (String currency, BigDecimal value) throws APIException, IOException {\n        Map<String, String> params = new HashMap<String, String>();\n        params.put(\"currency\", currency);\n        params.put(\"value\", String.valueOf(value.multiply(BigDecimal.valueOf(100000000L)))); // The endpoint is expecting satoshi\n        if (apiCode != null) {\n            params.put(\"api_code\", apiCode);\n        }\n\n        String response = HttpClient.getInstance().get(\"frombtc\", params);\n        return new BigDecimal(response);\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/pushtx/PushTx.java",
    "content": "package info.blockchain.api.pushtx;\n\nimport info.blockchain.api.APIException;\nimport info.blockchain.api.HttpClient;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * This class reflects the functionality provided at\n * https://blockchain.info/pushtx. It allows users to broadcast hex encoded\n * transactions to the bitcoin network.\n */\npublic class PushTx {\n    /**\n     * Pushes a hex encoded transaction to the network.\n     *\n     * @param tx Hex encoded transaction\n     * @throws APIException If the server returns an error (malformed tx etc.)\n     */\n    public static void pushTx (String tx) throws APIException, IOException {\n        pushTx(tx, null);\n    }\n\n    /**\n     * Pushes a hex encoded transaction to the network.\n     *\n     * @param tx      Hex encoded transaction\n     * @param apiCode Blockchain.info API code (optional, nullable)\n     * @throws APIException If the server returns an error (malformed tx etc.)\n     */\n    public static void pushTx (String tx, String apiCode) throws APIException, IOException {\n        Map<String, String> params = new HashMap<String, String>();\n        params.put(\"tx\", tx);\n\n        if (apiCode != null) {\n            params.put(\"api_code\", apiCode);\n        }\n\n        HttpClient.getInstance().post(\"pushtx\", params);\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/receive/CallbackLog.java",
    "content": "package info.blockchain.api.receive;\n\nimport com.google.gson.JsonObject;\n\npublic class CallbackLog {\n    private String callback;\n    private String callbackTime;\n    private String response;\n    private int responseCode;\n\n    public CallbackLog (JsonObject callbackJson) {\n        this(\n                callbackJson.has(\"callback\") ? callbackJson.get(\"callback\").getAsString() : \"\",\n                callbackJson.has(\"called_at\") ? callbackJson.get(\"called_at\").getAsString() : \"\",\n                callbackJson.has(\"raw_response\") ? callbackJson.get(\"raw_response\").getAsString() : \"\",\n                callbackJson.has(\"response_code\") ? callbackJson.get(\"response_code\").getAsInt() : 0\n        );\n    }\n\n    public CallbackLog (String callback, String callbackTime, String response, int responseCode) {\n        this.callback = callback;\n        this.callbackTime = callbackTime;\n        this.response = response;\n        this.responseCode = responseCode;\n    }\n\n    public String getCallback () {\n        return callback;\n    }\n\n    public void setCallback (String callback) {\n        this.callback = callback;\n    }\n\n    public String getCallbackTime () {\n        return callbackTime;\n    }\n\n    public void setCallbackTime (String callbackTime) {\n        this.callbackTime = callbackTime;\n    }\n\n    public String getResponse () {\n        return response;\n    }\n\n    public void setResponse (String response) {\n        this.response = response;\n    }\n\n    public int getResponseCode () {\n        return responseCode;\n    }\n\n    public void setResponseCode (int responseCode) {\n        this.responseCode = responseCode;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/receive/Receive.java",
    "content": "package info.blockchain.api.receive;\n\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\nimport info.blockchain.api.APIException;\nimport info.blockchain.api.HttpClient;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * This class reflects the functionality necessary for using the receive-payments-api v2.\n * Passing on a xPUB, callbackUrl and the apiCode will return an address for receiving a payment.\n * <p>\n * Upon receiving a payment on this address, the merchant will be notified using the callback URL.\n */\npublic class Receive {\n    private final String apiCode;\n\n    /**\n     * @param apiCode     Blockchain.info API code for the receive-payments v2 API (different from normal API key)\n     */\n    public Receive (String apiCode) {\n        this.apiCode = apiCode;\n    }\n\n    /**\n     * Calls the receive-payments-api v2 and returns an address for the payment.\n     *\n     * @param xPUB        Destination address where the payment should be sent\n     * @param callbackUrl Callback URI that will be called upon payment\n     * @return An instance of the ReceiveV2Response class\n     * @throws APIException If the server returns an error\n     */\n    public ReceiveResponse receive (String xPUB, String callbackUrl) throws APIException, IOException {\n        Map<String, String> params = new HashMap<String, String>();\n        params.put(\"xpub\", xPUB);\n        params.put(\"callback\", callbackUrl);\n        params.put(\"key\", apiCode);\n\n        String response = HttpClient.getInstance().get(\"https://api.blockchain.info/\", \"v2/receive\", params);\n        JsonParser parser = new JsonParser();\n        JsonObject obj = parser.parse(response).getAsJsonObject();\n\n        return new ReceiveResponse(obj.get(\"index\").getAsInt(), obj.get(\"address\").getAsString(), obj.get(\"callback\").getAsString());\n    }\n\n    /**\n     * Calls the receive-payments-api v2 and returns the xpub gap of an xpub.\n     *\n     * @param xPUB        Destination address where the payment should be sent\n     * @return An instance of the ReceiveV2Response class\n     * @throws APIException If the server returns an error\n     */\n    public int checkGap (String xPUB) throws APIException, IOException {\n        Map<String, String> params = new HashMap<String, String>();\n        params.put(\"xpub\", xPUB);\n        params.put(\"key\", apiCode);\n\n        String response = HttpClient.getInstance().get(\"https://api.blockchain.info/\", \"v2/receive/checkgap\", params);\n        JsonObject obj = new JsonParser().parse(response).getAsJsonObject();\n\n        return obj.get(\"gap\").getAsInt();\n    }\n\n    /**\n     * Calls the receive-payments-api v2 and returns the callback log based on url.\n     *\n     * @param callbackUrl Callback URI that will be called upon payment\n     * @return An instance of the ReceiveV2Response class\n     * @throws APIException If the server returns an error\n     */\n    public CallbackLog getCallbackLog (String callbackUrl) throws APIException, IOException {\n        Map<String, String> params = new HashMap<String, String>();\n        params.put(\"callback\", callbackUrl);\n        params.put(\"key\", apiCode);\n\n        String response = HttpClient.getInstance().get(\"https://api.blockchain.info/\", \"v2/receive/callback\", params);\n        JsonObject obj = new JsonParser().parse(response).getAsJsonObject();\n\n        return new CallbackLog(obj);\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/receive/ReceiveResponse.java",
    "content": "package info.blockchain.api.receive;\n\n/**\n * This class is used as a response object to the `ReceiveV2.receive` method.\n */\npublic class ReceiveResponse {\n    private int index;\n    private String receivingAddress;\n    private String callbackUrl;\n\n    public ReceiveResponse (int index, String receivingAddress, String callbackUrl) {\n        this.index = index;\n        this.receivingAddress = receivingAddress;\n        this.callbackUrl = callbackUrl;\n    }\n\n    /**\n     * @return Index of the address in the account\n     */\n    public int getIndex () {\n        return index;\n    }\n\n    /**\n     * @return Address to be displayed for the customer at checkout.\n     */\n    public String getReceivingAddress () {\n        return receivingAddress;\n    }\n\n    /**\n     * @return Callback URI that will be called upon payment\n     */\n    public String getCallbackUrl () {\n        return callbackUrl;\n    }\n}"
  },
  {
    "path": "src/main/java/info/blockchain/api/statistics/Chart.java",
    "content": "package info.blockchain.api.statistics;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Chart {\n\n    private String status;\n    private String name;\n    private String unit;\n    private String period;\n    private String description;\n    private List<Point> values;\n\n    public Chart (JsonObject chartJson) {\n        this(\n                chartJson.has(\"status\") ? chartJson.get(\"status\").getAsString() : \"\",\n                chartJson.has(\"name\") ? chartJson.get(\"name\").getAsString() : \"\",\n                chartJson.has(\"unit\") ? chartJson.get(\"unit\").getAsString() : \"\",\n                chartJson.has(\"period\") ? chartJson.get(\"period\").getAsString() : \"\",\n                chartJson.has(\"description\") ? chartJson.get(\"description\").getAsString() : \"\",\n                null\n        );\n        values = getPoints(chartJson);\n    }\n\n    public Chart (String status, String name, String unit, String period, String description, List<Point> values) {\n        this.status = status;\n        this.name = name;\n        this.unit = unit;\n        this.period = period;\n        this.description = description;\n        this.values = values;\n    }\n\n    private List<Point> getPoints(JsonObject chartJson) {\n        List<Point> points = new ArrayList<Point>();\n        for (JsonElement pointElement : chartJson.getAsJsonArray()) {\n            JsonObject pointJson = pointElement.getAsJsonObject();\n            points.add(new Point(pointJson));\n        }\n\n        return points;\n    }\n\n    public String getStatus() {\n        return status;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public String getUnit() {\n        return unit;\n    }\n\n    public String getPeriod() {\n        return period;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public List<Point> getValues() {\n        return values;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/statistics/Point.java",
    "content": "package info.blockchain.api.statistics;\n\nimport com.google.gson.JsonObject;\n\npublic class Point {\n\n    private float x;\n    private float y;\n\n    public Point (JsonObject pointJson) {\n        this(\n                pointJson.has(\"x\") ? pointJson.get(\"x\").getAsFloat() : 0.0F,\n                pointJson.has(\"y\") ? pointJson.get(\"y\").getAsFloat() : 0.0F\n        );\n    }\n\n    public Point (float x, float y) {\n        this.x = x;\n        this.y = y;\n    }\n\n    public float getX() {\n        return x;\n    }\n\n    public float getY() {\n        return y;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/statistics/Statistics.java",
    "content": "package info.blockchain.api.statistics;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\nimport com.google.gson.reflect.TypeToken;\nimport info.blockchain.api.APIException;\nimport info.blockchain.api.HttpClient;\n\nimport java.io.IOException;\nimport java.lang.reflect.Type;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * This class reflects the functionality documented\n * at https://blockchain.info/api/charts_api\n */\npublic class Statistics {\n    private final String apiCode;\n\n    public Statistics () {\n        this(null);\n    }\n\n    public Statistics (String apiCode) {\n        this.apiCode = apiCode;\n    }\n\n    /**\n     * Gets the network statistics.\n     *\n     * @return An instance of the StatisticsResponse class\n     * @throws APIException If the server returns an error\n     */\n    public StatisticsResponse getStats () throws APIException, IOException {\n        Map<String, String> params = new HashMap<String, String>();\n        params.put(\"format\", \"json\");\n        if (apiCode != null) {\n            params.put(\"api_code\", apiCode);\n        }\n\n        String response = HttpClient.getInstance().get(\"stats\", params);\n        return new StatisticsResponse(response);\n    }\n\n    /**\n     * This method can be used to get and manipulate data behind all Blockchain.info's charts.\n     *\n     * @param type of chart (Example: transactions-per-second, total-bitcoins)\n     * @param timeSpan (Example: 5weeks)\n     * @param rollingAverage (Example: 8hours)\n     * @return {@code Chart} represents the series of data of the chart\n     * @throws APIException If the server returns an error\n     */\n    public Chart getChart(String type, String timeSpan, String rollingAverage) throws APIException, IOException {\n        Map<String, String> params = new HashMap<String, String>();\n        params.put(\"format\", \"json\");\n        if (apiCode != null) {\n            params.put(\"api_code\", apiCode);\n        }\n        if (timeSpan != null) {\n            params.put(\"timespan\", timeSpan);\n        }\n        if (rollingAverage != null) {\n            params.put(\"rollingAverage\", rollingAverage);\n        }\n\n        String response = HttpClient.getInstance().get(\"charts/\" + type, params);\n        JsonObject chartJson = new JsonParser().parse(response).getAsJsonObject();\n\n        return new Chart(chartJson);\n    }\n\n    /**\n     * This method can be used to get the data behind Blockchain.info's pools information.\n     *\n     * @param timeSpan (Example: 5weeks)\n     * @return a map of pool name and the number of blocks it mined\n     * @throws APIException If the server returns an error\n     */\n    public Map<String, Integer> getPools(String timeSpan) throws APIException, IOException {\n        Map<String, String> params = new HashMap<String, String>();\n        params.put(\"format\", \"json\");\n        if (apiCode != null) {\n            params.put(\"api_code\", apiCode);\n        }\n        if (timeSpan != null) {\n            params.put(\"timespan\", timeSpan);\n        }\n\n        String response = HttpClient.getInstance().get(\"pools\", params);\n        Type type = new TypeToken<Map<String, String>>(){}.getType();\n        Gson gson = new Gson();\n        Map<String, Integer> pools = gson.fromJson(response, type);\n\n        return pools;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/statistics/StatisticsResponse.java",
    "content": "package info.blockchain.api.statistics;\n\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\n\nimport java.math.BigDecimal;\n\n/**\n * This class is used as a response object to the 'get' method in the 'Statistics' class\n */\npublic class StatisticsResponse {\n    private BigDecimal tradeVolumeBTC;\n    private BigDecimal tradeVolumeUSD;\n    private BigDecimal minersRevenueBTC;\n    private BigDecimal minersRevenueUSD;\n    private BigDecimal marketPriceUSD;\n    private BigDecimal estimatedTransactionVolumeUSD;\n    private long totalFeesBTC;\n    private long totalBTCSent;\n    private long estimatedBTCSent;\n    private long btcMined;\n    private double difficulty;\n    private double minutesBetweenBlocks;\n    private long numberOfTransactions;\n    private double hashRate;\n    private long timestamp;\n    private long minedBlocks;\n    private long blocksSize;\n    private long totalBTC;\n    private long totalBlocks;\n    private long nextRetarget;\n\n    public StatisticsResponse (String jsonString) {\n        JsonObject s = new JsonParser().parse(jsonString).getAsJsonObject();\n\n        this.tradeVolumeBTC = new BigDecimal(s.get(\"trade_volume_btc\").getAsString());\n        this.tradeVolumeUSD = new BigDecimal(s.get(\"trade_volume_usd\").getAsString());\n        this.minersRevenueBTC = new BigDecimal(s.get(\"miners_revenue_btc\").getAsString());\n        this.minersRevenueUSD = new BigDecimal(s.get(\"miners_revenue_usd\").getAsString());\n        this.marketPriceUSD = new BigDecimal(s.get(\"market_price_usd\").getAsString());\n        this.estimatedTransactionVolumeUSD = new BigDecimal(s.get(\"estimated_transaction_volume_usd\").getAsString());\n        this.totalFeesBTC = s.get(\"total_fees_btc\").getAsLong();\n        this.totalBTCSent = s.get(\"total_btc_sent\").getAsLong();\n        this.estimatedBTCSent = s.get(\"estimated_btc_sent\").getAsLong();\n        this.btcMined = s.get(\"n_btc_mined\").getAsLong();\n        this.difficulty = s.get(\"difficulty\").getAsDouble();\n        this.minutesBetweenBlocks = s.get(\"minutes_between_blocks\").getAsDouble();\n        this.numberOfTransactions = s.get(\"n_tx\").getAsLong();\n        this.hashRate = s.get(\"hash_rate\").getAsDouble();\n        this.timestamp = s.get(\"timestamp\").getAsLong();\n        this.minedBlocks = s.get(\"n_blocks_mined\").getAsLong();\n        this.blocksSize = s.get(\"blocks_size\").getAsLong();\n        this.totalBTC = s.get(\"totalbc\").getAsLong();\n        this.totalBlocks = s.get(\"n_blocks_total\").getAsLong();\n        this.nextRetarget = s.get(\"nextretarget\").getAsLong();\n    }\n\n    /**\n     * @return Trade volume in the past 24 hours\n     */\n    public BigDecimal getTradeVolumeBTC () {\n        return tradeVolumeBTC;\n    }\n\n    /**\n     * @return Trade volume in the past 24 hours\n     */\n    public BigDecimal getTradeVolumeUSD () {\n        return tradeVolumeUSD;\n    }\n\n    /**\n     * @return Miners' revenue in BTC\n     */\n    public BigDecimal getMinersRevenueBTC () {\n        return minersRevenueBTC;\n    }\n\n    /**\n     * @return Miners' revenue in USD\n     */\n    public BigDecimal getMinersRevenueUSD () {\n        return minersRevenueUSD;\n    }\n\n    /**\n     * @return Current market price in USD\n     */\n    public BigDecimal getMarketPriceUSD () {\n        return marketPriceUSD;\n    }\n\n    /**\n     * @return Estimated transaction volume in the past 24 hours\n     */\n    public BigDecimal getEstimatedTransactionVolumeUSD () {\n        return estimatedTransactionVolumeUSD;\n    }\n\n    /**\n     * @return Total fees in the past 24 hours (in satoshi)\n     */\n    public long getTotalFeesBTC () {\n        return totalFeesBTC;\n    }\n\n    /**\n     * @return Total BTC sent in the past 24 hours (in satoshi)\n     */\n    public long getTotalBTCSent () {\n        return totalBTCSent;\n    }\n\n    /**\n     * @return Estimated BTC sent in the past 24 hours (in satoshi)\n     */\n    public long getEstimatedBTCSent () {\n        return estimatedBTCSent;\n    }\n\n    /**\n     * @return Number of BTC mined in the past 24 hours (in satoshi)\n     */\n    public long getBTCMined () {\n        return btcMined;\n    }\n\n    /**\n     * @return Current difficulty\n     */\n    public double getDifficulty () {\n        return difficulty;\n    }\n\n    /**\n     * @return Minutes between blocks\n     */\n    public double getMinutesBetweenBlocks () {\n        return minutesBetweenBlocks;\n    }\n\n    /**\n     * @return Number of transactions in the past 24 hours\n     */\n    public long getNumberOfTransactions () {\n        return numberOfTransactions;\n    }\n\n    /**\n     * @return Current hashrate in GH/s\n     */\n    public double getHashRate () {\n        return hashRate;\n    }\n\n    /**\n     * @return Timestamp of when this report was compiled (in ms)\n     */\n    public long getTimestamp () {\n        return timestamp;\n    }\n\n    /**\n     * @return Number of blocks mined in the past 24 hours\n     */\n    public long getMinedBlocks () {\n        return minedBlocks;\n    }\n\n    /**\n     * @return the blocksSize\n     */\n    public long getBlocksSize () {\n        return blocksSize;\n    }\n\n    /**\n     * @return Total BTC in existence (in satoshi)\n     */\n    public long getTotalBTC () {\n        return totalBTC;\n    }\n\n    /**\n     * @return Total number of blocks in existence (in satoshi)\n     */\n    public long getTotalBlocks () {\n        return totalBlocks;\n    }\n\n    /**\n     * @return The next block height where the difficulty retarget will occur\n     */\n    public long getNextRetarget () {\n        return nextRetarget;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/wallet/Wallet.java",
    "content": "package info.blockchain.api.wallet;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\n\nimport info.blockchain.api.APIException;\nimport info.blockchain.api.HttpClient;\nimport info.blockchain.api.wallet.entity.Address;\nimport info.blockchain.api.wallet.entity.CreateWalletResponse;\nimport info.blockchain.api.wallet.entity.PaymentResponse;\n\n/**\n * This class reflects the functionality documented\n * at https://blockchain.info/api/blockchain_wallet_api. It allows users to interact\n * with their Blockchain.info wallet. If you have an API code, please set it via the\n * `setApiCode` method.\n */\npublic class Wallet {\n    private JsonParser jsonParser;\n\n    private String serviceURL;\n    private String identifier;\n    private String password;\n    private String secondPassword;\n    private String apiCode;\n\n    /**\n     * Creates a new Blockchain.info wallet.\n     *\n     * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash)\n     * @param password Password for the new wallet. At least 10 characters.\n     * @param apiCode  API code with create wallets permission\n     * @return An instance of the CreateWalletResponse class\n     * @throws APIException If the server returns an error\n     */\n    public static CreateWalletResponse create (String serviceURL, String password,\n                                               String apiCode) throws IOException, APIException {\n        return create(serviceURL, password, apiCode, null, null, null);\n    }\n\n    /**\n     * Creates a new Blockchain.info wallet. It can be created containing a pre-generated\n     * private key or will otherwise generate a new private key.\n     *\n     * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash)\n     * @param password   Password for the new wallet. At least 10 characters.\n     * @param apiCode    API code with create wallets permission\n     * @param privateKey Private key to add to the wallet (optional, nullable)\n     * @param label      Label for the first address in the wallet (optional, nullable)\n     * @param email      Email to associate with the new wallet (optional, nullable)\n     * @return An instance of the CreateWalletResponse class\n     * @throws APIException If the server returns an error\n     */\n    public static CreateWalletResponse create (String serviceURL, String password,String apiCode,\n                                               String privateKey, String label, String email) throws IOException, APIException {\n        Map<String, String> params = new HashMap<String, String>();\n\n        params.put(\"password\", password);\n        params.put(\"api_code\", apiCode);\n        if (privateKey != null) {\n            params.put(\"priv\", privateKey);\n        }\n        if (label != null) {\n            params.put(\"label\", label);\n        }\n        if (email != null) {\n            params.put(\"email\", email);\n        }\n\n        String response = HttpClient.getInstance().post(serviceURL, \"api/v2/create\", params);\n        JsonObject jsonObj = new JsonParser().parse(response).getAsJsonObject();\n\n        return new CreateWalletResponse(\n                jsonObj.get(\"guid\").getAsString(),\n                jsonObj.get(\"address\").getAsString(),\n                jsonObj.get(\"label\").getAsString());\n    }\n\n    /**\n     * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash)\n     * @param apiCode    API Code\n     * @param identifier Wallet identifier (GUID)\n     * @param password   Decryption password\n     */\n    public Wallet (String serviceURL, String apiCode, String identifier, String password) {\n        this(serviceURL, apiCode, identifier, password, null);\n    }\n\n    /**\n     * @param serviceURL URL to an instance of service-my-wallet-v3 (with trailing slash)\n     * @param apiCode    API Code\n     * @param identifier     Wallet identifier (GUID)\n     * @param password       Decryption password\n     * @param secondPassword Second password\n     */\n    public Wallet (String serviceURL, String apiCode, String identifier, String password, String secondPassword) {\n        this.serviceURL = serviceURL;\n        this.apiCode = apiCode;\n        this.identifier = identifier;\n        this.password = password;\n        this.secondPassword = secondPassword;\n        this.jsonParser = new JsonParser();\n    }\n\n    /**\n     * Sends bitcoin from your wallet to a single address.\n     *\n     * @param toAddress   Recipient bitcoin address\n     * @param amount      Amount to send (in satoshi)\n     * @param fromAddress Specific address to send from (optional, nullable)\n     * @param fee         Transaction fee in satoshi. Must be greater than the default fee (optional, nullable).\n     * @return An instance of the PaymentResponse class\n     * @throws APIException If the server returns an error\n     */\n    public PaymentResponse send (String toAddress, long amount, String fromAddress, Long fee) throws APIException, IOException {\n        Map<String, Long> recipient = new HashMap<String, Long>();\n        recipient.put(toAddress, amount);\n\n        return sendMany(recipient, fromAddress, fee);\n    }\n\n    /**\n     * Sends bitcoin from your wallet to multiple addresses.\n     *\n     * @param recipients  Map with the structure of 'address':amount in satoshi (String:long)\n     * @param fromAddress Specific address to send from (optional, nullable)\n     * @param fee         Transaction fee in satoshi. Must be greater than the default fee (optional, nullable).\n     * @return An instance of the PaymentResponse class\n     * @throws APIException If the server returns an error\n     * @throws IllegalArgumentException If no recipients is null or empty\n     */\n    public PaymentResponse sendMany (Map<String, Long> recipients, String fromAddress, Long fee) throws APIException, IOException {\n        Map<String, String> params = buildBasicRequestWithSecondPassword();\n        String method = null;\n\n       if (recipients.size() == 1) {\n            method = \"payment\";\n            Entry<String, Long> e = recipients.entrySet().iterator().next();\n            params.put(\"to\", e.getKey());\n            params.put(\"amount\", e.getValue().toString());\n        } else {\n            method = \"sendmany\";\n            params.put(\"recipients\", new Gson().toJson(recipients));\n        }\n\n        if (fromAddress != null) {\n            params.put(\"from\", fromAddress);\n        }\n        if (fee != null) {\n            params.put(\"fee\", fee.toString());\n        }\n\n        String response = HttpClient.getInstance().post(serviceURL, String.format(\"merchant/%s/%s\", identifier, method), params);\n        JsonObject topElem = parseResponse(response);\n\n        return new PaymentResponse(topElem.get(\"message\").getAsString(), topElem.get(\"tx_hash\").getAsString(), topElem.has(\"notice\") ? topElem.get(\"notice\").getAsString() : null);\n    }\n\n    /**\n     * Fetches the wallet balance. Includes unconfirmed transactions and\n     * possibly double spends.\n     *\n     * @return Wallet balance in satoshi\n     * @throws APIException If the server returns an error\n     */\n    public long getBalance () throws APIException, IOException {\n        String response = HttpClient.getInstance().get(serviceURL, String.format(\"merchant/%s/balance\", identifier), buildBasicRequest());\n        JsonObject topElem = parseResponse(response);\n\n        return topElem.get(\"balance\").getAsLong();\n    }\n\n    /**\n     * Lists all active addresses in the wallet.\n     *\n     * @return A list of Address objects\n     * @throws APIException If the server returns an error\n     */\n    public List<Address> listAddresses () throws APIException, IOException {\n        Map<String, String> params = buildBasicRequest();\n\n        String response = HttpClient.getInstance().get(serviceURL, String.format(\"merchant/%s/list\", identifier), params);\n        JsonObject topElem = parseResponse(response);\n\n        List<Address> addresses = new ArrayList<Address>();\n        for (JsonElement jAddr : topElem.get(\"addresses\").getAsJsonArray()) {\n            JsonObject a = jAddr.getAsJsonObject();\n            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());\n\n            addresses.add(address);\n        }\n\n        return addresses;\n    }\n\n    /**\n     * Retrieves an address from the wallet.\n     *\n     * @param address       Address in the wallet to look up\n     * @return An instance of the Address class\n     * @throws APIException If the server returns an error\n     */\n    public Address getAddress (String address) throws APIException, IOException {\n        Map<String, String> params = buildBasicRequest();\n        params.put(\"address\", address);\n\n        String response = HttpClient.getInstance().get(serviceURL, String.format(\"merchant/%s/address_balance\", identifier), params);\n        JsonObject topElem = parseResponse(response);\n\n        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());\n    }\n\n    /**\n     * Generates a new address and adds it to the wallet.\n     *\n     * @param label Label to attach to this address (optional, nullable)\n     * @return An instance of the Address class\n     * @throws APIException If the server returns an error\n     */\n    public Address newAddress (String label) throws APIException, IOException {\n        Map<String, String> params = buildBasicRequest();\n        if (label != null) {\n            params.put(\"label\", label);\n        }\n\n        String response = HttpClient.getInstance().post(serviceURL, String.format(\"merchant/%s/new_address\", identifier), params);\n        JsonObject topElem = parseResponse(response);\n\n        return new Address(0L, topElem.get(\"address\").getAsString(), topElem.has(\"label\") && !topElem.get(\"label\").isJsonNull() ? topElem.get(\"label\").getAsString() : null, 0L);\n    }\n\n    /**\n     * Archives an address.\n     *\n     * @param address Address to archive\n     * @return String representation of the archived address\n     * @throws APIException If the server returns an error\n     */\n    public String archiveAddress (String address) throws APIException, IOException {\n        Map<String, String> params = buildBasicRequest();\n        params.put(\"address\", address);\n\n        String response = HttpClient.getInstance().post(serviceURL, String.format(\"merchant/%s/archive_address\", identifier), params);\n        JsonObject topElem = parseResponse(response);\n\n        return topElem.get(\"archived\").getAsString();\n    }\n\n    /**\n     * Unarchives an address.\n     *\n     * @param address Address to unarchive\n     * @return String representation of the unarchived address\n     * @throws APIException If the server returns an error\n     */\n    public String unarchiveAddress (String address) throws APIException, IOException {\n        Map<String, String> params = buildBasicRequest();\n        params.put(\"address\", address);\n\n        String response = HttpClient.getInstance().post(serviceURL, String.format(\"merchant/%s/unarchive_address\", identifier), params);\n        JsonObject topElem = parseResponse(response);\n\n        return topElem.get(\"active\").getAsString();\n    }\n\n    private Map<String, String> buildBasicRequest () {\n        Map<String, String> params = new HashMap<String, String>();\n\n        params.put(\"password\", password);\n        if (apiCode != null) {\n            params.put(\"api_code\", apiCode);\n        }\n\n        return params;\n    }\n\n    private Map<String, String> buildBasicRequestWithSecondPassword () {\n        Map<String, String> params = buildBasicRequest();\n        if (secondPassword != null) {\n            params.put(\"second_password\", secondPassword);\n        }\n\n        return params;\n    }\n\n    private JsonObject parseResponse (String response) throws APIException {\n        JsonObject topElem = jsonParser.parse(response).getAsJsonObject();\n        if (topElem.has(\"error\")) {\n            throw new APIException(topElem.get(\"error\").getAsString());\n        }\n\n        return topElem;\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        Wallet wallet = (Wallet) o;\n\n        return identifier == null ? wallet.identifier == null : identifier.equals(wallet.identifier);\n    }\n\n    @Override\n    public int hashCode () {\n        return identifier != null ? identifier.hashCode() : 0;\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/wallet/entity/Address.java",
    "content": "package info.blockchain.api.wallet.entity;\n\n/**\n * Used in combination with the `Wallet` class\n */\npublic class Address {\n    private long balance;\n    private String address;\n    private String label;\n    private long totalReceived;\n\n    public Address (long balance, String address, String label, long totalReceived) {\n        this.balance = balance;\n        this.address = address;\n        this.label = label;\n        this.totalReceived = totalReceived;\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (this == o) {\n            return true;\n        }\n        if (o == null || getClass() != o.getClass()) {\n            return false;\n        }\n\n        Address address1 = (Address) o;\n\n        if (balance != address1.balance) {\n            return false;\n        }\n        if (totalReceived != address1.totalReceived) {\n            return false;\n        }\n        if (address != null ? !address.equals(address1.address) : address1.address != null) {\n            return false;\n        }\n        return !(label != null ? !label.equals(address1.label) : address1.label != null);\n\n    }\n\n    @Override\n    public int hashCode () {\n        int result = (int) (balance ^ (balance >>> 32));\n        result = 31 * result + (address != null ? address.hashCode() : 0);\n        result = 31 * result + (label != null ? label.hashCode() : 0);\n        result = 31 * result + (int) (totalReceived ^ (totalReceived >>> 32));\n        return result;\n    }\n\n    /**\n     * @return Balance in satoshi\n     */\n    public long getBalance () {\n        return balance;\n    }\n\n    /**\n     * @return String representation of the address\n     */\n    public String getAddress () {\n        return address;\n    }\n\n    /**\n     * @return Label attached to the address\n     */\n    public String getLabel () {\n        return label;\n    }\n\n    /**\n     * @return Total received amount in satoshi\n     */\n    public long getTotalReceived () {\n        return totalReceived;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/wallet/entity/CreateWalletResponse.java",
    "content": "package info.blockchain.api.wallet.entity;\n\n/**\n * Used in response to the `create` method in the `CreateWallet` class.\n */\npublic class CreateWalletResponse {\n    private String identifier;\n    private String address;\n    private String label;\n\n    public CreateWalletResponse (String identifier, String address, String label) {\n        this.identifier = identifier;\n        this.address = address;\n        this.label = label;\n    }\n\n    /**\n     * @return Wallet identifier (GUID)\n     */\n    public String getIdentifier () {\n        return identifier;\n    }\n\n    /**\n     * @return First address in the wallet\n     */\n    public String getAddress () {\n        return address;\n    }\n\n    /**\n     * @return Label of first address in the wallet\n     */\n    public String getLable () {\n        return label;\n    }\n}\n"
  },
  {
    "path": "src/main/java/info/blockchain/api/wallet/entity/PaymentResponse.java",
    "content": "package info.blockchain.api.wallet.entity;\n\n/**\n * Used in response to the `send` and `sendMany` methods in the `Wallet` class.\n */\npublic class PaymentResponse {\n    private String message;\n    private String txHash;\n    private String notice;\n\n    public PaymentResponse (String message, String txHash, String notice) {\n        this.message = message;\n        this.txHash = txHash;\n        this.notice = notice;\n    }\n\n    @Override\n    public boolean equals (Object o) {\n        if (o == null) {\n            return false;\n        }\n        if (o instanceof PaymentResponse) {\n            PaymentResponse that = (PaymentResponse) o;\n            return (this.getMessage().equals(that.getMessage()) && this.getTxHash().equals(that.getTxHash()) && this.getNotice().equals(that.getNotice()));\n        }\n        return false;\n    }\n\n    /**\n     * @return Response message from the server\n     */\n    public String getMessage () {\n        return message;\n    }\n\n    /**\n     * @return Transaction hash\n     */\n    public String getTxHash () {\n        return txHash;\n    }\n\n    /**\n     * @return Additional response message from the server\n     */\n    public String getNotice () {\n        return notice;\n    }\n\n}\n"
  },
  {
    "path": "src/test/java/info/blockchain/api/AppTest.java",
    "content": "package info.blockchain.api;\n\nimport junit.framework.Test;\nimport junit.framework.TestCase;\nimport junit.framework.TestSuite;\n\n/**\n * Unit test for simple App.\n */\npublic class AppTest extends TestCase {\n    /**\n     * Create the test case\n     *\n     * @param testName name of the test case\n     */\n    public AppTest (String testName) {\n        super(testName);\n    }\n\n    /**\n     * @return the suite of tests being tested\n     */\n    public static Test suite () {\n        return new TestSuite(AppTest.class);\n    }\n\n    /**\n     * Rigourous Test :-)\n     */\n    public void testApp () {\n        assertTrue(true);\n    }\n}\n"
  },
  {
    "path": "src/test/java/info/blockchain/api/blockexplorer/BlockExplorerTest.java",
    "content": "package info.blockchain.api.blockexplorer;\n\nimport info.blockchain.api.blockexplorer.entity.*;\nimport org.junit.Assert;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport static org.junit.Assert.*;\n\n/**\n * Created by ray on 10/05/2017.\n */\npublic class BlockExplorerTest {\n\n    BlockExplorer client;\n\n    @Before\n    public void setUp () throws Exception {\n        client = new BlockExplorer();\n    }\n\n    @Test\n    public void getAddress () throws Exception {\n        Address address = client.getAddress(\"1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW\", FilterType.All, 10, null);\n\n        assertEquals(\"1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW\", address.getAddress());\n        assertEquals(\"07feead7f9fb7d16a0251421ac9fa090169cc169\",\n                address.getHash160());\n        assertEquals(0, address.getFinalBalance());\n        assertEquals(16, address.getTxCount());\n        assertEquals(605204 , address.getTotalReceived());\n        assertEquals(605204 , address.getTotalSent());\n        assertEquals(10, address.getTransactions().size());\n    }\n\n    @Test\n    public void getUnspentOutputs () throws Exception {\n        String address1 = \"1FrWWFJ95Jq7EDgpkeBwVLAtoJMPwmYS7T\";\n        String address2 = \"xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn\";\n        List<UnspentOutput> unspentOutputs = client.getUnspentOutputs(Arrays.asList(address1, address2), 6, 10);\n\n        assertTrue(unspentOutputs != null && unspentOutputs.size() != 0);\n        assertEquals(\"2e7ab41818ee0ab987d393d4c8bf5e436b6e8c15ef3535a2b3eac581e33c7472\", unspentOutputs.get(0).getTransactionHash());\n        assertEquals(20000, unspentOutputs.get(0).getValue());\n    }\n\n    @Test\n    public void getBalance () throws Exception {\n        String address1 = \"1jH7K4RJrQBXijtLj1JpzqPRhR7MdFtaW\";\n        String address2 = \"xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn\";\n\n        List<String> list = Arrays.asList(address1, address2);\n\n        Map<String, Balance> balances = client.getBalance(list, FilterType.All);\n\n        assertEquals(0, balances.get(address1).getFinalBalance());\n        assertEquals(16, balances.get(address1).getTxCount());\n        assertEquals(605204, balances.get(address1).getTotalReceived());\n        assertEquals(20000, balances.get(address2).getFinalBalance());\n        assertEquals(1, balances.get(address2).getTxCount());\n        assertEquals(20000, balances.get(address2).getTotalReceived());\n    }\n\n    @Test\n    public void getXpub () throws Exception {\n        String address = \"xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn\";\n        XpubFull xpub = client.getXpub(address, null, null, null);\n\n        assertEquals(xpub.getAddress(),\n                \"xpub6CmZamQcHw2TPtbGmJNEvRgfhLwitarvzFn3fBYEEkFTqztus7W7CNbf48Kxuj1bRRBmZPzQocB6qar9ay6buVkQk73ftKE1z4tt9cPHWRn\");\n        assertEquals(1, xpub.getTxCount());\n        assertEquals(20000, xpub.getTotalReceived());\n        assertEquals(0, xpub.getTotalSent());\n        assertEquals(20000, xpub.getFinalBalance());\n        assertEquals(0, xpub.getChangeIndex());\n        assertEquals(1, xpub.getAccountIndex());\n        assertEquals(0, xpub.getGapLimit());\n    }\n\n}"
  }
]