-
Notifications
You must be signed in to change notification settings - Fork 310
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
252 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { c32address, c32addressDecode } from 'c32check'; | ||
import { AddressVersion } from '../constants'; | ||
import { privateKeyToAddress, publicKeyToAddressSingleSig } from '../keys'; | ||
import { AddressString, ContractIdString } from '../types'; | ||
|
||
const C32 = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; | ||
|
||
export type AddressRepr = { hash160: string; contractName?: string } & ( | ||
| { | ||
version: AddressVersion; | ||
versionChar: string; | ||
} | ||
| { | ||
version: AddressVersion; | ||
} | ||
| { | ||
versionChar: string; | ||
} | ||
); | ||
|
||
/** | ||
* Parse a C32 Stacks address string to an address object. | ||
* @param address - The address string to parse. | ||
* @example | ||
* ```ts | ||
* import { Address } from '@stacks/transactions'; | ||
* | ||
* const address = Address.parse('SP000000000000000000002Q6VF78'); | ||
* // { version: 22, versionChar: 'P', hash160: '0000000000000000000000000000000000000000' } | ||
* | ||
* const address = Address.parse('ST000000000000000000002AMW42H.pox'); | ||
* // { version: 22, versionChar: 'P', hash160: '0000000000000000000000000000000000000000', contractName: 'pox' } | ||
* ``` | ||
*/ | ||
export function parse( | ||
address: | ||
| AddressString | ||
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents | ||
| ContractIdString | ||
): AddressRepr { | ||
const [addr, contractName] = address.split('.'); | ||
const parsed = c32addressDecode(addr); | ||
return { | ||
version: parsed[0], | ||
versionChar: C32[parsed[0]], | ||
hash160: parsed[1], | ||
contractName: contractName, | ||
}; | ||
} | ||
|
||
/** | ||
* Stringify an address to the C32 address format. | ||
* @param address - The address object to stringify. | ||
* @example | ||
* ```ts | ||
* import { Address } from '@stacks/transactions'; | ||
* | ||
* const address = Address.stringify({ version: 22, hash160: '0000000000000000000000000000000000000000' }); | ||
* console.log(address); // 'SP000000000000000000002Q6VF78' | ||
* | ||
* const address = Address.stringify({ versionChar: 'P', hash160: '0000000000000000000000000000000000000000' }); | ||
* console.log(address); // 'SP000000000000000000002Q6VF78' | ||
* ``` | ||
*/ | ||
export function stringify(address: AddressRepr): string { | ||
const version = | ||
'version' in address ? address.version : C32.indexOf(address.versionChar.toUpperCase()); | ||
const addr = c32address(version, address.hash160); | ||
|
||
if (address.contractName) return `${addr}.${address.contractName}`; | ||
return addr; | ||
} | ||
|
||
/** | ||
* Convert a private key to a single-sig C32 Stacks address. | ||
* @param privateKey - The private key to convert. | ||
* @returns The address string. | ||
* | ||
* @example | ||
* ```ts | ||
* import { Address } from '@stacks/transactions'; | ||
* | ||
* const address = Address.fromPrivateKey('73a2f291df5a8ce3ceb668a25ac7af45639513af7596d710ddf59f64f484fd2801'); | ||
* // 'SP10J81WVGVB3M4PHQN4Q4G0R8586TBJH948RESDR' | ||
* | ||
* const address = Address.fromPrivateKey('73a2f291df5a8ce3ceb668a25ac7af45639513af7596d710ddf59f64f484fd2801', 'testnet'); | ||
* // 'ST10J81WVGVB3M4PHQN4Q4G0R8586TBJH94CGRESQ' | ||
* ``` | ||
*/ | ||
export const fromPrivateKey = privateKeyToAddress; | ||
|
||
/** | ||
* Convert a public key to a single-sig C32 Stacks address. | ||
* @param publicKey - The public key to convert. | ||
* @returns The address string. | ||
* | ||
* @example | ||
* ```ts | ||
* import { Address } from '@stacks/transactions'; | ||
* | ||
* const address = Address.fromPublicKey('0316e35d38b52d4886e40065e4952a49535ce914e02294be58e252d1998f129b19'); | ||
* // 'SP10J81WVGVB3M4PHQN4Q4G0R8586TBJH948RESDR' | ||
* | ||
* const address = Address.fromPublicKey('0316e35d38b52d4886e40065e4952a49535ce914e02294be58e252d1998f129b19', 'testnet'); | ||
* // 'ST10J81WVGVB3M4PHQN4Q4G0R8586TBJH94CGRESQ' | ||
* ``` | ||
*/ | ||
export const fromPublicKey = publicKeyToAddressSingleSig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * as Address from './address'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import { Address } from '../../src'; | ||
|
||
describe('Address Namespace', () => { | ||
describe(Address.parse, () => { | ||
it('should parse addresses correctly', () => { | ||
const address = Address.parse('SP000000000000000000002Q6VF78'); | ||
expect(address).toEqual({ version: 22, versionChar: 'P', hash160: '0'.repeat(40) }); | ||
|
||
const addressWithVersionChar = Address.parse('SP000000000000000000002Q6VF78'); | ||
expect(addressWithVersionChar).toEqual({ | ||
version: 22, | ||
versionChar: 'P', | ||
hash160: '0'.repeat(40), | ||
}); | ||
|
||
const addressWithContractId = Address.parse('ST000000000000000000002AMW42H.pox'); | ||
expect(addressWithContractId).toEqual({ | ||
version: 26, | ||
versionChar: 'T', | ||
hash160: '0'.repeat(40), | ||
contractName: 'pox', | ||
}); | ||
}); | ||
|
||
it('should throw an error for invalid addresses', () => { | ||
expect(() => Address.parse('invalid')).toThrow('Invalid c32 address: must start with "S"'); | ||
}); | ||
|
||
it('should throw an error for checksum mismatches', () => { | ||
expect(() => Address.parse('ST000BLABLA')).toThrow( | ||
'Invalid c32check string: checksum mismatch' | ||
); | ||
}); | ||
}); | ||
|
||
describe(Address.stringify, () => { | ||
it('should return the correct address', () => { | ||
const addressWithVersion = Address.stringify({ version: 22, hash160: '0'.repeat(40) }); | ||
expect(addressWithVersion).toBe('SP000000000000000000002Q6VF78'); | ||
|
||
const addressWithVersionChar = Address.stringify({ | ||
versionChar: 'P', | ||
hash160: '0'.repeat(40), | ||
}); | ||
expect(addressWithVersionChar).toBe('SP000000000000000000002Q6VF78'); | ||
|
||
const addressWithContractId = Address.stringify({ | ||
versionChar: 'T', | ||
hash160: '0'.repeat(40), | ||
contractName: 'pox', | ||
}); | ||
expect(addressWithContractId).toBe('ST000000000000000000002AMW42H.pox'); | ||
}); | ||
}); | ||
|
||
describe(Address.fromPrivateKey, () => { | ||
it('should return the correct address', () => { | ||
const address = Address.fromPrivateKey( | ||
'73a2f291df5a8ce3ceb668a25ac7af45639513af7596d710ddf59f64f484fd2801' | ||
); | ||
expect(address).toBe('SP10J81WVGVB3M4PHQN4Q4G0R8586TBJH948RESDR'); | ||
|
||
const addressTestnet = Address.fromPrivateKey( | ||
'73a2f291df5a8ce3ceb668a25ac7af45639513af7596d710ddf59f64f484fd2801', | ||
'testnet' | ||
); | ||
expect(addressTestnet).toBe('ST10J81WVGVB3M4PHQN4Q4G0R8586TBJH94CGRESQ'); | ||
}); | ||
}); | ||
|
||
describe(Address.fromPublicKey, () => { | ||
it('should return the correct address', () => { | ||
const address = Address.fromPublicKey( | ||
'0316e35d38b52d4886e40065e4952a49535ce914e02294be58e252d1998f129b19' | ||
); | ||
expect(address).toBe('SP10J81WVGVB3M4PHQN4Q4G0R8586TBJH948RESDR'); | ||
|
||
const addressTestnet = Address.fromPublicKey( | ||
'0316e35d38b52d4886e40065e4952a49535ce914e02294be58e252d1998f129b19', | ||
'testnet' | ||
); | ||
expect(addressTestnet).toBe('ST10J81WVGVB3M4PHQN4Q4G0R8586TBJH94CGRESQ'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Address } from '../../src'; | ||
|
||
describe('Address', () => { | ||
describe(Address.parse, () => {}); | ||
|
||
describe(Address.stringify, () => { | ||
it('should return the correct address', () => { | ||
const addressWithVersion = Address.stringify({ version: 22, hash160: '0'.repeat(40) }); | ||
expect(addressWithVersion).toBe('SP000000000000000000002Q6VF78'); | ||
|
||
const addressWithVersionChar = Address.stringify({ | ||
versionChar: 'P', | ||
hash160: '0'.repeat(40), | ||
}); | ||
expect(addressWithVersionChar).toBe('SP000000000000000000002Q6VF78'); | ||
|
||
const addressWithContractId = Address.stringify({ | ||
versionChar: 'T', | ||
hash160: '0'.repeat(40), | ||
contractName: 'pox', | ||
}); | ||
expect(addressWithContractId).toBe('ST000000000000000000002Q6VF78.pox'); | ||
}); | ||
}); | ||
}); |