stellar_base-rails 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +19 -29
  3. data/app/concepts/stellar_base/bridge_callbacks/bridge_callback_policy.rb +14 -0
  4. data/app/concepts/stellar_base/bridge_callbacks/contracts/{process.rb → create.rb} +5 -5
  5. data/app/concepts/stellar_base/bridge_callbacks/operations/create.rb +31 -0
  6. data/app/concepts/stellar_base/withdrawal_requests/contracts/create.rb +38 -0
  7. data/app/concepts/stellar_base/withdrawal_requests/operations/create.rb +53 -0
  8. data/app/concepts/stellar_base/withdrawal_requests/withdrawal_request_policy.rb +14 -0
  9. data/app/controllers/stellar_base/bridge_callbacks_controller.rb +1 -1
  10. data/app/controllers/stellar_base/withdraw_controller.rb +35 -0
  11. data/app/models/stellar_base/bridge_callback.rb +1 -14
  12. data/app/models/stellar_base/withdrawal_request.rb +4 -0
  13. data/app/representers/application_representer.rb +3 -0
  14. data/app/representers/withdrawal_request_representer.rb +13 -0
  15. data/app/services/stellar_base/bridge_callbacks/process.rb +9 -7
  16. data/app/services/stellar_base/gen_random_string.rb +10 -0
  17. data/app/services/stellar_base/withdrawal_requests/call_on_withdraw.rb +30 -0
  18. data/app/services/stellar_base/withdrawal_requests/determine_fee.rb +21 -0
  19. data/app/services/stellar_base/withdrawal_requests/find_withdrawal_request.rb +40 -0
  20. data/app/services/stellar_base/withdrawal_requests/gen_memo.rb +19 -0
  21. data/app/services/stellar_base/withdrawal_requests/process.rb +27 -0
  22. data/app/twins/application_twin.rb +3 -0
  23. data/app/twins/withdrawal_request_twin.rb +13 -0
  24. data/config/routes.rb +2 -3
  25. data/db/migrate/20180816014433_create_stellar_base_bridge_callbacks.rb +21 -0
  26. data/db/migrate/20180816110314_create_stellar_base_withdrawal_requests.rb +25 -0
  27. data/db/migrate/20180816135847_unique_stellar_base_bridge_callbacks_operation_id.rb +11 -0
  28. data/lib/stellar_base.rb +33 -0
  29. data/lib/stellar_base/factories.rb +3 -0
  30. data/lib/stellar_base/factories/bridge_callbacks.rb +9 -0
  31. data/lib/stellar_base/factories/withdrawal_requests.rb +14 -0
  32. data/lib/stellar_base/version.rb +1 -1
  33. metadata +97 -5
  34. data/app/concepts/stellar_base/bridge_callbacks/operations/process.rb +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a83410d8dc46aa9b3e3a7501ff339f620e4bb16073a5156b935a942249204fcd
4
- data.tar.gz: b76ff40be0ee4f471c37198856157f152ef041d14736e2fdf0eebfaf42f3a770
3
+ metadata.gz: 15b69658233c066ee56074dd0bcf6ceed2abb687d873322e7d732f36bfb9d96c
4
+ data.tar.gz: 29e95f9804ccd034b6a9d210792ad19800936fbfddc9ae7c0588dd62bce0231d
5
5
  SHA512:
6
- metadata.gz: 2db9224965d1e88f9f6f8b1deba1e50bcf6ac32c7226da4ba8cbaee737c6d9834b0d663861597cabb65eb9e601e13d6e695b7097d44c73df34ca2e6419f3e7f6
7
- data.tar.gz: 1bf1a4bd010321297c05638303b681756d1c16089d006e8acdc3e4a50fc438d45ef9a35cd5404d0b73c5f9105ef2a1af505d246a9ea243ae32d09d7401e98224
6
+ metadata.gz: 9340c436c18b047df93e5a6ca27321c4fe586ed0c60f449e3d55a2042cfd8ac9f19411d7fdb2e6954c4767d64dbd8c22f2c436f0475daa0d1513bcaf27018f57
7
+ data.tar.gz: 123e430f1e9f8fc2a938bcd928e09a34174f3325c7df4a9649d48de58e255bed5a4e3b2961118485ed220c08a6368761c3bddbcaab35cd3b612769080f991a0c
data/README.md CHANGED
@@ -11,6 +11,11 @@ Adding modules to your routes:
11
11
  mount StellarBase::Engine => "/stellar_base"
