Skip to content
Merged
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- changed: Added cashaddr prefixes to ecash.
- changed: Added ecash to bitcoincash forks.

## 3.8.0 (2025-05-19)

- added: Added eCash.
Expand Down
11 changes: 10 additions & 1 deletion src/common/plugin/CurrencyTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,20 @@ export function makeCurrencyTools(
obj: EdgeEncodeUri & EncodeUriMetadata,
_customTokens?: EdgeMetaToken[]
): Promise<string> {
const { publicAddress } = obj
let publicAddress = obj.publicAddress
if (publicAddress === '') {
throw new Error('InvalidPublicAddressError')
}

// Remove any cashaddr prefixes from the public address if they exist.
if (
coinInfo.prefixes.cashaddr?.some(prefix =>
publicAddress.startsWith(`${prefix}:`)
) === true
) {
publicAddress = publicAddress.split(':')[1]
}

// TODO: validate network address

const query: string[] = []
Expand Down
8 changes: 8 additions & 0 deletions src/common/plugin/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,14 @@ export interface CoinInfo {
*/
coinType: number

/**
* Whether the currency should include its cashaddr prefix in the formatted
* addresses.
*
* Defaults to `false`.
*/
includeCashaddrPrefix?: boolean

/**
* Optional sighash value to be passed to AltcoinJS as the sighashType field
* on inputs. This is primarily used by currencies that leverage it as
Expand Down
3 changes: 2 additions & 1 deletion src/common/utxobased/engine/UtxoWalletTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@ export function makeUtxoWalletTools(
return scriptPubkeyToAddress({
scriptPubkey: args.scriptPubkey,
addressType,
coin
coin,
includeCashaddrPrefix: pluginInfo.coinInfo.includeCashaddrPrefix
})
},

Expand Down
2 changes: 1 addition & 1 deletion src/common/utxobased/info/bitcoincash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const engineInfo: EngineInfo = {
}
],
formats: ['bip44', 'bip32'],
forks: [], // 'bitcoinsv' is currently disabled, so not included in the forks
forks: ['ecash'], // 'bitcoinsv' is currently disabled, so not included in the forks
gapLimit: 10,
feeUpdateInterval: 60000,
defaultFeeInfo: {
Expand Down
10 changes: 6 additions & 4 deletions src/common/utxobased/info/ecash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ const currencyInfo: EdgeCurrencyInfo = {

// Explorers:
blockExplorer: 'https://blockchair.com/ecash/block/%s',
addressExplorer: 'https://explorer.e.cash/address/ecash:%s',
transactionExplorer: 'https://explorer.e.cash/tx/%s',
addressExplorer: 'https://blockchair.com/ecash/address/%s',
transactionExplorer: 'https://blockchair.com/ecash/tx/%s',

denominations: [
{ name: 'XEC', multiplier: '100', symbol: 'e' },
Expand Down Expand Up @@ -65,9 +65,10 @@ const engineInfo: EngineInfo = {
},
asBlockbookAddress: asCodec(
raw => {
return asString(raw).split(':')[1]
const address = asString(raw)
return address.startsWith('ecash:') ? address.split(':')[1] : address
},
address => `ecash:${address}`
address => (address.startsWith('ecash:') ? address : `ecash:${address}`)
)
}

Expand All @@ -76,6 +77,7 @@ export const coinInfo: CoinInfo = {
segwit: false,
sighash: Psbt.BCH_SIGHASH_ALL,
coinType: 899,
includeCashaddrPrefix: true,

prefixes: {
messagePrefix: ['\x18Bitcoin Signed Message::\n'],
Expand Down
13 changes: 9 additions & 4 deletions src/common/utxobased/keymanager/keymanager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ export interface ScriptPubkeyToAddressArgs {
addressType: AddressTypeEnum
coin: string
redeemScript?: string
includeCashaddrPrefix?: boolean
}

export interface ScriptPubkeyToAddressReturn {
Expand Down Expand Up @@ -612,7 +613,8 @@ export function scriptPubkeyToAddress(
coin: args.coin
}),
CashaddrTypeEnum.pubkeyhash,
standardPrefixes.cashaddr
standardPrefixes.cashaddr,
args.includeCashaddrPrefix
)
}
if (legacyPrefixes.cashaddr != null) {
Expand All @@ -623,7 +625,8 @@ export function scriptPubkeyToAddress(
coin: args.coin
}),
CashaddrTypeEnum.pubkeyhash,
legacyPrefixes.cashaddr
legacyPrefixes.cashaddr,
args.includeCashaddrPrefix
)
}
payment = payments.p2pkh
Expand All @@ -637,7 +640,8 @@ export function scriptPubkeyToAddress(
coin: args.coin
}),
CashaddrTypeEnum.scripthash,
standardPrefixes.cashaddr
standardPrefixes.cashaddr,
args.includeCashaddrPrefix
)
}
if (legacyPrefixes.cashaddr != null) {
Expand All @@ -648,7 +652,8 @@ export function scriptPubkeyToAddress(
coin: args.coin
}),
CashaddrTypeEnum.scripthash,
legacyPrefixes.cashaddr
legacyPrefixes.cashaddr,
args.includeCashaddrPrefix
)
}
payment = payments.p2sh
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,31 @@ export const bitcoincash: FixtureType = {
{ publicAddress: '1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu' },
'1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu'
],
'cashaddr prefixed address only': [
{
publicAddress: 'bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a'
},
'qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a'
],
'cashaddr prefixed address with amount': [
{
publicAddress: 'bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a',
nativeAmount: '123456780000'
},
'bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=1234.5678'
],
'cashaddr prefixed address with amount and metadata': [
{
publicAddress: 'bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a',
nativeAmount: '123456780000',
currencyCode: 'BCH',
metadata: {
name: 'Johnny BitcoinCash',
notes: 'Hello World, I miss you !'
}
},
'bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a?amount=1234.5678&label=Johnny%20BitcoinCash&message=Hello%20World,%20I%20miss%20you%20!'
],
'address & amount': [
{
publicAddress: 'qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a',
Expand Down
161 changes: 161 additions & 0 deletions test/common/plugin/CurrencyPlugin.fixtures/currencies/ecash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { airbitzSeeds, FixtureType, key, mnemonics } from '../common'

export const ecash: FixtureType = {
pluginId: 'ecash',
WALLET_TYPE: 'wallet:ecash',
WALLET_FORMAT: 'bip44',
'Test Currency code': 'XEC',
key,
xpub:
'xpub6BrtJ1DTaX6hhhGgKiZ4M4LyGzxUBdQmQw6HW7e53x44C9BhiM5wJk5D6h8BJH1suQN9YHei2A9Jr4E4hDBCszKU4xdUZC3dZu1YP2TnW7q',
'invalid key name': {
id: 'unknown',
type: 'wallet:ecash',
keys: {
ecashKeyz: '12345678abcd',
format: 'bip44'
}
},
'invalid wallet type': {
id: 'unknown',
type: 'shitcoin',
keys: { ecashKeyz: '12345678abcd' }
},
importKey: {
validKeys: [...mnemonics],
invalidKeys: [
...airbitzSeeds.map(seed => seed.slice(1)),
...mnemonics.map(mnemonic => mnemonic.split(' ').slice(1).join(' ')),
'bunch of garbly gook !@#$%^&*()'
],
unsupportedKeys: airbitzSeeds
},
parseUri: {
'address only': [
'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
{
publicAddress: 'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
metadata: {}
}
],
'address only2': [
'ecash:qrxcfwvkxumlnq28we8e9v0hjes45nw34gjjtvmlsw',
{
publicAddress: 'qrxcfwvkxumlnq28we8e9v0hjes45nw34gjjtvmlsw',
metadata: {}
}
],
'uri address': [
'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
{
publicAddress: 'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
metadata: {}
}
],
'uri address with amount': [
'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna?amount=12345678.9',
{
publicAddress: 'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
metadata: {},
nativeAmount: '1234567890',
currencyCode: 'XEC'
}
],
'uri address with amount & label': [
'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna?amount=12345678.9&label=Johnny%20eCash',
{
publicAddress: 'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
metadata: {
name: 'Johnny eCash'
},
nativeAmount: '1234567890',
currencyCode: 'XEC'
}
],
'uri address with amount, label & message': [
'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna?amount=12345678.9&label=Johnny%20eCash&message=Hello%20World,%20I%20miss%20you%20!',
{
publicAddress: 'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
metadata: {
name: 'Johnny eCash',
notes: 'Hello World, I miss you !'
},
nativeAmount: '1234567890',
currencyCode: 'XEC'
}
]
},
encodeUri: {
'address only': [
{ publicAddress: 'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna' },
'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna'
],
// Test cashaddr prefix removal - address only (should strip prefix and return same as non-prefixed)
'cashaddr prefixed address only': [
{ publicAddress: 'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna' },
'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna'
],
// Test cashaddr prefix removal - with amount (should strip prefix and return same as non-prefixed)
'cashaddr prefixed address with amount': [
{
publicAddress: 'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
nativeAmount: '123456780'
},
'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna?amount=1234567.8'
],
// Test cashaddr prefix removal - with amount and metadata (should strip prefix and return same as non-prefixed)
'cashaddr prefixed address with amount and metadata': [
{
publicAddress: 'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
nativeAmount: '123456780',
currencyCode: 'XEC',
metadata: {
name: 'Johnny eCash',
notes: 'Hello World, I miss you !'
}
},
'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna?amount=1234567.8&label=Johnny%20eCash&message=Hello%20World,%20I%20miss%20you%20!'
],
'address & amount': [
{
publicAddress: 'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
nativeAmount: '123456780'
},
'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna?amount=1234567.8'
],
'address, amount, and label': [
{
publicAddress: 'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
nativeAmount: '123456780',
currencyCode: 'XEC',
metadata: {
name: 'Johnny eCash'
}
},
'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna?amount=1234567.8&label=Johnny%20eCash'
],
'address, amount, label, & message': [
{
publicAddress: 'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
nativeAmount: '123456780',
currencyCode: 'XEC',
metadata: {
name: 'Johnny eCash',
notes: 'Hello World, I miss you !'
}
},
'ecash:qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna?amount=1234567.8&label=Johnny%20eCash&message=Hello%20World,%20I%20miss%20you%20!'
],
'invalid currencyCode': [
{
publicAddress: 'qpmq6uvs4sktft22lhlmsqpfr53hq03snc0s4nlwna',
nativeAmount: '123456780',
currencyCode: 'INVALID',
metadata: {
name: 'Johnny eCash',
notes: 'Hello World, I miss you !'
}
}
]
}
}
2 changes: 2 additions & 0 deletions test/common/plugin/CurrencyPlugin.fixtures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { bitcoin } from './currencies/bitcoin'
import { bitcoincash } from './currencies/bitcoincash'
import { bitcoinsv } from './currencies/bitcoinsv'
import { digibyte } from './currencies/digibyte'
import { ecash } from './currencies/ecash'
import { feathercoin } from './currencies/feathercoin'
import { groestlcoin } from './currencies/groestlcoin'
import { litecoin } from './currencies/litecoin'
Expand All @@ -14,6 +15,7 @@ export const fixtures: FixtureType[] = [
bitcoincash,
bitcoinsv,
digibyte,
ecash,
feathercoin,
groestlcoin,
litecoin,
Expand Down
Loading
Loading