Ability to add new BCH address to an account

Stephen Paul Weber created

Change summary

config-schema.dhall          |  5 ++++-
config.dhall.sample          |  4 ++--
forms/alt_top_up.rb          |  4 +++-
lib/alt_top_up_form.rb       | 37 +++++++++++++++++++++++++++++++------
lib/customer.rb              |  6 +++---
lib/customer_financials.rb   | 19 ++++++++++++++++++-
sgx_jmp.rb                   |  1 +
test/test_alt_top_up_form.rb | 25 ++++++++++++++++++++-----
8 files changed, 82 insertions(+), 19 deletions(-)

Detailed changes

config-schema.dhall 🔗

@@ -30,7 +30,10 @@
     , rpc_username : Text
     }
 , electrum_notify_url :
-    forall (address : Text) -> forall (customer_id : Text) -> Text
+    forall (address : Text) ->
+    forall (customer_id : Text) ->
+    forall (currency : Text) ->
+      Text
 , interac : Text
 , keep_area_codes : List Text
 , keep_area_codes_in : { account : Text, sip_peer_id : Text, site_id : Text }

config.dhall.sample 🔗

@@ -86,8 +86,8 @@ in
 	activation_amount_accept = 15,
 	credit_card_url = \(jid: Text) -> \(customer_id: Text) ->
 		"https://pay.jmp.chat/${jid}/credit_cards?customer_id=${customer_id}",
-	electrum_notify_url = \(address: Text) -> \(customer_id: Text) ->
-		"https://pay.jmp.chat/electrum_notify?address=${address}&customer_id=${customer_id}",
+	electrum_notify_url = \(address: Text) -> \(customer_id: Text) -> \(currency: Text) ->
+		"https://pay.jmp.chat/electrum_notify?address=${address}&customer_id=${customer_id}&currency=${currency}",
 	adr = "",
 	interac = "",
 	payable = "",

forms/alt_top_up.rb 🔗

