Finance-CoinbasePro-Lite

 view release on metacpan or  search on metacpan

lib/Finance/CoinbasePro/Lite.pm  view on Meta::CPAN

package Finance::CoinbasePro::Lite;

our $DATE = '2018-11-29'; # DATE
our $VERSION = '0.004'; # VERSION

use 5.010001;
use strict;
use warnings;
use Log::ger;

use Digest::SHA qw(hmac_sha256_base64);
use MIME::Base64 qw(decode_base64);
use Time::HiRes qw(time);

my $url_prefix = "https://api.pro.coinbase.com";

sub new {
    my ($class, %args) = @_;

    my $self = {};
    if (my $key = delete $args{key}) {
        $self->{key} = $key;
    }
    if (my $secret = delete $args{secret}) {
        $self->{secret} = $secret;
    }
    if (my $passphrase = delete $args{passphrase}) {
        $self->{passphrase} = $passphrase;
    }
    if (keys %args) {
        die "Unknown argument(s): ".join(", ", sort keys %args);
    }

    require HTTP::Tiny;
    $self->{_http} = HTTP::Tiny->new;

    require JSON::XS;
    $self->{_json} = JSON::XS->new;

    require URI::Encode;
    $self->{_urienc} = URI::Encode->new;

    bless $self, $class;
}

sub _get_json {
    my ($self, $url) = @_;

    log_trace("JSON API request: %s", $url);

    my $res = $self->{_http}->get($url);
    die "Can't retrieve $url: $res->{status} - $res->{reason}"
        unless $res->{success};
    my $decoded;
    eval { $decoded = $self->{_json}->decode($res->{content}) };
    die "Can't decode response from $url: $@" if $@;

    log_trace("JSON API response: %s", $decoded);

    $decoded;
}

sub _request {
    my ($self, $is_private, $method, $request_path, $params) = @_;

    $params //= {};

    if ($is_private) {
        $self->{key} or die "Please supply API key in new()";
        $self->{secret} or die "Please supply API secret in new()";
        $self->{passphrase} or die "Please supply API passphrase in new()";
    }

    my $time = $ENV{FINANCE_COINBASEPRO_LITE_DEBUG_TIME} // sprintf "%.3f", time();
    log_trace("API %s request [%s]: %s %s %s",
              $is_private ? "private" : "public",
              $time, $method, $request_path, $params);

    my $url = "$url_prefix$request_path";

    my $body;
    my $encoded_body = '';
    if ($method eq 'POST') {
        $body = $encoded_body = $self->{_json}->encode($params);
    } else {
        if (keys %$params) {
            my $qs = '?' . join(
                "&",
                map { $self->{_urienc}->encode($_ // ''). "=" .
                          $self->{_urienc}->encode($params->{$_} // '') }
                    sort keys(%$params),
            );
            $url .= $qs;
            $encoded_body = $qs;
        }
    }

    my $signature;
    if ($is_private) {
        my $what = $time . $method . $request_path . $encoded_body;
        $signature = hmac_sha256_base64($what, decode_base64($self->{secret}));
        while (length($signature) % 4) { $signature .= '=' }
    }

    my $options = {
        headers => {
            ("CB-ACCESS-KEY"  => $self->{key}) x !!$is_private,
            ("CB-ACCESS-SIGN" => $signature  ) x !!$is_private,
            ("CB-ACCESS-TIMESTAMP" => $time  ) x !!$is_private,
            ("CB-ACCESS-PASSPHRASE" => $self->{passphrase}) x !!$is_private,,
            "Content-Type"   => "application/json",
            "Accept"         => "application/json",
        },
        (content => $body) x !!defined($body),
    };

    my $res = $self->{_http}->request($method, $url, $options);

    if ($res->{headers}{'content-type'} =~ m!application/json!) {
        $res->{content} = $self->{_json}->decode($res->{content});



( run in 1.323 second using v1.01-cache-2.11-cpan-39bf76dae61 )