1
+ package wannabit.io.cosmostaion.chain
2
+
3
+ import com.google.gson.Gson
4
+ import com.google.gson.JsonObject
5
+ import wannabit.io.cosmostaion.common.BaseData
6
+ import wannabit.io.cosmostaion.common.jsonRpcResponse
7
+ import wannabit.io.cosmostaion.data.model.req.JsonRpcRequest
8
+ import java.math.BigDecimal
9
+ import java.math.RoundingMode
10
+
11
+ class BtcFetcher (private val chain : BaseChain ) : CosmosFetcher(chain) {
12
+
13
+ var btcBalances = BigDecimal .ZERO
14
+ var btcPendingInput = BigDecimal .ZERO
15
+ var btcPendingOutput = BigDecimal .ZERO
16
+ var btcBlockHeight: Long = 0
17
+ var btcHistory: MutableList <JsonObject > = mutableListOf ()
18
+
19
+ var bitState = true
20
+
21
+ override fun allAssetValue (isUsd : Boolean? ): BigDecimal {
22
+ val price = BaseData .getPrice(chain.coinGeckoId, isUsd)
23
+ return (btcBalances.add(btcPendingInput)).multiply(price).movePointLeft(8 ).setScale(8 , RoundingMode .DOWN )
24
+ }
25
+
26
+ fun mempoolUrl (): String {
27
+ if (chain.isTestnet) {
28
+ return " https://mempool.space/testnet4/"
29
+ }
30
+ return " https://mempool.space/"
31
+ }
32
+
33
+ fun bitType (): String {
34
+ return when (chain.accountKeyType.pubkeyType) {
35
+ PubKeyType .BTC_NATIVE_SEGWIT -> {
36
+ " p2wpkh"
37
+ }
38
+
39
+ PubKeyType .BTC_NESTED_SEGWIT -> {
40
+ " p2sh"
41
+ }
42
+
43
+ PubKeyType .BTC_LEGACY -> {
44
+ " p2pkh"
45
+ }
46
+
47
+ else -> {
48
+ " "
49
+ }
50
+ }
51
+ }
52
+
53
+ fun network (): String {
54
+ return if (! chain.isTestnet) " bitcoin" else " testnet"
55
+ }
56
+
57
+ fun bitVBytesFee (utxo : MutableList <JsonObject >? ): BigDecimal {
58
+ return when (chain.accountKeyType.pubkeyType) {
59
+ PubKeyType .BTC_NATIVE_SEGWIT -> {
60
+ P2WPKH_VBYTE .OVERHEAD .toBigDecimal().add(
61
+ P2WPKH_VBYTE .INPUTS .toBigDecimal().multiply(
62
+ BigDecimal (utxo!! .count())
63
+ )
64
+ ).add(P2WPKH_VBYTE .OUTPUTS .toBigDecimal().multiply(BigDecimal (2 )))
65
+ }
66
+
67
+ PubKeyType .BTC_NESTED_SEGWIT -> {
68
+ P2SH_VBYTE .OVERHEAD .toBigDecimal().add(
69
+ P2SH_VBYTE .INPUTS .toBigDecimal().multiply(
70
+ BigDecimal (utxo!! .count())
71
+ )
72
+ ).add(P2SH_VBYTE .OUTPUTS .toBigDecimal().multiply(BigDecimal (2 )))
73
+ }
74
+
75
+ else -> {
76
+ P2PKH_VBYTE .OVERHEAD .toBigDecimal().add(
77
+ P2PKH_VBYTE .INPUTS .toBigDecimal().multiply(
78
+ BigDecimal (utxo!! .count())
79
+ )
80
+ ).add(P2PKH_VBYTE .OUTPUTS .toBigDecimal().multiply(BigDecimal (2 )))
81
+ }
82
+ }
83
+ }
84
+
85
+ fun txInputString (utxo : MutableList <JsonObject >? ): String {
86
+ var inputString = " "
87
+ when (chain.accountKeyType.pubkeyType) {
88
+ PubKeyType .BTC_NATIVE_SEGWIT -> {
89
+ utxo?.forEach { tx ->
90
+ if (tx[" status" ].asJsonObject[" confirmed" ].asBoolean) {
91
+ val input = """
92
+ {
93
+ hash: '${tx[" txid" ].asString} ',
94
+ index: ${tx[" vout" ].asInt} ,
95
+ witnessUtxo: {
96
+ script: senderPayment.output,
97
+ value: ${tx[" value" ].asLong}
98
+ }
99
+ },
100
+ """
101
+ inputString + = input
102
+ }
103
+ }
104
+ }
105
+
106
+ PubKeyType .BTC_NESTED_SEGWIT -> {
107
+ utxo?.forEach { tx ->
108
+ if (tx[" status" ].asJsonObject[" confirmed" ].asBoolean) {
109
+ val input = """
110
+ {
111
+ hash: '${tx[" txid" ].asString} ',
112
+ index: ${tx[" vout" ].asInt} ,
113
+ redeemScript: senderPayment.redeem.output,
114
+ witnessUtxo: {
115
+ script: senderPayment.output,
116
+ value: ${tx[" value" ].asLong}
117
+ }
118
+ },
119
+ """
120
+ inputString + = input
121
+ }
122
+ }
123
+ }
124
+
125
+ PubKeyType .BTC_LEGACY -> {
126
+ utxo?.forEach { tx ->
127
+ if (tx[" status" ].asJsonObject[" block_hash" ] != null ) {
128
+ val rawTransactionRequest = JsonRpcRequest (
129
+ method = " getrawtransaction" , params = listOf (
130
+ tx[" txid" ].asString, false , tx[" status" ].asJsonObject[" block_hash" ].asString
131
+ )
132
+ )
133
+ val rawTransactionResponse =
134
+ jsonRpcResponse(chain.rpcUrl, rawTransactionRequest)
135
+ val rawTransactionJsonObject = Gson ().fromJson(
136
+ rawTransactionResponse.body?.string(), JsonObject ::class .java
137
+ )
138
+ val nonWitnessUtxoHex = rawTransactionJsonObject[" result" ].asString
139
+
140
+ if (tx[" status" ].asJsonObject[" confirmed" ].asBoolean) {
141
+ val input = """
142
+ {
143
+ hash: '${tx[" txid" ].asString} ',
144
+ index: ${tx[" vout" ].asInt} ,
145
+ nonWitnessUtxo: aTb('${nonWitnessUtxoHex} '),
146
+ },
147
+ """
148
+ inputString + = input
149
+ }
150
+ }
151
+ }
152
+ }
153
+
154
+ else -> {}
155
+ }
156
+ return inputString
157
+ }
158
+
159
+ fun txOutputString (
160
+ receiver : String , toAmount : String , changedValue : String , opReturn : String?
161
+ ): String {
162
+ return if (opReturn?.isNotEmpty() == true ) {
163
+ """
164
+ {
165
+ address: '${receiver} ',
166
+ value: ${toAmount.toLong()}
167
+ },
168
+ {
169
+ address: '${chain.mainAddress} ',
170
+ value: ${changedValue.toLong()}
171
+ },
172
+ m('${opReturn} ')
173
+ """
174
+
175
+ } else {
176
+ """
177
+ {
178
+ address: '${receiver} ',
179
+ value: ${toAmount.toLong()}
180
+ },
181
+ {
182
+ address: '${chain.mainAddress} ',
183
+ value: ${changedValue.toLong()}
184
+ },
185
+ """
186
+ }
187
+ }
188
+ }
189
+
190
+ const val OP_RETURN = 83
191
+
192
+ object P2WPKH_VBYTE {
193
+ const val OVERHEAD = 11
194
+ const val INPUTS = 68
195
+ const val OUTPUTS = 31
196
+ }
197
+
198
+ object P2SH_VBYTE {
199
+ const val OVERHEAD = 10
200
+ const val INPUTS = 297
201
+ const val OUTPUTS = 32
202
+ }
203
+
204
+ object P2PKH_VBYTE {
205
+ const val OVERHEAD = 10
206
+ const val INPUTS = 148
207
+ const val OUTPUTS = 34
208
+ }
0 commit comments