gtmtech-crypto 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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: []
|