@@ -14,6 +14,7 @@ field(
 render "alt_top_up/mailing_address"
 render "alt_top_up/interac" if @currency == :CAD
 render "alt_top_up/btc_addresses"
+render "alt_top_up/bch_addresses"
 
 field(
 	var: "http://jabber.org/protocol/commands#actions",
@@ -21,7 +22,8 @@ field(
 	type: "list-single",
 	options: [
 		{ label: "Done", value: "complete" },
-		{ label: "Add new Bitcoin address to account", value: "BTC" },
+		{ label: "Add new Bitcoin address", value: "BTC" },
+		{ label: "Add new Bitcoin Cash address", value: "BCH" },
 		{ label: "Get single-use Monero address", value: "XMR" },
 		{ label: "Get single-use Ethereum address", value: "ETH" }
 	],

lib/alt_top_up_form.rb 🔗

@@ -4,16 +4,20 @@ require_relative "simple_swap"
 
 class AltTopUpForm
 	def self.for(customer)
-		customer.btc_addresses.then do |addrs|
-			AltTopUpForm.new(customer, addrs)
+		EMPromise.all([
+			customer.btc_addresses,
+			customer.bch_addresses
+		]).then do |(btc, bch)|
+			AltTopUpForm.new(customer, btc, bch)
 		end
 	end
 
-	def initialize(customer, btc_addresses)
+	def initialize(customer, btc_addresses, bch_addresses)
 		@customer = customer
 		@balance = customer.balance
 		@currency = customer.currency
 		@btc_addresses = btc_addresses
+		@bch_addresses = bch_addresses
 	end
 
 	def form
@@ -21,7 +25,8 @@ class AltTopUpForm
 			"alt_top_up",
 			balance: @balance,
 			currency: @currency,
-			btc_addresses: @btc_addresses
+			btc_addresses: @btc_addresses,
+			bch_addresses: @bch_addresses
 		)
 	end
 
@@ -29,8 +34,8 @@ class AltTopUpForm
 		action =
 			form.field("http://jabber.org/protocol/commands#actions")&.value.to_s
 		case action
-		when "BTC"
-			BitcoinAddress.new(@customer)
+		when "BTC", "BCH"
+			ADD_ADDR.fetch(action).new(@customer)
 		when /\A[A-Z]{3}\Z/
 			SimpleSwapAddress.new(@customer, action, @btc_addresses.first)
 		else
@@ -57,6 +62,21 @@ class AltTopUpForm
 		end
 	end
 
+	class BitcoinCashAddress
+		def initialize(customer)
+			@customer = customer
+		end
+
+		def action(reply)
+			@customer.add_bch_address.then do |addr|
+				reply.command << FormTemplate.render(
+					"alt_top_up/bch",
+					bch_addresses: [addr]
+				)
+			end
+		end
+	end
+
 	class SimpleSwapAddress
 		def initialize(customer, currency, btc_address, simple_swap: SimpleSwap.new)
 			@customer = customer
@@ -80,4 +100,9 @@ class AltTopUpForm
 			end
 		end
 	end
+
+	ADD_ADDR = {
+		"BTC" => BitcoinAddress,
+		"BCH" => BitcoinCashAddress
+	}.freeze
 end

lib/customer.rb 🔗

@@ -30,9 +30,9 @@ class Customer
 	               :fwd, :transcription_enabled
 	def_delegators :@usage, :usage_report, :message_usage, :incr_message_usage,
 	               :calling_charges_this_month
-	def_delegators :@financials, :payment_methods, :btc_addresses,
-	               :add_btc_address, :declines, :mark_decline,
-	               :transactions
+	def_delegators :@financials, :payment_methods, :declines, :mark_decline,
+	               :btc_addresses, :add_btc_address, :transactions,
+	               :bch_addresses, :add_bch_address
 
 	def self.extract(customer_id, jid, **kwargs)
 		(kwargs[:parent_customer_id] ? ChildCustomer : Customer).new(

lib/customer_financials.rb 🔗

@@ -21,6 +21,10 @@ class CustomerFinancials
 		REDIS.smembers("jmp_customer_btc_addresses-#{@customer_id}")
 	end
 
+	def bch_addresses
+		REDIS.smembers("jmp_customer_bch_addresses-#{@customer_id}")
+	end
+
 	def add_btc_address
 		REDIS.spopsadd([
 			"jmp_available_btc_addresses",
@@ -28,7 +32,20 @@ class CustomerFinancials
 		]).then do |addr|
 			ELECTRUM.notify(
 				addr,
-				CONFIG[:electrum_notify_url].call(addr, @customer_id)
+				CONFIG[:electrum_notify_url].call(addr, @customer_id, "btc")
+			)
+			addr
+		end
+	end
+
+	def add_bch_address
+		REDIS.spopsadd([
+			"jmp_available_bch_addresses",
+			"jmp_customer_bch_addresses-#{@customer_id}"
+		]).then do |addr|
+			ELECTRUM_BCH.notify(
+				addr,
+				CONFIG[:electrum_notify_url].call(addr, @customer_id, "bch")
 			)
 			addr
 		end

sgx_jmp.rb 🔗

@@ -108,6 +108,7 @@ require_relative "web"
 require_relative "lib/statsd"
 
 ELECTRUM = Electrum.new(**CONFIG[:electrum])
+ELECTRUM_BCH = Electrum.new(**CONFIG[:electrum_bch])
 EM::Hiredis::Client.load_scripts_from("./redis_lua")
 
 Faraday.default_adapter = :em_synchrony

test/test_alt_top_up_form.rb 🔗

@@ -11,6 +11,11 @@ class AltTopUpFormTest < Minitest::Test
 			EMPromise.resolve([]),
 			["jmp_customer_btc_addresses-test"]
 		)
+		CustomerFinancials::REDIS.expect(
+			:smembers,
+			EMPromise.resolve([]),
+			["jmp_customer_bch_addresses-test"]
+		)
 		assert_kind_of(
 			AltTopUpForm,
 			AltTopUpForm.for(customer).sync
@@ -24,6 +29,11 @@ class AltTopUpFormTest < Minitest::Test
 			EMPromise.resolve(["testaddr"]),
 			["jmp_customer_btc_addresses-test"]
 		)
+		CustomerFinancials::REDIS.expect(
+			:smembers,
+			EMPromise.resolve([]),
+			["jmp_customer_bch_addresses-test"]
+		)
 		assert_kind_of(
 			AltTopUpForm,
 			AltTopUpForm.for(customer).sync
@@ -37,6 +47,11 @@ class AltTopUpFormTest < Minitest::Test
 			EMPromise.resolve([]),
 			["jmp_customer_btc_addresses-test"]
 		)
+		CustomerFinancials::REDIS.expect(
+			:smembers,
+			EMPromise.resolve([]),
+			["jmp_customer_bch_addresses-test"]
+		)
 		assert_kind_of(
 			AltTopUpForm,
 			AltTopUpForm.for(customer(plan_name: "test_cad")).sync
@@ -48,7 +63,7 @@ class AltTopUpFormTest < Minitest::Test
 		assert_kind_of(
 			Blather::Stanza::X,
 			AltTopUpForm.new(
-				customer, ["some_addr"]
+				customer, ["some_addr"], []
 			).form
 		)
 	end
@@ -57,7 +72,7 @@ class AltTopUpFormTest < Minitest::Test
 		assert_kind_of(
 			Blather::Stanza::X,
 			AltTopUpForm.new(
-				customer, []
+				customer, [], []
 			).form
 		)
 	end
@@ -69,7 +84,7 @@ class AltTopUpFormTest < Minitest::Test
 		]
 		assert_kind_of(
 			AltTopUpForm::NoOp,
-			AltTopUpForm.new(customer, []).parse(iq_form)
+			AltTopUpForm.new(customer, [], []).parse(iq_form)
 		)
 	end
 
@@ -80,7 +95,7 @@ class AltTopUpFormTest < Minitest::Test
 		]
 		assert_kind_of(
 			AltTopUpForm::BitcoinAddress,
-			AltTopUpForm.new(customer, []).parse(iq_form)
+			AltTopUpForm.new(customer, [], []).parse(iq_form)
 		)
 	end
 
@@ -91,7 +106,7 @@ class AltTopUpFormTest < Minitest::Test
 		]
 		assert_kind_of(
 			AltTopUpForm::SimpleSwapAddress,
-			AltTopUpForm.new(customer, []).parse(iq_form)
+			AltTopUpForm.new(customer, [], []).parse(iq_form)
 		)
 	end
 end