Resolve "WIP: UTXO Selection and Storage"
This commit is contained in:
@@ -28,6 +28,25 @@ export class Address {
|
||||
this.addressContents = addressContents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a Cash Address or Legacy Address.
|
||||
*
|
||||
* @param address {string} The address to attempt to decode.
|
||||
*
|
||||
* @throws {Error} If address cannot be decoded.
|
||||
*
|
||||
* @returns {Address} Instance of Address.
|
||||
*/
|
||||
public static from(address: string): Address {
|
||||
// Attempt to decode as a Cash Address.
|
||||
try {
|
||||
return Address.fromCashAddr(address);
|
||||
} catch (_error) {
|
||||
// And if that fails, attempt to decode as a Base58 Address.
|
||||
return Address.fromLegacy(address);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a CashAddr into an Address Entity.
|
||||
*
|
||||
@@ -40,13 +59,19 @@ export class Address {
|
||||
* @example
|
||||
* const address = Address.fromCashAddr('bitcoincash:qpeqv9k9j3l7fepp9du24f9pr5jdu7c2mvn4wwldav')
|
||||
*/
|
||||
public static fromCashAddr(cashAddr: string): Address {
|
||||
// CashAddr
|
||||
const decodeResult = decodeCashAddress(cashAddr);
|
||||
public static fromCashAddr(
|
||||
cashAddr: string,
|
||||
tryPrefixes: Array<string> = ['', 'bitcoincash', 'bchtest', 'bchreg'],
|
||||
): Address {
|
||||
// Try all prefixes (including empty string) until we find a valid decode
|
||||
const decodeResult = tryPrefixes
|
||||
.map((prefix) =>
|
||||
decodeCashAddress(prefix ? `${prefix}:${cashAddr}` : cashAddr),
|
||||
)
|
||||
.find((result) => typeof result !== 'string');
|
||||
|
||||
// If a string is returned, this indicates an error...
|
||||
if (typeof decodeResult === 'string') {
|
||||
throw new Error(decodeResult);
|
||||
if (!decodeResult) {
|
||||
throw new Error('Invalid address format');
|
||||
}
|
||||
|
||||
// Define the encode type in advance.
|
||||
@@ -55,9 +80,11 @@ export class Address {
|
||||
// Determine the encoding to use based on the Decoding Result's Type.
|
||||
switch (decodeResult.type) {
|
||||
case 'p2pkh':
|
||||
case 'p2pkhWithTokens':
|
||||
encodeType = 'P2PKH';
|
||||
break;
|
||||
case 'p2sh':
|
||||
case 'p2shWithTokens':
|
||||
encodeType = decodeResult.payload.length === 32 ? 'P2SH32' : 'P2SH20';
|
||||
break;
|
||||
default:
|
||||
@@ -106,25 +133,6 @@ export class Address {
|
||||
return new Address({ payload: decodeResult.payload, type: encodeType });
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a Cash Address or Legacy Address.
|
||||
*
|
||||
* @param address {string} The address to attempt to decode.
|
||||
*
|
||||
* @throws {Error} If address cannot be decoded.
|
||||
*
|
||||
* @returns {Address} Instance of Address.
|
||||
*/
|
||||
public static fromCashAddrOrLegacy(address: string): Address {
|
||||
// Attempt to decode as a Cash Address.
|
||||
try {
|
||||
return Address.fromCashAddr(address);
|
||||
} catch (error) {
|
||||
// And if that fails, attempt to decode as a Base58 Address.
|
||||
return Address.fromLegacy(address);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate an address from Contract Bytecode.
|
||||
*
|
||||
@@ -136,7 +144,7 @@ export class Address {
|
||||
*/
|
||||
public static fromRedeemScript(
|
||||
contractBytes: Uint8Array,
|
||||
type: 'P2SH20' | 'P2SH32' = 'P2SH32'
|
||||
type: 'P2SH20' | 'P2SH32' = 'P2SH32',
|
||||
): Address {
|
||||
// Do an initial SHA256 on the Contract Bytes.
|
||||
let payload = sha256.hash(contractBytes);
|
||||
@@ -164,7 +172,7 @@ export class Address {
|
||||
*/
|
||||
public static fromRedeemScriptHex(
|
||||
hex: string,
|
||||
type: 'P2SH20' | 'P2SH32' = 'P2SH32'
|
||||
type: 'P2SH20' | 'P2SH32' = 'P2SH32',
|
||||
): Address {
|
||||
if (!isHex(hex)) {
|
||||
throw new Error('Invalid hex');
|
||||
@@ -227,12 +235,12 @@ export class Address {
|
||||
*/
|
||||
public static fromHash160(
|
||||
hash160: Uint8Array,
|
||||
type: AddressType = 'P2PKH'
|
||||
type: AddressType = 'P2PKH',
|
||||
): Address {
|
||||
// If the Hash160 bytes is not 20 in length, it is invalid...
|
||||
if (hash160.length !== 20) {
|
||||
throw new Error(
|
||||
`Invalid hash160 length: Expected 20 (Received ${hash160.length})`
|
||||
`Invalid hash160 length: Expected 20 (Received ${hash160.length})`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -251,7 +259,7 @@ export class Address {
|
||||
*/
|
||||
public static fromHash160Hex(
|
||||
hash160Hex: string,
|
||||
type: AddressType = 'P2PKH'
|
||||
type: AddressType = 'P2PKH',
|
||||
): Address {
|
||||
if (!isHex(hash160Hex)) {
|
||||
throw new Error('Invalid hex');
|
||||
@@ -262,10 +270,14 @@ export class Address {
|
||||
return Address.fromHash160(hash160Bytes, type);
|
||||
}
|
||||
|
||||
public static fromRaw(addressContents: KnownAddressTypeContents) {
|
||||
return new this(addressContents);
|
||||
}
|
||||
|
||||
public static isValid(address: string): boolean {
|
||||
try {
|
||||
Address.fromCashAddrOrLegacy(address);
|
||||
} catch (error) {
|
||||
Address.from(address);
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -275,7 +287,7 @@ export class Address {
|
||||
public static isValidLegacy(address: string): boolean {
|
||||
try {
|
||||
Address.fromLegacy(address);
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -285,7 +297,7 @@ export class Address {
|
||||
public static isValidCashAddr(address: string): boolean {
|
||||
try {
|
||||
Address.fromCashAddr(address);
|
||||
} catch (error) {
|
||||
} catch (_error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -330,25 +342,60 @@ export class Address {
|
||||
*/
|
||||
public toCashAddr(
|
||||
networkPrefix: 'bitcoincash' | 'bchtest' | 'bchreg' = 'bitcoincash',
|
||||
supportTokens = false
|
||||
): string {
|
||||
// Define the encode type in advance.
|
||||
let encodeType: 'p2pkh' | 'p2sh' | 'p2pkhWithTokens' | 'p2shWithTokens';
|
||||
let encodeType: 'p2pkh' | 'p2sh';
|
||||
|
||||
// Determine the encoding to use based on the Address Content's Type.
|
||||
switch (this.addressContents.type) {
|
||||
case 'P2PKH':
|
||||
encodeType = supportTokens ? 'p2pkhWithTokens' : 'p2pkh';
|
||||
encodeType = 'p2pkh';
|
||||
break;
|
||||
case 'P2SH20':
|
||||
encodeType = supportTokens ? 'p2shWithTokens' : 'p2sh';
|
||||
break;
|
||||
case 'P2SH32':
|
||||
encodeType = supportTokens ? 'p2shWithTokens' : 'p2sh';
|
||||
encodeType = 'p2sh';
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unsupported address type: ${this.addressContents.type}`
|
||||
`Unsupported address type: ${this.addressContents.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
// NOTE: This function will throw if the payload is invalid.
|
||||
return encodeCashAddress({
|
||||
prefix: networkPrefix,
|
||||
type: encodeType,
|
||||
payload: this.addressContents.payload,
|
||||
}).address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this address to a Tokens Address format.
|
||||
*
|
||||
* @param networkPrefix {'bitcoincash' | 'bchtest' | 'bchreg'} The Cash Address Network prefix.
|
||||
*
|
||||
* @throws {Error} If address cannot be encoded as a CashAddr.
|
||||
*
|
||||
* @returns {string} The address in Token Address format.
|
||||
*/
|
||||
public toTokenAddress(
|
||||
networkPrefix: 'bitcoincash' | 'bchtest' | 'bchreg' = 'bitcoincash',
|
||||
): string {
|
||||
// Define the encode type in advance.
|
||||
let encodeType: 'p2pkhWithTokens' | 'p2shWithTokens';
|
||||
|
||||
// Determine the encoding to use based on the Address Content's Type.
|
||||
switch (this.addressContents.type) {
|
||||
case 'P2PKH':
|
||||
encodeType = 'p2pkhWithTokens';
|
||||
break;
|
||||
case 'P2SH20':
|
||||
case 'P2SH32':
|
||||
encodeType = 'p2shWithTokens';
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unsupported address type: ${this.addressContents.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -381,7 +428,7 @@ export class Address {
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unsupported address type: ${this.addressContents.type}`
|
||||
`Unsupported address type: ${this.addressContents.type}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -419,4 +466,8 @@ export class Address {
|
||||
|
||||
return lockscriptAsm;
|
||||
}
|
||||
|
||||
public toRaw() {
|
||||
return { ...this.addressContents };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user