12
12
  ```
13
13
 
14
+ ```sh
15
+ rails stellar_base:install:migrations
16
+ rails db:migrate
17
+ ```
18
+
14
19
  ### Configuration
15
20
  Create an initializer in your rails application:
16
21
 
@@ -30,41 +35,21 @@ end
30
35
 
31
36
 
32
37
  #### c.modules
33
- - Value(s): array of symbols
34
- - bridge_callbacks - when supplied this will mount the `/bridge_callbacks` endpoint
38
+ You can supply what endpoints you want to activate with the gem
39
+
40
+ - Value(s): array of symbols. See the [modules documentation](docs/modules.md) for more details.
35
41
  - Default: `%i(bridge_callbacks)`
36
- - You can supply what endpoints you want to activate with the gem
37
- - `bridge_callbacks` - this will mount a HTTP/S POST endpoint that acts as callback receiver for bridge server payments on the path. It will call your `.on_bridge_callback` class.
42
+
43
+ #### c.distribution_account
44
+ This is the same distribution account that is setup in bridge. Currently, it is used in the `/withdraw` endpoint -- to tell the user to send the assets there.
45
+
46
+ - Value: Stellar account address
38
47
 
39
48
  #### c.horizon_url
40
49
  - Value(s): String, url to horizon
41
50
  - Default: https://horizon.stellar.org
42
51
  - This is where the engine will check bridge callbacks if `c.check_bridge_callbacks_authenticity` is turned on
43
52
 
44
- #### c.on_bridge_callback
45
- - Value(s): Class
46
- - Default: None
47
- - Once the bridge_receive endpoint receives a callback, the class will be called with .call
48
- - The class will be passed with the bridge server callback payload contained in a `StellarBase::BridgeCallback` object.
49
- - The class will be expected to return a boolean, return true if the callback was processed properly
50
- - Warning: The bridge server will may post multiple callbacks with the same ID, make sure you handle these correctly. https://github.com/stellar/bridge-server/blob/master/readme_bridge.md#callbacksreceive
51
-
52
-
53
- #### c.check_bridge_callbacks_authenticity
54
- - Value(s): `true` or `false`
55
- - Default: `false`
56
- - This secures the `/bridge_callbacks` endpoint from fake transactions by checking the transaction ID and it's contents against the Stellar Blockchain. If it doesn't add up, `/bridge_callbacks` endpoint will respond with a 422
57
-
58
- #### c.check_bridge_callbacks_mac_payload
59
- - Value(s): `true` or `false`
60
- - Default: `false`
61
- - This secures the `/bridge_callbacks` endpoint from fake transactions by checking the `X_PAYLOAD_MAC` header for 1.) existence and 2.) if it matches the HMAC-SH256 encoded raw request body
62
-
63
- #### c.bridge_callbacks_mac_key
64
- - Value(s): Any Stellar Private Key, it should be the same as the mac_key configured in your bridge server
65
- - Default: None
66
- - This is used to verify the contents of `X_PAYLOAD_MAC` by encoding the raw request body with the decoded `bridge_callback_mac_key` as the key
67
-
68
53
  ## Installation
69
54
  Add this line to your application's Gemfile:
70
55
 
@@ -78,7 +63,12 @@ $ bundle
78
63
  ```
79
64
 
80
65
  ## Contributing
81
- Contribution directions go here.
66
+
67
+ ```ruby
68
+ cp spec/config.yml{.sample,}
69
+ ```
70
+
71
+ Edit the `spec/config.yml` file.
82
72
 
83
73
  ## License
84
74
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,14 @@
1
+ module StellarBase
2
+ module BridgeCallbacks
3
+ class BridgeCallbackPolicy
4
+
5
+ def initialize(_, _)
6
+ end
7
+
8
+ def create?
9
+ StellarBase.included_module?(:withdraw)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -1,9 +1,9 @@
1
1
  module StellarBase
2
2
  module BridgeCallbacks
