Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Unreleased (develop)

- added: Auto-assign the `bitcoindepot` referral ID to accounts containing BitcoinDepot whitelabel wallets
- added: Logbox disable option to env.json
- added: Reverse-resolve recipient addresses to ENS / Unstoppable Domains / ZNS names in the send flow, address modal, and transaction history.

Expand Down
56 changes: 56 additions & 0 deletions src/__tests__/util/bitcoinDepotUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { EdgeAccount, EdgeWalletInfoFull } from 'edge-core-js'

import { hasBitcoinDepotWallets } from '../../util/bitcoinDepotUtils'

const makeWalletInfo = (
appIds: string[],
deleted: boolean = false
): EdgeWalletInfoFull => ({
id: 'wallet-id',
type: 'wallet:bitcoin',
keys: {},
appIds,
archived: false,
deleted,
hidden: false,
sortIndex: 0
})

const makeAccount = (allKeys: EdgeWalletInfoFull[]): EdgeAccount =>
// Partial mock; only `allKeys` is examined by the detection helper.
({ allKeys } as unknown as EdgeAccount)

describe('hasBitcoinDepotWallets', () => {
it('detects a wallet created under a BitcoinDepot appId', () => {
const account = makeAccount([
makeWalletInfo(['']),
makeWalletInfo(['com.bitcoindepot.wallet'])
])
expect(hasBitcoinDepotWallets(account)).toBe(true)
})

it('matches appIds case-insensitively', () => {
const account = makeAccount([makeWalletInfo(['com.BitcoinDepot.wallet'])])
expect(hasBitcoinDepotWallets(account)).toBe(true)
})

it('ignores deleted wallets', () => {
const account = makeAccount([
makeWalletInfo(['com.bitcoindepot.wallet'], true)
])
expect(hasBitcoinDepotWallets(account)).toBe(false)
})

it('returns false for ordinary Edge wallets', () => {
const account = makeAccount([
makeWalletInfo(['']),
makeWalletInfo(['app.coinhubatm.wallet'])
])
expect(hasBitcoinDepotWallets(account)).toBe(false)
})

it('returns false for an account with no wallets', () => {
const account = makeAccount([])
expect(hasBitcoinDepotWallets(account)).toBe(false)
})
})
36 changes: 35 additions & 1 deletion src/actions/AccountReferralActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ import {
asMessageTweak,
asPluginTweak
} from '../types/TweakTypes'
import {
BITCOIN_DEPOT_INSTALLER_ID,
hasBitcoinDepotWallets
} from '../util/bitcoinDepotUtils'
import { getActivePromoIds } from '../util/infoUtils'
import { fetchReferral } from '../util/network'
import { lockStartDates, type TweakSource } from '../util/ReferralHelpers'
Expand Down Expand Up @@ -52,6 +56,14 @@ export function loadAccountReferral(
const cache = asDiskReferralCache(JSON.parse(cacheText))
const referral = unpackAccountReferral(JSON.parse(referralText))

// Auto-affiliate BitcoinDepot whitelabel users who have no
// existing affiliation:
const isBitcoinDepot =
referral.installerId == null && hasBitcoinDepotWallets(account)
if (isBitcoinDepot) {
referral.installerId = BITCOIN_DEPOT_INSTALLER_ID
}

// Reference info server promo data to see if:
// 1. Any of these `activePromotions` are no longer valid (e.g. a
// promotion expired)
Expand All @@ -65,6 +77,14 @@ export function loadAccountReferral(

dispatch({ type: 'ACCOUNT_REFERRAL_LOADED', data: { cache, referral } })
await saveAccountReferral(getState())

// Also try activating the matching promotion (with silent errors):
if (isBitcoinDepot) {
await activatePromotion(BITCOIN_DEPOT_INSTALLER_ID)(
dispatch,
getState
).catch(() => undefined)
}
return
} catch (error: any) {}

Expand All @@ -76,18 +96,32 @@ export function loadAccountReferral(
} catch (error: any) {}
}

// Otherwise, just use default values:
// Otherwise, just use default values, auto-affiliating BitcoinDepot
// whitelabel users detected by their wallets:
const isBitcoinDepot = hasBitcoinDepotWallets(account)
const referral: AccountReferral = {
promotions: [],
ignoreAccountSwap: false,
hiddenAccountMessages: {},
activePromotions: []
}
if (isBitcoinDepot) {
referral.installerId = BITCOIN_DEPOT_INSTALLER_ID
}
const cache: ReferralCache = {
accountMessages: [],
accountPlugins: []
}
dispatch({ type: 'ACCOUNT_REFERRAL_LOADED', data: { cache, referral } })
if (isBitcoinDepot) {
// Persist the affiliation, then try activating the matching
// promotion (with silent errors):
await saveAccountReferral(getState())
await activatePromotion(BITCOIN_DEPOT_INSTALLER_ID)(
dispatch,
getState
).catch(() => undefined)
}
}
}

Expand Down
29 changes: 29 additions & 0 deletions src/util/bitcoinDepotUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { EdgeAccount } from 'edge-core-js'

/**
* The referral/affiliate ID auto-assigned to detected BitcoinDepot
* whitelabel users.
*/
export const BITCOIN_DEPOT_INSTALLER_ID = 'bitcoindepot'

/**
* Wallets created by the BitcoinDepot whitelabel app carry its login
* appId in their `appIds` list. Match by substring so minor appId
* variations (e.g. nested appIds) still detect.
*/
const BITCOIN_DEPOT_APP_ID_MATCH = 'bitcoindepot'

/**
* Detects BitcoinDepot whitelabel users by examining the account's wallets.
* Returns true if any non-deleted wallet was created under the BitcoinDepot
* login appId.
*/
export function hasBitcoinDepotWallets(account: EdgeAccount): boolean {
return account.allKeys.some(
walletInfo =>
!walletInfo.deleted &&
walletInfo.appIds.some(appId =>
appId.toLowerCase().includes(BITCOIN_DEPOT_APP_ID_MATCH)
)
)
}
Loading