package Finance::Dogecoin::API;
$Finance::Dogecoin::API::VERSION = '1.20140201.1608';
use 5.010;

use Moo;
# limit the damage from Moo's use of strictures
# see:
use warnings NONFATAL => 'all';

use Carp ();

use URI;
use JSON;
use HTTP::Headers;
use LWP::UserAgent;
use LWP::Protocol::https;
use URI::QueryParam;

has 'api_key',  is => 'ro', required => 1;
has 'endpoint', is => 'ro', default  => sub { '' };
has 'json',     is => 'ro', default  => sub {
    my $j = JSON->new; $j->allow_nonref; $j

has 'ua',       is => 'ro', default => sub {
    my $headers = HTTP::Headers->new;
    $headers->header( 'Content-Type' => 'application/json' );
        ssl_opts        => { verify_hostname => 1 },
        default_headers => $headers,

sub request {
    my ($self, $method, %params) = @_;

    # manually setting the 'a' parameter avoids a weird behavior in LWP::UA
    # which uppercases 'a'--not what the API expects or wants
    my $uri = URI->new( $self->endpoint );
    $uri->query_param( a => $method );

    while (my ($key, $value) = each %params) {
        $uri->query_param( $key => $value );

    my $response = $self->ua->get( $uri );
    my $result   = $self->json->decode( $response->decoded_content );

    Carp::croak( "Bad API call from $method() call" ) if $result eq 'Bad Query';
    Carp::croak( "Invalid API key '" . $self->api_key . "'" )
        if $result eq 'Invalid API Key';

    return $result;

    for my $non_api (qw( get_difficulty get_current_block get_current_price )) {
        my $method = sub { $_[0]->request( $non_api ) };
        do {
            no strict 'refs';
            *{ $non_api } = $method;

    for my $api (qw( get_balance get_my_addresses )) {
        my $method = sub { $_[0]->request( $api, api_key => $_[0]->api_key ) };
        do {
            no strict 'refs';
            *{ $api } = $method;

sub withdraw {
    my ($self, %params) = @_;

    my @errors;

    for my $param (qw( payment_address amount )) {
        push @errors, $param unless $params{$param};

    if (@errors) {
        my $error = join ', ', @errors;
        Carp::croak( "Must call withdraw() with $error params" );

    Carp::croak( 'Must call withdraw() with amount of at least 5 Doge' )
        if $params{amount} < 5;

    $self->request( 'withdraw', api_key => $self->api_key, %params );

sub get_new_address {
    my ($self, %params) = @_;
    $self->request( 'get_new_address', api_key => $self->api_key, %params );

sub get_address_received {
    my ($self, %params)        = @_;
    $params{payment_address} //= $params{address_label};

    Carp::croak( 'Must call get_address_received() with payment_address param' )
        unless $params{payment_address};

    $self->request( 'get_address_received',
        api_key => $self->api_key, %params

sub get_address_by_label {
    my ($self, %params) = @_;

    Carp::croak( 'Must call get_address_by_label() with address_label param' )
        unless $params{address_label};

    $self->request( 'get_address_by_label',
        api_key => $self->api_key, %params

'to the moon';

=head1 NAME

Finance::Dogecoin::API - use the API from Perl


    use Finance::Dogecoin::API;

    # may throw errors
    eval {
        my $dc      = Finance::Dogecoin::API->new( api_key => $SECRET_KEY );
        my $block   = $dc->get_current_block;
        my $price   = $dc->get_current_price;

        my $balance = $dc->get_balance;
        my $result  = $dc->withdraw(
            payment_address => $ADDY, amount => $AMOUNT

    # tip the author
        payment_address => 'DPxuFc7dhNrTvNMCE53ENGF5g7LSGrzyYs',
        amount          => 5,


C<Finance::Dogecoin::API> provides an OO interface to the Dogecoin API provided
by L<>. You need to sign up for an API key to use most of
the methods in this class; do so at the site. When creating the object, you
must provide the C<api_key> as a constructor argument.

=head1 METHODS

This module provides several methods. See the documentation at
L<> for current details. These methods
may I<all> throw an exception if a network error or protocol error occurs, so
be ready to catch them:

=head2 get_current_price()

Returns the current price of Dogecoins in US dollars. This is a floating point

=head2 get_current_block()

Returns the current block of Doge mining.

=head2 get_difficulty()

Returns the current difficulty of Doge mining.

=head2 get_balance()

Returns the balance of your entire account across all wallets. This is a
floating point number.

=head2 get_my_addresses()

Returns an array reference of all payment addresses associated with your
account. This array may be empty.

=head2 withdraw( payment_address => $address, amount => $amount )

Withdraws C<$amount> from your account and sends it to <$address>. The API and
the Doge network each charge a modest transaction fee. The transaction will
fail unless your account meets these criteria. In particular, you must transfer
at least 5 Doge at a time.

If you do not provide I<both> the C<payment_address> and C<amount> parameters,
this method will throw an exception.

=head2 get_new_address( address_label => $label )

Creates and returns a new payment address for your account. You may provide an
I<optional> C<address_label> parameter. The API will use this alphanumeric
value as the label if possible.

=head2 get_address_received( payment_address => $address )

Returns the current amount of Dogecoins recieved at the given address or label.
This method will throw an exception if you do not provide either the
C<payment_address> or C<address_label> parameters. This method will return the
number C<0> if you provide an invalid address or label.

=head2 get_address_by_label( address_label => $label )

Returns the payment address for the given address label. This method will throw
an exception if you do not provide the C<address_label> parameter. This method
will return the string C<No matching addresses> if there are no matching

=head1 CAVEATS

The Dogecoin API is under development, so these methods might change and new
methods might appear.


Copyright 2014 chromatic, some rights reserved.

This program is free software. You can redistribute it and/or modify it under
the same terms as Perl 5.18.