gtmtech-crypto 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +20 -0
- data/README.md +90 -0
- data/Rakefile +1 -0
- data/bin/crypto +20 -0
- data/build.sh +13 -0
- data/gtmtech-crypto.gemspec +19 -0
- data/lib/gtmtech/crypto.rb +9 -0
- data/lib/gtmtech/crypto/CLI.rb +43 -0
- data/lib/gtmtech/crypto/data.rb +88 -0
- data/lib/gtmtech/crypto/subcommand.rb +111 -0
- data/lib/gtmtech/crypto/subcommands/account.rb +79 -0
- data/lib/gtmtech/crypto/subcommands/help.rb +41 -0
- data/lib/gtmtech/crypto/subcommands/txn.rb +127 -0
- data/lib/gtmtech/crypto/subcommands/unknown_command.rb +21 -0
- data/lib/gtmtech/crypto/subcommands/version.rb +26 -0
- data/lib/gtmtech/crypto/utils.rb +63 -0
- metadata +77 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 262dc2d5d049a25d80227742c81eccc6307ac364
|
4
|
+
data.tar.gz: 67f33bbf9667ac1f4e3d4fe2c4aa4b49aa3f4e58
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f0fda99a14c4ec5c039a4f7edeb3d9db64e44b31256461c47e94f5cf56ea617329cfbea447678cbd5400ae5117a0a7c9342db2b46f12c7963b83c1b57c21d031
|
7
|
+
data.tar.gz: a817e4aa4ad4c30c26224d659c74f5724f4a206946017caf0cf996b5d8afb2193f240b121162e9780c01d4d245f200c55cfcb23f4ebc247cfd47e91f62f66223
|
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Geoff Meakin
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
crypto
|
2
|
+
======
|
3
|
+
|
4
|
+
crypto is a utiity for keeping track of all your cryptocurrency accounts. It aims to do the following
|
5
|
+
|
6
|
+
* Log transactions
|
7
|
+
* Provide account statements
|
8
|
+
* Provide detailed transaction history
|
9
|
+
|
10
|
+
in order that:
|
11
|
+
|
12
|
+
* You understand where your money is
|
13
|
+
* You can provide records for tax purposes
|
14
|
+
|
15
|
+
|
16
|
+
Setup:
|
17
|
+
======
|
18
|
+
|
19
|
+
First install ruby/rubygems. Then install this
|
20
|
+
|
21
|
+
```
|
22
|
+
gem install gtmtech-crypto
|
23
|
+
```
|
24
|
+
|
25
|
+
|
26
|
+
Usage:
|
27
|
+
======
|
28
|
+
|
29
|
+
Firstly set up some accounts
|
30
|
+
|
31
|
+
```
|
32
|
+
$ crypto new account --name barclays --currencies GBP --type bank
|
33
|
+
$ crypto new account --name kraken --currencies GBP,BTC --type exchange
|
34
|
+
$ crypto new account --name ledger --currencies BTC,ETH --type wallet
|
35
|
+
```
|
36
|
+
|
37
|
+
Then trade
|
38
|
+
|
39
|
+
```
|
40
|
+
$ crypto new txn --from barclays.GBP=200 --to bitbargain.GBP
|
41
|
+
$ crypto new txn --from kraken.GBP=150 --to kraken.BTC=0.08
|
42
|
+
```
|
43
|
+
|
44
|
+
some transactions have fees associated with them
|
45
|
+
|
46
|
+
```
|
47
|
+
$ crypto new txn --from kraken.BTC=0.08 --to ledger.BTC=0.078 # implicit fees of 0.0002 BTC
|
48
|
+
$ crypto new txn --from kraken.BTC=0.08 --to ledger.BTC --fees.BTC=0.002 # extra fees paid at source (kraken)
|
49
|
+
```
|
50
|
+
|
51
|
+
Output transaction history
|
52
|
+
|
53
|
+
```
|
54
|
+
$ crypto list txn # all transactions
|
55
|
+
$ crypto list txn --account barclays # all barclays transactions
|
56
|
+
$ crypto list txn --currency BTC # all bitcoin transactions
|
57
|
+
```
|
58
|
+
|
59
|
+
Output pnl sheet
|
60
|
+
|
61
|
+
```
|
62
|
+
$ crypto list pnl # profit-and-loss
|
63
|
+
```
|
64
|
+
|
65
|
+
|
66
|
+
Building
|
67
|
+
========
|
68
|
+
|
69
|
+
Install chruby and ruby-build
|
70
|
+
|
71
|
+
* OSX: https://gist.github.com/andrewroycarter/6815905
|
72
|
+
|
73
|
+
Install version 2.0.0 of ruby using chruby
|
74
|
+
|
75
|
+
```
|
76
|
+
$ ruby-install ruby 2.0.0
|
77
|
+
or $ ruby-build ruby 2.0.0
|
78
|
+
```
|
79
|
+
|
80
|
+
Build the gem
|
81
|
+
|
82
|
+
```
|
83
|
+
$ ./build.sh
|
84
|
+
```
|
85
|
+
|
86
|
+
Disclaimer
|
87
|
+
==========
|
88
|
+
|
89
|
+
Please see LICENSE.TXT for usage of this software.
|
90
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/crypto
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'gtmtech/crypto/CLI'
|
5
|
+
|
6
|
+
begin
|
7
|
+
Gtmtech::Crypto::CLI.parse
|
8
|
+
rescue StandardError => e
|
9
|
+
puts e.message
|
10
|
+
puts e.backtrace.join("\n")
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
Gtmtech::Crypto::CLI.execute
|
16
|
+
rescue StandardError => e
|
17
|
+
puts e.message
|
18
|
+
puts e.backtrace.join("\n")
|
19
|
+
exit 1
|
20
|
+
end
|
data/build.sh
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -x -e -o pipefail
|
4
|
+
|
5
|
+
gem uninstall gtmtech-crypto --executables
|
6
|
+
RAKE_OUT=$(rake build)
|
7
|
+
echo "${RAKE_OUT}"
|
8
|
+
|
9
|
+
VERSION=$(echo "${RAKE_OUT}" | awk '{print $2}')
|
10
|
+
echo "Installing version: ${VERSION} ..."
|
11
|
+
|
12
|
+
gem install pkg/gtmtech-crypto-${VERSION}.gem
|
13
|
+
crypto version
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = "gtmtech-crypto"
|
7
|
+
gem.version = "0.0.1"
|
8
|
+
gem.description = "Simple tool for accounting of cryptocurrencies"
|
9
|
+
gem.summary = "Simple tool for accounting of cryptocurrencies"
|
10
|
+
gem.author = "Geoff Meakin"
|
11
|
+
gem.license = "MIT"
|
12
|
+
|
13
|
+
gem.homepage = "http://github.com/gtmtechltd/crypto"
|
14
|
+
gem.files = `git ls-files`.split($/).reject { |file| file =~ /^features.*$/ }
|
15
|
+
gem.executables << "crypto"
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.add_dependency('trollop', '~> 2.0')
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'gtmtech/crypto/utils'
|
2
|
+
require 'gtmtech/crypto/subcommand'
|
3
|
+
|
4
|
+
module Gtmtech
|
5
|
+
module Crypto
|
6
|
+
class CLI
|
7
|
+
|
8
|
+
attr_reader :subcommands
|
9
|
+
|
10
|
+
def self.parse
|
11
|
+
|
12
|
+
Utils.require_dir 'gtmtech/crypto/subcommands'
|
13
|
+
@@subcommands = Utils.find_all_subclasses_of({ :parent_class => Gtmtech::Crypto::Subcommands }).collect {|classname| Utils.snakecase classname}
|
14
|
+
|
15
|
+
subcommand = ARGV.shift
|
16
|
+
subcommand = case subcommand
|
17
|
+
when nil
|
18
|
+
ARGV.delete_if {true}
|
19
|
+
"unknown_command"
|
20
|
+
when /^\-/
|
21
|
+
ARGV.delete_if {true}
|
22
|
+
"help"
|
23
|
+
else
|
24
|
+
subcommand
|
25
|
+
end
|
26
|
+
|
27
|
+
@@command_class = Subcommand.find subcommand
|
28
|
+
@@options = @@command_class.parse
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.execute
|
33
|
+
@@command_class.execute
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.subcommands
|
37
|
+
@@subcommands
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'gtmtech/crypto/utils'
|
3
|
+
|
4
|
+
module Gtmtech
|
5
|
+
module Crypto
|
6
|
+
class Data
|
7
|
+
|
8
|
+
def self.load
|
9
|
+
@@path = "#{ENV['HOME']}/.crypto.yaml"
|
10
|
+
unless File.exist? @@path
|
11
|
+
File.open(@@path, 'w') do |file|
|
12
|
+
file.write("---\naccounts: {}\ntransactions: []\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
@@document = YAML.load(File.read(@@path))
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.add_account name, currency, type, url
|
19
|
+
if @@document[ "accounts" ].key? name
|
20
|
+
currencies = @@document[ "accounts" ][ name ][ "currencies" ]
|
21
|
+
currencies << currency
|
22
|
+
@@document[ "accounts" ][ name ][ "currencies" ] = currencies.uniq.sort
|
23
|
+
@@document[ "accounts" ][ name ][ "type" ] = type if type
|
24
|
+
@@document[ "accounts" ][ name ][ "url" ] = url if url
|
25
|
+
else
|
26
|
+
@@document[ "accounts" ][ name ] = { "currencies" => [ currency ], "type" => type, "url" => url }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.list_accounts
|
31
|
+
printf("%-10s %-10s %-60s\n", "name", "type", "currencies")
|
32
|
+
puts "-" * 80
|
33
|
+
@@document[ "accounts" ].each do |name, account_info|
|
34
|
+
printf("%-10s %-10s %-60s\n", name, account_info[ "type" ], account_info[ "currencies" ].join(","))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.account_exists? name, currency
|
39
|
+
return false unless @@document[ "accounts" ].key? name
|
40
|
+
return false unless @@document[ "accounts" ][ name ][ "currencies" ].include? currency
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.add_transaction date, source_account, source_currency, source_amount, dest_account, dest_currency, dest_amount, fees_account, fees_currency, fees_amount
|
45
|
+
if date.downcase == "now"
|
46
|
+
date = Time.now.to_s.split(" ").take(2).join(",")
|
47
|
+
end
|
48
|
+
|
49
|
+
source_amount = Utils.make_decimal source_amount
|
50
|
+
dest_amount = Utils.make_decimal dest_amount
|
51
|
+
|
52
|
+
id = SecureRandom.uuid.to_s
|
53
|
+
@@document[ "transactions" ][ id ] = { "date" => date,
|
54
|
+
"source_account" => source_account,
|
55
|
+
"source_currency" => source_currency,
|
56
|
+
"source_amount" => source_amount,
|
57
|
+
"dest_account" => dest_account,
|
58
|
+
"dest_currency" => dest_currency,
|
59
|
+
"dest_amount" => dest_amount,
|
60
|
+
"fees_account" => fees_account,
|
61
|
+
"fees_currency" => fees_currency,
|
62
|
+
"fees_amount" => fees_amount }
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.list_transactions
|
66
|
+
printf("%-36s %-20s %-15s %-15s %-15s %-15s\n", "id", "date", "src account", "src amount", "dest account", "dest amount")
|
67
|
+
puts "-" * 120
|
68
|
+
@@document[ "transactions" ].each do |id, txn|
|
69
|
+
printf("%-36s %-20s %-15s %-15s %-15s %-15s\n", id, txn["date"], "#{txn["source_account"]}.#{txn["source_currency"]}", txn["source_amount"], "#{txn["dest_account"]}.#{txn["dest_currency"]}", txn["dest_amount"])
|
70
|
+
if txn["fees_account"]
|
71
|
+
printf("%-36s %-20s %-15s %-15s %-15s %-15s\n", id, txn["date"], "#{txn["fees_account"]}.#{txn["fees_currency"]}", txn["fees_amount"], "fees.#{txn["fees_currency"]}", txn["fees_amount"])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.delete_transaction id
|
77
|
+
@@document[ "transactions" ].delete( id )
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.save
|
81
|
+
File.open(@@path, 'w') do |file|
|
82
|
+
file.write( @@document.to_yaml )
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'yaml'
|
3
|
+
require 'trollop'
|
4
|
+
# require 'gtmtech/crypto/subcommands/unknown_command'
|
5
|
+
require 'gtmtech/crypto'
|
6
|
+
|
7
|
+
module Gtmtech
|
8
|
+
module Crypto
|
9
|
+
|
10
|
+
class Subcommand
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :global_options, :options, :helptext
|
14
|
+
end
|
15
|
+
|
16
|
+
@@global_options = [
|
17
|
+
{:name => :version,
|
18
|
+
:description => "Show version information"},
|
19
|
+
{:name => :help,
|
20
|
+
:description => "Information on how to use this command",
|
21
|
+
:short => 'h'}
|
22
|
+
]
|
23
|
+
|
24
|
+
def self.all_options
|
25
|
+
options = @@global_options.dup
|
26
|
+
options += self.options if self.options
|
27
|
+
{ :options => options }
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.find commandname = "unknown_command"
|
31
|
+
begin
|
32
|
+
require "gtmtech/crypto/subcommands/#{commandname.downcase}"
|
33
|
+
rescue Exception => e
|
34
|
+
require "gtmtech/crypto/subcommands/unknown_command"
|
35
|
+
return Gtmtech::Crypto::Subcommands::UnknownCommand
|
36
|
+
end
|
37
|
+
command_module = Module.const_get('Gtmtech').const_get('Crypto').const_get('Subcommands')
|
38
|
+
command_class = Utils.find_closest_class :parent_class => command_module, :class_name => commandname
|
39
|
+
if command_class
|
40
|
+
command_class
|
41
|
+
else
|
42
|
+
require "gtmtech/crypto/subcommands/unknown_command"
|
43
|
+
Gtmtech::Crypto::Subcommands::UnknownCommand
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.parse
|
48
|
+
|
49
|
+
me = self
|
50
|
+
all = self.all_options
|
51
|
+
|
52
|
+
@@options = Trollop::options do
|
53
|
+
|
54
|
+
version "gtmtech-crypto version " + Gtmtech::Crypto::VERSION.to_s
|
55
|
+
banner "#{me.usage}\n\n"
|
56
|
+
|
57
|
+
all[:options].each do |available_option|
|
58
|
+
|
59
|
+
skeleton = {:description => "",
|
60
|
+
:short => :none}
|
61
|
+
|
62
|
+
skeleton.merge! available_option
|
63
|
+
opt skeleton[:name],
|
64
|
+
skeleton[:desc] || skeleton[:description], #legacy plugins
|
65
|
+
:short => skeleton[:short],
|
66
|
+
:default => skeleton[:default],
|
67
|
+
:type => skeleton[:type]
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.validate args
|
76
|
+
args
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.usage
|
80
|
+
"Usage: \ncrypto #{self.prettyname} [global-options] [options]\nDescription: #{self.description}"
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.description
|
84
|
+
"no description"
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.helptext
|
88
|
+
"Usage: crypto #{self.prettyname} [options]"
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.execute
|
92
|
+
raise StandardError, "This command is not implemented yet (#{self.to_s.split('::').last})"
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.prettyname
|
96
|
+
Utils.snakecase self.to_s.split('::').last
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.hidden?
|
100
|
+
false
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.error message
|
104
|
+
puts "Error:"
|
105
|
+
puts message
|
106
|
+
exit 1
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'gtmtech/crypto/subcommand'
|
2
|
+
require 'gtmtech/crypto/data'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Gtmtech
|
6
|
+
module Crypto
|
7
|
+
module Subcommands
|
8
|
+
|
9
|
+
class Account < Subcommand
|
10
|
+
|
11
|
+
def self.description
|
12
|
+
"manage accounts"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.usage
|
16
|
+
<<-EOS
|
17
|
+
Usage (crypto #{self.prettyname})
|
18
|
+
|
19
|
+
crypto #{self.prettyname} new --name=<s> --currencies=<s> [--type=<s> --url=<s>]
|
20
|
+
- create a new account
|
21
|
+
|
22
|
+
crypto #{self.prettyname} list
|
23
|
+
- list all accounts
|
24
|
+
|
25
|
+
Options:
|
26
|
+
EOS
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.options
|
30
|
+
[{:name => :name,
|
31
|
+
:description => "Name of the account",
|
32
|
+
:short => 'n',
|
33
|
+
:type => :string},
|
34
|
+
{:name => :currencies,
|
35
|
+
:description => "Comma separated list of currencies this account supports",
|
36
|
+
:short => 'c',
|
37
|
+
:type => :string},
|
38
|
+
{:name => :type,
|
39
|
+
:description => "(optional) Type of account - options are wallet,exchange,bank",
|
40
|
+
:short => 't',
|
41
|
+
:type => :string},
|
42
|
+
{:name => :url,
|
43
|
+
:description => "(optional) URL for account website",
|
44
|
+
:short => 'u',
|
45
|
+
:type => :string}
|
46
|
+
]
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.create
|
50
|
+
self.error "--name required" unless @@options[:name_given]
|
51
|
+
self.error "--currencies required" unless @@options[:currencies_given]
|
52
|
+
|
53
|
+
Data.load
|
54
|
+
Data.add_account( @@options[:name], @@options[:currencies], @@options[:type], @@options[:url] )
|
55
|
+
Data.save
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.list
|
59
|
+
Data.load
|
60
|
+
Data.list_accounts
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.execute
|
64
|
+
verb = ARGV.shift
|
65
|
+
case verb
|
66
|
+
when "new", "create"
|
67
|
+
self.create
|
68
|
+
when "list"
|
69
|
+
self.list
|
70
|
+
else
|
71
|
+
self.error "account takes an action [new, list] . See --help for more info"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'gtmtech/crypto/subcommand'
|
2
|
+
require 'gtmtech/crypto'
|
3
|
+
require 'gtmtech/crypto/CLI'
|
4
|
+
|
5
|
+
module Gtmtech
|
6
|
+
module Crypto
|
7
|
+
module Subcommands
|
8
|
+
|
9
|
+
class Help < Subcommand
|
10
|
+
|
11
|
+
def self.options
|
12
|
+
[]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.execute
|
16
|
+
puts <<-EOS
|
17
|
+
Welcome to gtmtech-crypto #{Gtmtech::Crypto::VERSION}
|
18
|
+
|
19
|
+
Usage:
|
20
|
+
crypto <subcommand> ... [global-opts] [subcommand-opts]
|
21
|
+
|
22
|
+
Available subcommands:
|
23
|
+
#{Gtmtech::Crypto::CLI.subcommands.collect {|command|
|
24
|
+
command_class = Gtmtech::Crypto::Subcommands.const_get(Utils.camelcase command)
|
25
|
+
sprintf "%15s: %-65s", command.downcase, command_class.description unless command_class.hidden?
|
26
|
+
}.compact.join("\n")}
|
27
|
+
|
28
|
+
For more help on an individual command, use --help on that command
|
29
|
+
|
30
|
+
EOS
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.description
|
34
|
+
"print help message"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'gtmtech/crypto/subcommand'
|
2
|
+
|
3
|
+
module Gtmtech
|
4
|
+
module Crypto
|
5
|
+
module Subcommands
|
6
|
+
|
7
|
+
class Txn < Subcommand
|
8
|
+
|
9
|
+
def self.description
|
10
|
+
"manage accounts"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.usage
|
14
|
+
<<-EOS
|
15
|
+
Usage (crypto #{self.prettyname})
|
16
|
+
|
17
|
+
crypto #{self.prettyname} new --date=<s> --from=<s> --to=<s> [--fees=<s>]
|
18
|
+
- create a new transaction
|
19
|
+
|
20
|
+
crypto #{self.prettyname} delete --id=<s>
|
21
|
+
- delete a transaction
|
22
|
+
|
23
|
+
crypto #{self.prettyname} list
|
24
|
+
- list all transactions
|
25
|
+
|
26
|
+
|
27
|
+
Options:
|
28
|
+
EOS
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.options
|
32
|
+
[{:name => :date,
|
33
|
+
:description => "Date of transaction (in format 2017-06-24,11:23:45 or \"now\")",
|
34
|
+
:short => 'd',
|
35
|
+
:type => :string},
|
36
|
+
{:name => :from,
|
37
|
+
:description => "Source of the transaction in the form <name>.<currency>=<sent amount> e.g. barclays.GBP=20.50",
|
38
|
+
:short => 'n',
|
39
|
+
:type => :string},
|
40
|
+
{:name => :to,
|
41
|
+
:description => "Destination of the transaction in the form <name>.<currency>=<received amount> e.g. kraken.BTC=0.40503",
|
42
|
+
:short => 't',
|
43
|
+
:type => :string},
|
44
|
+
{:name => :fees,
|
45
|
+
:description => "Any fees EXTERNAL to the transaction, in the form <name>.<currency>=<amount> e.g. kraken.BTC=0.00001",
|
46
|
+
:short => 'f',
|
47
|
+
:type => :string},
|
48
|
+
{:name => :id,
|
49
|
+
:description => "Specify the id you wish to amend/delete in a delete operation",
|
50
|
+
:short => 'i',
|
51
|
+
:type => :string}
|
52
|
+
]
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.create
|
56
|
+
self.error "--date required" unless @@options[:date_given]
|
57
|
+
self.error "--date must be in format \"now\" or YYYY-MM-DD,hh:mm:ss" unless (@@options[:date] == "now" or @@options[:date] =~ /\d\d\d\d\-\d\d\-\d\d,\d\d:\d\d:\d\d/)
|
58
|
+
|
59
|
+
self.error "--from required" unless @@options[:from_given]
|
60
|
+
self.error "--from must be in format <name>.<currency>=<amount>" unless @@options[:from] =~ /^\w+\.\w+=[\d\.]+$/
|
61
|
+
|
62
|
+
self.error "--to required" unless @@options[:to_given]
|
63
|
+
self.error "--to must be in format <name>.<currency>=<amount>" unless @@options[:to] =~ /^\w+\.\w+=[\d\.]+$/
|
64
|
+
|
65
|
+
if @@options[:fees_given]
|
66
|
+
self.error "--fees must be in format <name>.<currency>=<amount>" unless @@options[:fees] =~ /^\w+\.\w+=[\d\.]+$/
|
67
|
+
end
|
68
|
+
|
69
|
+
source_account = @@options[:from].split(".")[0]
|
70
|
+
source_currency = @@options[:from].split(".")[1].split("=")[0]
|
71
|
+
source_amount = @@options[:from].split("=")[1]
|
72
|
+
self.error "--from: txn amount must be a decimal or integer" unless source_amount =~ /^\d*\.\d+$/ or source_amount =~ /^\d+$/
|
73
|
+
|
74
|
+
dest_account = @@options[:to].split(".")[0]
|
75
|
+
dest_currency = @@options[:to].split(".")[1].split("=")[0]
|
76
|
+
dest_amount = @@options[:to].split("=")[1]
|
77
|
+
self.error "--to: txn amount must be a decimal or integer" unless dest_amount =~ /^\d*\.\d+$/ or dest_amount =~ /^\d+$/
|
78
|
+
|
79
|
+
self.error "cannot create a self-referential transaction" if source_account == dest_account and source_currency == dest_currency
|
80
|
+
|
81
|
+
fees_account = nil
|
82
|
+
fees_currency = nil
|
83
|
+
fees_amount = nil
|
84
|
+
if @@options[:fees_given]
|
85
|
+
fees_account = @@options[:fees].split(".")[0]
|
86
|
+
fees_currency = @@options[:fees].split(".")[1].split("=")[0]
|
87
|
+
fees_amount = @@options[:fees].split("=")[1]
|
88
|
+
end
|
89
|
+
|
90
|
+
Data.load
|
91
|
+
self.error "The account #{source_account}.#{source_currency} does not yet exist" unless Data.account_exists? source_account, source_currency
|
92
|
+
self.error "The account #{dest_account}.#{dest_currency} does not yet exist" unless Data.account_exists? dest_account, dest_currency
|
93
|
+
Data.add_transaction @@options[:date], source_account, source_currency, source_amount, dest_account, dest_currency, dest_amount, fees_account, fees_currency, fees_amount
|
94
|
+
Data.save
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.list
|
98
|
+
Data.load
|
99
|
+
Data.list_transactions
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.delete
|
103
|
+
self.error "--id required" unless @@options[:id_given]
|
104
|
+
Data.load
|
105
|
+
Data.delete_transaction @@options[:id]
|
106
|
+
Data.save
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.execute
|
110
|
+
verb = ARGV.shift
|
111
|
+
case verb
|
112
|
+
when "new", "create"
|
113
|
+
self.create
|
114
|
+
when "list"
|
115
|
+
self.list
|
116
|
+
when "delete"
|
117
|
+
self.delete
|
118
|
+
else
|
119
|
+
self.error "transaction takes an action [new, list, delete] . See --help for more info"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'gtmtech/crypto/subcommand'
|
2
|
+
|
3
|
+
module Gtmtech
|
4
|
+
module Crypto
|
5
|
+
module Subcommands
|
6
|
+
|
7
|
+
class UnknownCommand < Subcommand
|
8
|
+
|
9
|
+
def self.options
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.execute
|
14
|
+
puts "Unknown command"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'gtmtech/crypto/subcommand'
|
2
|
+
require 'gtmtech/crypto'
|
3
|
+
|
4
|
+
module Gtmtech
|
5
|
+
module Crypto
|
6
|
+
module Subcommands
|
7
|
+
|
8
|
+
class Version < Subcommand
|
9
|
+
|
10
|
+
def self.options
|
11
|
+
[]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.execute
|
15
|
+
puts "gtmtech-crypto v#{Gtmtech::Crypto::VERSION}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.description
|
19
|
+
"output version information"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Gtmtech
|
4
|
+
module Crypto
|
5
|
+
class Utils
|
6
|
+
|
7
|
+
def self.camelcase string
|
8
|
+
return string if string !~ /_/ && string =~ /[A-Z]+.*/
|
9
|
+
string.split('_').map{|e| e.capitalize}.join
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.snakecase string
|
13
|
+
return string if string !~ /[A-Z]/
|
14
|
+
string.split(/(?=[A-Z])/).collect {|x| x.downcase}.join("_")
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.find_closest_class args
|
18
|
+
parent_class = args[ :parent_class ]
|
19
|
+
class_name = args[ :class_name ]
|
20
|
+
constants = parent_class.constants
|
21
|
+
candidates = []
|
22
|
+
constants.each do | candidate |
|
23
|
+
candidates << candidate.to_s if candidate.to_s.downcase == class_name.downcase
|
24
|
+
end
|
25
|
+
if candidates.count > 0
|
26
|
+
parent_class.const_get candidates.first
|
27
|
+
else
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.require_dir classdir
|
33
|
+
num_class_hierarchy_levels = self.to_s.split("::").count - 1
|
34
|
+
root_folder = File.dirname(__FILE__) + "/" + Array.new(num_class_hierarchy_levels).fill("..").join("/")
|
35
|
+
class_folder = root_folder + "/" + classdir
|
36
|
+
Dir[File.expand_path("#{class_folder}/*.rb")].uniq.each do |file|
|
37
|
+
require file
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.find_all_subclasses_of args
|
42
|
+
parent_class = args[ :parent_class ]
|
43
|
+
constants = parent_class.constants
|
44
|
+
candidates = []
|
45
|
+
constants.each do | candidate |
|
46
|
+
candidates << candidate.to_s.split('::').last if parent_class.const_get(candidate).class.to_s == "Class"
|
47
|
+
end
|
48
|
+
candidates
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.make_decimal digits
|
52
|
+
if digits.start_with? "."
|
53
|
+
"0#{digits}"
|
54
|
+
elsif digits =~ /^\d+$/
|
55
|
+
"#{digits}.0"
|
56
|
+
else
|
57
|
+
digits
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gtmtech-crypto
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Geoff Meakin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-06-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: trollop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
description: Simple tool for accounting of cryptocurrencies
|
28
|
+
email:
|
29
|
+
executables:
|
30
|
+
- crypto
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- .ruby-version
|
36
|
+
- Gemfile
|
37
|
+
- LICENSE.txt
|
38
|
+
- README.md
|
39
|
+
- Rakefile
|
40
|
+
- bin/crypto
|
41
|
+
- build.sh
|
42
|
+
- gtmtech-crypto.gemspec
|
43
|
+
- lib/gtmtech/crypto.rb
|
44
|
+
- lib/gtmtech/crypto/CLI.rb
|
45
|
+
- lib/gtmtech/crypto/data.rb
|
46
|
+
- lib/gtmtech/crypto/subcommand.rb
|
47
|
+
- lib/gtmtech/crypto/subcommands/account.rb
|
48
|
+
- lib/gtmtech/crypto/subcommands/help.rb
|
49
|
+
- lib/gtmtech/crypto/subcommands/txn.rb
|
50
|
+
- lib/gtmtech/crypto/subcommands/unknown_command.rb
|
51
|
+
- lib/gtmtech/crypto/subcommands/version.rb
|
52
|
+
- lib/gtmtech/crypto/utils.rb
|
53
|
+
homepage: http://github.com/gtmtechltd/crypto
|
54
|
+
licenses:
|
55
|
+
- MIT
|
56
|
+
metadata: {}
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
requirements: []
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 2.0.14
|
74
|
+
signing_key:
|
75
|
+
specification_version: 4
|
76
|
+
summary: Simple tool for accounting of cryptocurrencies
|
77
|
+
test_files: []
|