@exodus/bitcoin-api 2.20.0 → 2.20.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,14 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [2.20.1](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@2.20.0...@exodus/bitcoin-api@2.20.1) (2024-07-15)
7
+
8
+ **Note:** Version bump only for package @exodus/bitcoin-api
9
+
10
+
11
+
12
+
13
+
6
14
  ## [2.20.0](https://github.com/ExodusMovement/assets/compare/@exodus/bitcoin-api@2.19.0...@exodus/bitcoin-api@2.20.0) (2024-07-09)
7
15
 
8
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/bitcoin-api",
3
- "version": "2.20.0",
3
+ "version": "2.20.1",
4
4
  "description": "Exodus bitcoin-api",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -66,5 +66,5 @@
66
66
  "type": "git",
67
67
  "url": "git+https://github.com/ExodusMovement/assets.git"
68
68
  },
69
- "gitHead": "0dc141fc6a249ac051a5ca591d46cbc81096a4f3"
69
+ "gitHead": "e1354557f99e81651fad1b25a4f72183587324b6"
70
70
  }
@@ -1,6 +1,11 @@
1
1
  import { AccountState, UtxoCollection } from '@exodus/models'
2
2
 
3
- export function createAccountState({ asset, ordinalsEnabled = false, brc20Enabled = false }) {
3
+ export function createAccountState({
4
+ asset,
5
+ ordinalsEnabled = false,
6
+ brc20Enabled = false,
7
+ isMagicEdenFungibleBalancesEnabled = false,
8
+ }) {
4
9
  const empty = UtxoCollection.createEmpty({
5
10
  currency: asset.currency,
6
11
  })
@@ -22,6 +27,10 @@ export function createAccountState({ asset, ordinalsEnabled = false, brc20Enable
22
27
  defaults.brc20Balances = {}
23
28
  }
24
29
 
30
+ if (isMagicEdenFungibleBalancesEnabled) {
31
+ defaults.magicEdenApiFungibleBalances = []
32
+ }
33
+
25
34
  return class BitcoinAccountState extends AccountState {
26
35
  static defaults = defaults
27
36
  }
package/src/balances.js CHANGED
@@ -13,6 +13,20 @@ export const getBalancesFactory = ({ feeData, getSpendableBalance, ordinalsEnabl
13
13
  assert(asset, 'asset is required')
14
14
  assert(accountState, 'accountState is required')
15
15
  assert(txLog, 'txLog is required')
16
+
17
+ if (accountState.magicEdenApiFungibleBalances) {
18
+ const validBalances = accountState.magicEdenApiFungibleBalances.filter(
19
+ (fungibleBalance) => fungibleBalance.asset.ticker === asset.ticker
20
+ )
21
+ const parsedBalances = validBalances.map(({ balance }) =>
22
+ asset.currency.defaultUnit(balance.balance)
23
+ )
24
+ const totalBalance = asset.currency.defaultUnit(
25
+ parsedBalances.reduce((sum, item) => sum.add(item), asset.currency.ZERO)
26
+ )
27
+ return { balance: totalBalance }
28
+ }
29
+
16
30
  const utxos = getUtxos({ asset, accountState })
17
31
  const balance = utxos.value
18
32
  const spendableBalance = getSpendableBalance({
@@ -4,7 +4,7 @@ import * as bech32 from 'bech32'
4
4
  import assert from 'minimalistic-assert'
5
5
  import { identity, pickBy } from 'lodash'
6
6
  import * as defaultBitcoinjsLib from '@exodus/bitcoinjs-lib'
7
- import * as secp256k1 from 'secp256k1'
7
+ import secp256k1 from 'secp256k1'
8
8
  import { hash160 } from './hash-utils'
9
9
  import { toXOnly } from './bitcoinjs-lib/ecc-utils'
10
10
  import { ecc } from './bitcoinjs-lib/ecc'
package/src/move-funds.js CHANGED
@@ -2,7 +2,7 @@ import wif from 'wif'
2
2
  import { UtxoCollection, Address } from '@exodus/models'
3
3
  import { createInputs, createOutput, getNonWitnessTxs } from './tx-send'
4
4
  import assert from 'minimalistic-assert'
5
- import * as secp256k1 from 'secp256k1'
5
+ import secp256k1 from 'secp256k1'
6
6
 
7
7
  const isValidPrivateKey = (privateKey) => {
8
8
  try {
@@ -0,0 +1,78 @@
1
+ import { Monitor as BitcoinMonitor } from './bitcoin-monitor'
2
+ import fetchFlagrEvaluation from './me-flagr'
3
+
4
+ const FLAGR_KEY = 'bitcoin-balance-api'
5
+
6
+ export class MagicEdenBitcoinMonitor extends BitcoinMonitor {
7
+ constructor(args) {
8
+ super(args)
9
+ this.useMagicEdenMonitor = false
10
+ }
11
+
12
+ async setServer(assetConfig = {}) {
13
+ super.setServer(assetConfig)
14
+
15
+ const walletAccounts = await this.aci.getWalletAccounts({ assetName: this.asset.name })
16
+ const address = await this.aci.getReceiveAddress({
17
+ assetName: this.asset.name,
18
+ walletAccount: walletAccounts[0],
19
+ useCache: true,
20
+ })
21
+ await this.initFlagr(address)
22
+ }
23
+
24
+ async initFlagr(address) {
25
+ if (this.useMagicEdenMonitor) return
26
+ try {
27
+ const response = await fetchFlagrEvaluation(address, FLAGR_KEY)
28
+ if (response.variantKey === 'on') {
29
+ this.useMagicEdenMonitor = true
30
+ }
31
+ } catch (error) {
32
+ console.error('Failed to fetch useMagicEdenMonitor config:', error)
33
+ }
34
+ }
35
+
36
+ async fetchFungibleBalances(walletAccount) {
37
+ if (!this.useMagicEdenMonitor) return
38
+
39
+ const purposes = await this.aci.getSupportedPurposes({
40
+ assetName: this.asset.name,
41
+ walletAccount,
42
+ })
43
+
44
+ const addresses = await Promise.all(
45
+ purposes.map((purpose) =>
46
+ this.aci.getReceiveAddress({
47
+ assetName: this.asset.name,
48
+ walletAccount,
49
+ useCache: true,
50
+ purpose,
51
+ })
52
+ )
53
+ )
54
+
55
+ const response = await fetch('https://api-mainnet.magiceden.io/v1/wallet/balances/fungible', {
56
+ method: 'POST',
57
+ headers: {
58
+ Accept: 'application/json',
59
+ 'Content-Type': 'application/json',
60
+ },
61
+ body: JSON.stringify(
62
+ addresses.map((address) => ({ address, chain: this.asset.baseAssetName }))
63
+ ),
64
+ })
65
+
66
+ if (!response.ok) {
67
+ throw new Error(
68
+ `Unable to fetch bitcoin fungible balanes: API returned ${response.status}: ${response.statusText || 'Unknown Status Text'}`
69
+ )
70
+ }
71
+
72
+ const { balances } = await response.json()
73
+
74
+ return balances
75
+ }
76
+ }
77
+
78
+ export const createMagicEdenBitcoinMonitor = (args) => new MagicEdenBitcoinMonitor(args)
@@ -281,6 +281,12 @@ export class Monitor extends BaseMonitor {
281
281
  newData.mem = { unconfirmedTxAncestor }
282
282
  }
283
283
 
284
+ if (this.fetchFungibleBalances) {
285
+ try {
286
+ newData.magicEdenApiFungibleBalances = await this.fetchFungibleBalances(walletAccount)
287
+ } catch {}
288
+ }
289
+
284
290
  await aci.updateAccountState({
285
291
  assetName,
286
292
  walletAccount,
@@ -1,2 +1,3 @@
1
1
  export * from './bitcoin-monitor'
2
+ export * from './magic-eden-bitcoin-monitor'
2
3
  export * from './bitcoin-monitor-scanner'
@@ -0,0 +1,86 @@
1
+ import { Monitor as BitcoinMonitor } from './bitcoin-monitor'
2
+ import fetchFlagrEvaluation from './me-flagr'
3
+
4
+ const FLAGR_KEY = 'bitcoin-balance-api'
5
+
6
+ export class MagicEdenBitcoinMonitor extends BitcoinMonitor {
7
+ constructor(args) {
8
+ super(args)
9
+ this.useMagicEdenMonitor = false
10
+ }
11
+
12
+ async setServer(assetConfig = {}) {
13
+ super.setServer(assetConfig)
14
+
15
+ const walletAccounts = await this.aci.getWalletAccounts({ assetName: this.asset.name })
16
+ const address = await this.aci.getReceiveAddress({
17
+ assetName: this.asset.name,
18
+ walletAccount: walletAccounts[0],
19
+ useCache: true,
20
+ })
21
+ await this.initFlagr(address)
22
+ }
23
+
24
+ async initFlagr(address) {
25
+ if (this.useMagicEdenMonitor) return
26
+ try {
27
+ const response = await fetchFlagrEvaluation(address, FLAGR_KEY)
28
+ if (response.variantKey === 'on') {
29
+ this.useMagicEdenMonitor = true
30
+ }
31
+ } catch (error) {
32
+ console.error('Failed to fetch useMagicEdenMonitor config:', error)
33
+ }
34
+ }
35
+
36
+ async fetchFungibleBalances(walletAccount) {
37
+ if (!this.useMagicEdenMonitor) return
38
+
39
+ const purposes = await this.aci.getSupportedPurposes({
40
+ assetName: this.asset.name,
41
+ walletAccount,
42
+ })
43
+
44
+ const addresses = await Promise.all(
45
+ purposes.map((purpose) =>
46
+ this.aci.getReceiveAddress({
47
+ assetName: this.asset.name,
48
+ walletAccount,
49
+ useCache: true,
50
+ purpose,
51
+ })
52
+ )
53
+ )
54
+
55
+ const response = await fetch('https://api-mainnet.magiceden.io/v1/wallet/balances/fungible', {
56
+ method: 'POST',
57
+ headers: {
58
+ Accept: 'application/json',
59
+ 'Content-Type': 'application/json',
60
+ },
61
+ body: JSON.stringify(
62
+ addresses.map((address) => ({ address, chain: this.asset.baseAssetName }))
63
+ ),
64
+ })
65
+
66
+ if (!response.ok) {
67
+ throw new Error(
68
+ `Unable to fetch bitcoin fungible balanes: API returned ${response.status}: ${response.statusText || 'Unknown Status Text'}`
69
+ )
70
+ }
71
+
72
+ const { balances } = await response.json()
73
+
74
+ const metadata = new Map()
75
+ balances.forEach((balance) => {
76
+ if (balance.asset?.id && balance.image) {
77
+ metadata.set(balance.asset.id, { imageURL: balance.image })
78
+ }
79
+ })
80
+ this.emit('token-metadata', { source: 'bitcoin', metadata })
81
+
82
+ return balances
83
+ }
84
+ }
85
+
86
+ export const createMagicEdenBitcoinMonitor = (args) => new MagicEdenBitcoinMonitor(args)
@@ -0,0 +1,22 @@
1
+ const FLAGR_URL = 'https://flagr-w.magiceden.io/api/v1/evaluation'
2
+
3
+ const fetchFlagrEvaluation = async (wallet, flagKey) => {
4
+ const response = await fetch(FLAGR_URL, {
5
+ method: 'POST',
6
+ headers: {
7
+ Accept: 'application/json',
8
+ 'Content-Type': 'application/json',
9
+ },
10
+ body: JSON.stringify({ entityContext: { wallet }, flagKey }),
11
+ })
12
+
13
+ if (!response.ok) {
14
+ throw new Error(
15
+ `${FLAGR_URL} returned ${response.status}: ${response.statusText || 'Unknown Status Text'}`
16
+ )
17
+ }
18
+
19
+ return response.json()
20
+ }
21
+
22
+ export default fetchFlagrEvaluation
@@ -6,7 +6,7 @@ import KeyIdentifier from '@exodus/key-identifier'
6
6
 
7
7
  import { getECPair } from '../bitcoinjs-lib'
8
8
 
9
- import * as secp256k1 from 'secp256k1'
9
+ import secp256k1 from 'secp256k1'
10
10
 
11
11
  const ECPair = getECPair()
12
12