3
3
  module Contracts
4
- class Process < ApplicationContract
4
+ class Create < ApplicationContract
5
5
 
6
- property :id
6
+ property :operation_id
7
7
  property :from
8
8
  property :route
9
9
  property :amount
@@ -14,7 +14,7 @@ module StellarBase
14
14
  property :data
15
15
  property :transaction_id
16
16
 
17
- validates :id, presence: true
17
+ validates :operation_id, presence: true
18
18
  validates :transaction_id, presence: true
19
19
  validates :from, presence: true
20
20
  validates :amount, presence: true
@@ -27,10 +27,10 @@ module StellarBase
27
27
  end
28
28
 
29
29
  result = BridgeCallbacks::Check.({
30
- operation_id: id,
30
+ operation_id: operation_id,
31
31
  transaction_id: transaction_id,
32
32
  params: {
33
- id: id,
33
+ id: operation_id,
34
34
  from: from,
35
35
  route: route,
36
36
  amount: amount,
@@ -0,0 +1,31 @@
1
+ module StellarBase
2
+ module BridgeCallbacks
3
+ module Operations
4
+ class Create < ApplicationOperation
5
+ step Policy::Pundit(BridgeCallbackPolicy, :create?)
6
+ step :assign_operation_id!
7
+ step :find_model!
8
+ step Model(BridgeCallback, :new)
9
+ step Contract::Build(constant: Contracts::Create)
10
+ step Contract::Validate(key: :bridge_callback)
11
+ step Contract::Persist()
12
+ step :process!
13
+
14
+ def find_model!(options, params:, **)
15
+ operation_id = params[:bridge_callback][:operation_id]
16
+ bridge_callback = BridgeCallback.find_by(operation_id: operation_id)
17
+ options["model"] = bridge_callback
18
+ bridge_callback.present? ? Railway.pass_fast! : true
19
+ end
20
+
21
+ def assign_operation_id!(options, params:, **)
22
+ params[:bridge_callback][:operation_id] = params[:bridge_callback][:id]
23
+ end
24
+
25
+ def process!(options, **)
26
+ BridgeCallbacks::Process.(options["model"])
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ module StellarBase
2
+ module WithdrawalRequests
3
+ module Contracts
4
+ class Create < ApplicationContract
5
+
6
+ property :asset_type
7
+ property :asset_code
8
+ property :dest
9
+ property :dest_extra
10
+ property :issuer
11
+ property :account_id
12
+ property :memo_type
13
+ property :memo
14
+ property :eta
15
+ property :min_amount
16
+ property :max_amount
17
+ property :fee_fixed
18
+ property :fee_percent
19
+ property :fee_network
20
+
21
+ validates(
22
+ *%i[
23
+ asset_type
24
+ asset_code
25
+ dest
26
+ issuer
27
+ account_id
28
+ fee_network
29
+ fee_fixed
30
+ fee_percent
31
+ ],
32
+ presence: true
33
+ )
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,53 @@
1
+ module StellarBase
2
+ module WithdrawalRequests
3
+ module Operations
4
+ class Create < ApplicationOperation
5
+
6
+ DEFAULT_ETA = (10 * 60).freeze
7
+
8
+ step self::Policy::Pundit(WithdrawalRequestPolicy, :create?)
9
+ step Model(WithdrawalRequest, :new)
10
+ step :find_withdrawal_asset_details!
11
+ success :set_defaults!
12
+ step Contract::Build(constant: Contracts::Create)
13
+ step Contract::Validate(key: :withdrawal_request)
14
+ step Contract::Persist()
15
+
16
+ private
17
+
18
+ def find_withdrawal_asset_details!(options, params:, **)
19
+ withdrawable_assets = StellarBase.configuration.withdrawable_assets
20
+ details = withdrawable_assets.find do |e|
21
+ e[:asset_code] == params[:withdrawal_request][:asset_code]
22
+ end
23
+
24
+ return Railway.fail_fast! if details.nil?
25
+
26
+ options["withdrawal_asset_details"] = details
27
+ return true
28
+ end
29
+
30
+ def set_defaults!(options, withdrawal_asset_details:, params:, **)
31
+ fee_network = DetermineFee.network(
32
+ withdrawal_asset_details[:network],
33
+ params[:withdrawal_request][:fee_network],
34
+ )
35
+ params[:withdrawal_request].merge!({
36
+ asset_type: params[:withdrawal_request][:type],
37
+ issuer: withdrawal_asset_details[:issuer],
38
+ account_id: StellarBase.configuration.distribution_account,
39
+ memo_type: "text",
40
+ memo: GenMemo.(),
41
+ eta: DEFAULT_ETA,
42
+ min_amount: 0.0,
43
+ max_amount: nil,
44
+ fee_fixed: DetermineFee.(withdrawal_asset_details[:fee_fixed]),
45
+ fee_percent: DetermineFee.(withdrawal_asset_details[:fee_percent]),
46
+ fee_network: fee_network,
47
+ })
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,14 @@
1
+ module StellarBase
2
+ module WithdrawalRequests
3
+ class WithdrawalRequestPolicy
4
+
5
+ def initialize(_, _)
6
+ end
7
+
8
+ def create?
9
+ StellarBase.included_module?(:withdraw)
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -4,7 +4,7 @@ module StellarBase
4
4
  before_action :verify_mac_payload, if: :check_mac_payload?
5
5
 
6
6
  def create
7
- op = BridgeCallbacks::Operations::Process.(bridge_callback: callback_params)
7
+ op = BridgeCallbacks::Operations::Create.(bridge_callback: callback_params)
8
8
 
9
9
  respond_to do |f|
10
10
  f.json do
@@ -0,0 +1,35 @@
1
+ module StellarBase
2
+ class WithdrawController < ApplicationController
3
+
4
+ WITHDRAWAL_REQUEST_PARAMS = %i[
5
+ type
6
+ asset_code
7
+ dest
8
+ dest_extra
9
+ fee_network
10
+ ]
11
+
12
+ def create
13
+ op = WithdrawalRequests::Operations::Create.(withdrawal_request: withdrawal_request_params)
14
+
15
+ respond_to do |f|
16
+ f.json do
17
+ if op.success?
18
+ twin = WithdrawalRequestTwin.new(op["model"])
19
+ representer = WithdrawalRequestRepresenter.new(twin)
20
+ render json: representer
21
+ else
22
+ head :unprocessable_entity
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def withdrawal_request_params
31
+ params.permit(*WITHDRAWAL_REQUEST_PARAMS)
32
+ end
33
+
34
+ end
35
+ end
@@ -1,17 +1,4 @@
1
1
  module StellarBase
2
- class BridgeCallback
3
- include Virtus.model
4
- # Attributes and params from
5
- # https://github.com/stellar/bridge-server/blob/master/readme_bridge.md#callbacksreceive
6
- attribute :id, String
7
- attribute :from, String
8
- attribute :route, String
9
- attribute :amount, Float
10
- attribute :asset_code, String
11
- attribute :asset_issuer, String
12
- attribute :memo_type, String
13
- attribute :memo, String
14
- attribute :data, String
15
- attribute :transaction_id, String
2
+ class BridgeCallback < ApplicationRecord
16
3
  end
17
4
  end
@@ -0,0 +1,4 @@
1
+ module StellarBase
2
+ class WithdrawalRequest < ApplicationRecord
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRepresenter < Representable::Decorator
2
+ include Representable::JSON
3
+ end
@@ -0,0 +1,13 @@
1
+ class WithdrawalRequestRepresenter < ApplicationRepresenter
2
+
3
+ property :account_id
4
+ property :memo_type
5
+ property :memo
6
+ property :min_amount
7
+ property :max_amount
8
+ property :fee_fixed
9
+ property :fee_percent
10
+ property :fee_network
11
+ property :extra_info
12
+
13
+ end
@@ -3,7 +3,15 @@ module StellarBase
3
3
  class Process
4
4
  def self.call(callback)
5
5
  callback_class.(callback)
6
- rescue NameError => e
6
+ end
7
+
8
+ private
9
+
10
+ def self.callback_class
11
+ on_bridge_callback = StellarBase.configuration.on_bridge_callback
12
+ return on_bridge_callback if on_bridge_callback.respond_to?(:call)
13
+ on_bridge_callback.constantize
14
+ rescue NameError
7
15
  error_message = [
8
16
  "StellarBase.on_bridge_callback isn't configured or the",
9
17
  "configured callback class doesn't exist:",
@@ -12,12 +20,6 @@ module StellarBase
12
20
 
13
21
  raise NameError, error_message
14
22
  end
15
-
16
- private
17
-
18
- def self.callback_class
19
- StellarBase.configuration.on_bridge_callback.constantize
20
- end
21
23
  end
22
24
  end
23
25
  end
@@ -0,0 +1,10 @@
1
+ module StellarBase
2
+ class GenRandomString
3
+
4
+ def self.call(length:)
5
+ o = [(0..9), ('A'..'Z')].map(&:to_a).flatten
6
+ string = (0...length).map { o[rand(o.length)] }.join
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+ module StellarBase
2
+ module WithdrawalRequests
3
+ class CallOnWithdraw
4
+
5
+ extend LightService::Action
6
+ expects :withdrawal_request, :bridge_callback, :on_withdraw
7
+
8
+ ON_WITHDRAW_ERROR = "`on_withdraw` must be a string of an object " \
9
+ "that responds to `.call` or the object itself"
10
+
11
+ executed do |c|
12
+ callback = callback_from(c.on_withdraw)
13
+ callback.(c.withdrawal_request, c.bridge_callback)
14
+ end
15
+
16
+ def self.callback_from(on_withdraw)
17
+ if on_withdraw.respond_to?(:constantize)
18
+ on_withdraw = on_withdraw.constantize
19
+ end
20
+
21
+ if !on_withdraw.respond_to?(:call)
22
+ fail ArgumentError, ON_WITHDRAW_ERROR
23
+ end
24
+
25
+ on_withdraw
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ module StellarBase
2
+ module WithdrawalRequests
3
+ class DetermineFee
4
+
5
+ DEFAULT = 0.0
6
+ # NOTE: Hard-code default network fees for now.
7
+ DEFAULT_NETWORK = {
8
+ bitcoin: 0.0001,
9
+ }.with_indifferent_access.freeze
10
+
11
+ def self.call(v)
12
+ v || DEFAULT
13
+ end
14
+
15
+ def self.network(network, v)
16
+ v || DEFAULT_NETWORK.fetch(network)
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ module StellarBase
2
+ module WithdrawalRequests
3
+ class FindWithdrawalRequest
4
+
5
+ extend LightService::Action
6
+ expects :bridge_callback
7
+ promises :withdrawal_request
8
+
9
+ MATCH_WITHDRAWAL_REQUEST_TO_BRIDGE_CALLBACK_MAP = {
10
+ asset_code: :asset_code,
11
+ issuer: :asset_issuer,
12
+ memo_type: :memo_type,
13
+ memo: :memo,
14
+ }.freeze
15
+
16
+ executed do |c|
17
+ bridge_callback = c.bridge_callback
18
+ c.withdrawal_request = withdrawal_request = WithdrawalRequest.
19
+ find_by(memo: bridge_callback.memo)
20
+
21
+ if withdrawal_request.nil?
22
+ c.skip_remaining!
23
+ next c
24
+ end
25
+
26
+ map = MATCH_WITHDRAWAL_REQUEST_TO_BRIDGE_CALLBACK_MAP
27
+ mismatch = map.any? do |withdrawal_request_attr, bridge_callback_attr|
28
+ withdrawal_request.send(withdrawal_request_attr) !=
29
+ bridge_callback.send(bridge_callback_attr)
30
+ end
31
+
32
+ if mismatch
33
+ c.skip_remaining!
34
+ next c
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,19 @@
1
+ module StellarBase
2
+ module WithdrawalRequests
3
+ class GenMemo
4
+
5
+ LENGTH = 8
6
+
7
+ def self.call
8
+ loop do
9
+ memo = GenRandomString.(length: LENGTH)
10
+ if !WithdrawalRequest.exists?(memo: memo)
11
+ return memo
12
+ break
13
+ end
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ module StellarBase
2
+ module WithdrawalRequests
3
+ class Process
4
+
5
+ extend LightService::Organizer
6
+
7
+ def self.call(
8
+ bridge_callback,
9
+ on_withdraw: StellarBase.configuration.on_withdraw
10
+ )
11
+ with(
12
+ bridge_callback: bridge_callback,
13
+ on_withdraw: on_withdraw,
14
+ ).reduce(actions)
15
+ end
16
+
17
+ def self.actions
18
+ [
19
+ FindWithdrawalRequest,
20
+ CallOnWithdraw,
21
+ ]
22
+ end
23
+ private_class_method :actions
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationTwin < Disposable::Twin
2
+ feature Expose
3
+ end
@@ -0,0 +1,13 @@
1
+ class WithdrawalRequestTwin < ApplicationTwin
2
+
3
+ property :account_id
4
+ property :memo_type
5
+ property :memo
6
+ property :min_amount
7
+ property :max_amount
8
+ property :fee_fixed
9
+ property :fee_percent
10
+ property :fee_network
11
+ property :extra_info
12
+
13
+ end
data/config/routes.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  StellarBase::Engine.routes.draw do
2
- if StellarBase.included_module?(:bridge_callbacks)
3
- resources :bridge_callbacks, only: [:create], defaults: { format: "json" }
4
- end
2
+ resources :bridge_callbacks, only: [:create], defaults: { format: "json" }
3
+ get "/withdraw" => "withdraw#create", as: :withdraw
5
4
  end
6
5
 
@@ -0,0 +1,21 @@
1
+ class CreateStellarBaseBridgeCallbacks < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :stellar_base_bridge_callbacks do |t|
4
+ t.string :operation_id, null: false
5
+ t.string :from, null: false
6
+ t.string :route
7
+ t.decimal :amount, null: false
8
+ t.string :asset_code
9
+ t.string :asset_issuer
10
+ t.string :memo_type
11
+ t.string :memo
12
+ t.string :data
13
+ t.string :transaction_id
14
+ t.index %i[asset_code asset_issuer], name: :index_stellar_base_bridge_callbacks_on_asset
15
+ t.index :memo
16
+ t.index :operation_id
17
+ t.index :transaction_id
18
+ t.timestamps
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ class CreateStellarBaseWithdrawalRequests < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :stellar_base_withdrawal_requests do |t|
4
+ t.string :asset_type, null: false
5
+ t.string :asset_code, null: false
6
+ t.string :dest, null: false
7
+ t.string :issuer, null: false
8
+ t.string :dest_extra
9
+ t.string :account_id
10
+ t.string :memo_type
11
+ t.string :memo
12
+ t.integer :eta
13
+ t.decimal :min_amount, null: false, default: 0.0
14
+ t.decimal :max_amount
15
+ t.decimal :fee_fixed, null: false, default: 0.0
16
+ t.decimal :fee_percent, null: false, default: 0.0
17
+ t.decimal :fee_network, null: false, default: 0.0
18
+ t.string :extra_info
19
+ t.index([:asset_type, :asset_code], {
20
+ name: :index_stellar_base_withdrawal_requests_on_asset,
21
+ })
22
+ t.timestamps
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ class UniqueStellarBaseBridgeCallbacksOperationId < ActiveRecord::Migration[5.1]
2
+ def up
3
+ remove_index :stellar_base_bridge_callbacks, :operation_id
4
+ add_index :stellar_base_bridge_callbacks, :operation_id, unique: true
5
+ end
6
+
7
+ def down
8
+ remove_index :stellar_base_bridge_callbacks, :operation_id
9
+ add_index :stellar_base_bridge_callbacks, :operation_id
10
+ end
11
+ end
data/lib/stellar_base.rb CHANGED
@@ -5,8 +5,10 @@ require "virtus"
5
5
  require "httparty"
6
6
  require "trailblazer-rails"
7
7
  require "disposable"
8
+ require "multi_json"
8
9
  require "reform"
9
10
  require "reform/form/coercion"
11
+ require "representable"
10
12
 
11
13
  require "stellar_base/engine"
12
14
 
@@ -17,15 +19,46 @@ module StellarBase
17
19
  has :horizon_url, default: "https://horizon.stellar.org"
18
20
  has :modules, default: [:bridge_callbacks]
19
21
 
22
+ has :distribution_account, classes: [NilClass, String]
23
+
20
24
  has :on_bridge_callback
21
25
  has :check_bridge_callbacks_authenticity, default: false
22
26
  has :check_bridge_callbacks_mac_payload, default: false
23
27
  has :bridge_callbacks_mac_key, default: false
28
+
29
+ has :withdrawable_assets, classes: [NilClass, Array, String, Pathname]
30
+ has :on_withdraw
31
+ end
32
+
33
+ after_configuration_change do
34
+ self.convert_config_withdraw!
24
35
  end
25
36
 
26
37
  def self.included_module?(module_name)
27
38
  self.configuration.modules&.include?(module_name)
28
39
  end
40
+
41
+ def self.convert_config_withdraw!
42
+ withdrawable_assets = self.configuration.withdrawable_assets
43
+ return if withdrawable_assets.is_a?(Array) || withdrawable_assets.nil?
44
+
45
+ array_of_hashes = try_from_yaml_file_path(withdrawable_assets) ||
46
+ try_from_json(withdrawable_assets)
47
+
48
+ self.configuration.withdrawable_assets =
49
+ array_of_hashes.map(&:with_indifferent_access)
50
+ end
51
+
52
+ def self.try_from_json(str)
53
+ JSON.parse(str)
54
+ rescue JSON::ParserError
55
+ end
56
+
57
+ def self.try_from_yaml_file_path(str)
58
+ YAML.load_file(str.to_s)
59
+ rescue Errno::ENOENT
60
+ end
61
+
29
62
  end
30
63
 
31
64
  require "stellar_base/horizon_client"
@@ -0,0 +1,3 @@
1
+ Dir[File.join(File.dirname(__FILE__), "factories/*.rb")].each do |f|
2
+ require f
3
+ end
@@ -0,0 +1,9 @@
1
+ FactoryBot.define do
2
+
3
+ factory :stellar_base_bridge_callback, class: "StellarBase::BridgeCallback" do
4
+ sequence(:operation_id) { |n| n.to_s }
5
+ from "from"
6
+ amount 100.0
7
+ end
8
+
9
+ end
@@ -0,0 +1,14 @@
1
+ FactoryBot.define do
2
+
3
+ factory(:stellar_base_withdrawal_request, {
4
+ class: "StellarBase::WithdrawalRequest",
5
+ }) do
6
+ asset_type "crypto"
7
+ asset_code "BTC"
8
+ issuer "issuer"
9
+ memo_type "text"
10
+ dest "destination"
11
+ sequence(:memo) {|n| "XYZ#{n}" }
12
+ end
13
+
14
+ end
@@ -1,3 +1,3 @@
1
1
  module StellarBase
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stellar_base-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ace Subido
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-08 00:00:00.000000000 Z
11
+ date: 2018-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: virtus
@@ -136,6 +136,48 @@ dependencies:
136
136
  - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: disposable
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: representable
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: multi_json
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
139
181
  - !ruby/object:Gem::Dependency
140
182
  name: sqlite3
141
183
  requirement: !ruby/object:Gem::Requirement
@@ -192,6 +234,34 @@ dependencies:
192
234
  - - ">="
193
235
  - !ruby/object:Gem::Version
194
236
  version: '0'
237
+ - !ruby/object:Gem::Dependency
238
+ name: factory_bot_rails
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '0'
251
+ - !ruby/object:Gem::Dependency
252
+ name: stellar-sdk
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - ">="
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - ">="
263
+ - !ruby/object:Gem::Version
264
+ version: '0'
195
265
  description: API Endpoints for the Stellar Protocol
196
266
  email:
197
267
  - ace.subido@gmail.com
@@ -207,15 +277,23 @@ files:
207
277
  - app/assets/stylesheets/stellar_base/application.css
208
278
  - app/concepts/stellar_base/application_contract.rb
209
279
  - app/concepts/stellar_base/application_operation.rb
210
- - app/concepts/stellar_base/bridge_callbacks/contracts/process.rb
211
- - app/concepts/stellar_base/bridge_callbacks/operations/process.rb
280
+ - app/concepts/stellar_base/bridge_callbacks/bridge_callback_policy.rb
281
+ - app/concepts/stellar_base/bridge_callbacks/contracts/create.rb
282
+ - app/concepts/stellar_base/bridge_callbacks/operations/create.rb
283
+ - app/concepts/stellar_base/withdrawal_requests/contracts/create.rb
284
+ - app/concepts/stellar_base/withdrawal_requests/operations/create.rb
285
+ - app/concepts/stellar_base/withdrawal_requests/withdrawal_request_policy.rb
212
286
  - app/controllers/stellar_base/application_controller.rb
213
287
  - app/controllers/stellar_base/bridge_callbacks_controller.rb
288
+ - app/controllers/stellar_base/withdraw_controller.rb
214
289
  - app/helpers/stellar_base/application_helper.rb
215
290
  - app/jobs/stellar_base/application_job.rb
216
291
  - app/mailers/stellar_base/application_mailer.rb
217
292
  - app/models/stellar_base/application_record.rb
218
293
  - app/models/stellar_base/bridge_callback.rb
294
+ - app/models/stellar_base/withdrawal_request.rb
295
+ - app/representers/application_representer.rb
296
+ - app/representers/withdrawal_request_representer.rb
219
297
  - app/services/stellar_base/bridge_callbacks/check.rb
220
298
  - app/services/stellar_base/bridge_callbacks/compare.rb
221
299
  - app/services/stellar_base/bridge_callbacks/get_operation.rb
@@ -228,11 +306,25 @@ files:
228
306
  - app/services/stellar_base/bridge_callbacks/mac_payloads/encode_params.rb
229
307
  - app/services/stellar_base/bridge_callbacks/process.rb
230
308
  - app/services/stellar_base/bridge_callbacks/verify_mac_payload.rb
309
+ - app/services/stellar_base/gen_random_string.rb
310
+ - app/services/stellar_base/withdrawal_requests/call_on_withdraw.rb
311
+ - app/services/stellar_base/withdrawal_requests/determine_fee.rb
312
+ - app/services/stellar_base/withdrawal_requests/find_withdrawal_request.rb
313
+ - app/services/stellar_base/withdrawal_requests/gen_memo.rb
314
+ - app/services/stellar_base/withdrawal_requests/process.rb
315
+ - app/twins/application_twin.rb
316
+ - app/twins/withdrawal_request_twin.rb
231
317
  - app/views/layouts/stellar_base/application.html.erb
232
318
  - config/routes.rb
319
+ - db/migrate/20180816014433_create_stellar_base_bridge_callbacks.rb
320
+ - db/migrate/20180816110314_create_stellar_base_withdrawal_requests.rb
321
+ - db/migrate/20180816135847_unique_stellar_base_bridge_callbacks_operation_id.rb
233
322
  - lib/stellar_base-rails.rb
234
323
  - lib/stellar_base.rb
235
324
  - lib/stellar_base/engine.rb
325
+ - lib/stellar_base/factories.rb
326
+ - lib/stellar_base/factories/bridge_callbacks.rb
327
+ - lib/stellar_base/factories/withdrawal_requests.rb
236
328
  - lib/stellar_base/horizon_client.rb
237
329
  - lib/stellar_base/version.rb
238
330
  - lib/tasks/stellar_base_tasks.rake
@@ -256,7 +348,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
256
348
  version: '0'
257
349
  requirements: []
258
350
  rubyforge_project:
259
- rubygems_version: 2.7.3
351
+ rubygems_version: 2.7.7
260
352
  signing_key:
261
353
  specification_version: 4
262
354
  summary: Mountable Stellar API Endpoints for Rails
@@ -1,21 +0,0 @@
1
- module StellarBase
2
- module BridgeCallbacks
3
- module Operations
4
- class Process < ApplicationOperation
5
- step :model!
6
- step Contract::Build(constant: Contracts::Process)
7
- step Contract::Validate(key: :bridge_callback)
8
- step Contract::Persist(method: :sync)
9
- step :process!
10
-
11
- def model!(options, **)
12
- options["model"] = BridgeCallback.new
13
- end
14
-
15
- def process!(options, **)
16
- BridgeCallbacks::Process.(options["model"])
17
- end
18
- end
19
- end
20
- end
21
- end