-
Notifications
You must be signed in to change notification settings - Fork 312
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat!: update to new network object (#1623)
* feat: split network into object and api opts * fix: update imports --------- Co-authored-by: janniks <[email protected]>
- Loading branch information
Showing
26 changed files
with
997 additions
and
1,418 deletions.
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
2 changes: 1 addition & 1 deletion
2
packages/network/tests/fetch.test.ts → packages/common/tests/fetch.test.ts
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,42 @@ | ||
/** | ||
* The chain ID (unsigned 32-bit integer), used so transactions can't be replayed on other chains. | ||
* Similar to the {@link TransactionVersion}. | ||
*/ | ||
export enum ChainId { | ||
Mainnet = 0x00000001, | ||
Testnet = 0x80000000, | ||
} | ||
|
||
/** | ||
* The **peer** network ID. | ||
* Typically not used in signing, but used for broadcasting to the P2P network. | ||
* It can also be used to determine the parent of a subnet. | ||
* | ||
* **Attention:** | ||
* For mainnet/testnet the v2/info response `.network_id` refers to the chain ID. | ||
* For subnets the v2/info response `.network_id` refers to the peer network ID and the chain ID (they are the same for subnets). | ||
* The `.parent_network_id` refers to the actual peer network ID (of the parent) in both cases. | ||
*/ | ||
export enum PeerNetworkId { | ||
Mainnet = 0x17000000, | ||
Testnet = 0xff000000, | ||
} | ||
|
||
export const DEFAULT_CHAIN_ID = ChainId.Mainnet; | ||
|
||
/** | ||
* The transaction version, used so transactions can't be replayed on other networks. | ||
* Similar to the {@link ChainId}. | ||
* Used internally for serializing and deserializing transactions. | ||
*/ | ||
export enum TransactionVersion { | ||
Mainnet = 0x00, | ||
Testnet = 0x80, | ||
} | ||
|
||
export const DEFAULT_TRANSACTION_VERSION = TransactionVersion.Mainnet; | ||
|
||
/** @ignore */ | ||
export function whenTransactionVersion(transactionVersion: TransactionVersion) { | ||
return <T>(map: Record<TransactionVersion, T>): T => map[transactionVersion]; | ||
} |
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 |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './constants'; | ||
export * from './network'; |
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 |
---|---|---|
@@ -1,215 +1,65 @@ | ||
import { TransactionVersion, ChainID } from '@stacks/common'; | ||
import { createFetchFn, FetchFn } from '@stacks/common'; | ||
|
||
export const HIRO_MAINNET_DEFAULT = 'https://api.mainnet.hiro.so'; | ||
export const HIRO_TESTNET_DEFAULT = 'https://api.testnet.hiro.so'; | ||
export const HIRO_MOCKNET_DEFAULT = 'http://localhost:3999'; | ||
|
||
/** | ||
* Used for constructing Network instances | ||
* @related {@link StacksNetwork}, {@link StacksMainnet}, {@link StacksTestnet}, {@link StacksDevnet}, {@link StacksMocknet} | ||
*/ | ||
export interface NetworkConfig { | ||
/** The base API/node URL for the network fetch calls */ | ||
url: string; | ||
/** An optional custom fetch function to override default behaviors */ | ||
fetchFn?: FetchFn; | ||
import { DEVNET_URL, HIRO_MAINNET_URL, HIRO_TESTNET_URL } from '@stacks/common'; | ||
import { ChainId, PeerNetworkId, TransactionVersion } from './constants'; | ||
|
||
export interface StacksNetwork { | ||
chainId: number; | ||
transactionVersion: number; // todo: txVersion better? | ||
peerNetworkId: number; | ||
magicBytes: string; | ||
// todo: add check32 character bytes string | ||
} | ||
|
||
export const STACKS_MAINNET: StacksNetwork = { | ||
chainId: ChainId.Mainnet, | ||
transactionVersion: TransactionVersion.Mainnet, | ||
peerNetworkId: PeerNetworkId.Mainnet, | ||
magicBytes: 'X2', // todo: comment bytes version of magic bytes | ||
}; | ||
|
||
export const STACKS_TESTNET: StacksNetwork = { | ||
chainId: ChainId.Testnet, | ||
transactionVersion: TransactionVersion.Testnet, | ||
peerNetworkId: PeerNetworkId.Testnet, | ||
magicBytes: 'T2', // todo: comment bytes version of magic bytes | ||
}; | ||
|
||
export const STACKS_DEVNET: StacksNetwork = { | ||
...STACKS_TESTNET, | ||
magicBytes: 'id', // todo: comment bytes version of magic bytes | ||
}; | ||
export const STACKS_MOCKNET: StacksNetwork = { ...STACKS_DEVNET }; | ||
|
||
/** @ignore internal */ | ||
export const StacksNetworks = ['mainnet', 'testnet', 'devnet', 'mocknet'] as const; | ||
/** The enum-style names of different common Stacks networks */ | ||
export type StacksNetworkName = (typeof StacksNetworks)[number]; | ||
|
||
/** | ||
* The base class for Stacks networks. Typically used via its subclasses. | ||
* @related {@link StacksMainnet}, {@link StacksTestnet}, {@link StacksDevnet}, {@link StacksMocknet} | ||
*/ | ||
export class StacksNetwork { | ||
version: TransactionVersion = TransactionVersion.Mainnet; | ||
chainId: ChainID = ChainID.Mainnet; | ||
bnsLookupUrl = 'https://api.mainnet.hiro.so'; | ||
broadcastEndpoint = '/v2/transactions'; | ||
transferFeeEstimateEndpoint = '/v2/fees/transfer'; | ||
transactionFeeEstimateEndpoint = '/v2/fees/transaction'; | ||
accountEndpoint = '/v2/accounts'; | ||
contractAbiEndpoint = '/v2/contracts/interface'; | ||
readOnlyFunctionCallEndpoint = '/v2/contracts/call-read'; | ||
|
||
readonly coreApiUrl: string; | ||
|
||
fetchFn: FetchFn; | ||
|
||
constructor(networkConfig: NetworkConfig) { | ||
this.coreApiUrl = networkConfig.url; | ||
this.fetchFn = networkConfig.fetchFn ?? createFetchFn(); | ||
} | ||
|
||
/** A static network constructor from a network name */ | ||
static fromName = (networkName: StacksNetworkName): StacksNetwork => { | ||
switch (networkName) { | ||
case 'mainnet': | ||
return new StacksMainnet(); | ||
case 'testnet': | ||
return new StacksTestnet(); | ||
case 'devnet': | ||
return new StacksDevnet(); | ||
case 'mocknet': | ||
return new StacksMocknet(); | ||
default: | ||
throw new Error( | ||
`Invalid network name provided. Must be one of the following: ${StacksNetworks.join( | ||
', ' | ||
)}` | ||
); | ||
} | ||
}; | ||
|
||
/** @ignore internal */ | ||
static fromNameOrNetwork = (network: StacksNetworkName | StacksNetwork) => { | ||
if (typeof network !== 'string' && 'version' in network) { | ||
return network; | ||
} | ||
|
||
return StacksNetwork.fromName(network); | ||
}; | ||
|
||
/** Returns `true` if the network is configured to 'mainnet', based on the TransactionVersion */ | ||
isMainnet = () => this.version === TransactionVersion.Mainnet; | ||
getBroadcastApiUrl = () => `${this.coreApiUrl}${this.broadcastEndpoint}`; | ||
getTransferFeeEstimateApiUrl = () => `${this.coreApiUrl}${this.transferFeeEstimateEndpoint}`; | ||
getTransactionFeeEstimateApiUrl = () => | ||
`${this.coreApiUrl}${this.transactionFeeEstimateEndpoint}`; | ||
getAccountApiUrl = (address: string) => | ||
`${this.coreApiUrl}${this.accountEndpoint}/${address}?proof=0`; | ||
getAccountExtendedBalancesApiUrl = (address: string) => | ||
`${this.coreApiUrl}/extended/v1/address/${address}/balances`; | ||
getAbiApiUrl = (address: string, contract: string) => | ||
`${this.coreApiUrl}${this.contractAbiEndpoint}/${address}/${contract}`; | ||
getReadOnlyFunctionCallApiUrl = ( | ||
contractAddress: string, | ||
contractName: string, | ||
functionName: string | ||
) => | ||
`${this.coreApiUrl}${ | ||
this.readOnlyFunctionCallEndpoint | ||
}/${contractAddress}/${contractName}/${encodeURIComponent(functionName)}`; | ||
getInfoUrl = () => `${this.coreApiUrl}/v2/info`; | ||
getBlockTimeInfoUrl = () => `${this.coreApiUrl}/extended/v1/info/network_block_times`; | ||
getPoxInfoUrl = () => `${this.coreApiUrl}/v2/pox`; | ||
getRewardsUrl = (address: string, options?: any) => { | ||
let url = `${this.coreApiUrl}/extended/v1/burnchain/rewards/${address}`; | ||
if (options) { | ||
url = `${url}?limit=${options.limit}&offset=${options.offset}`; | ||
} | ||
return url; | ||
}; | ||
getRewardsTotalUrl = (address: string) => | ||
`${this.coreApiUrl}/extended/v1/burnchain/rewards/${address}/total`; | ||
getRewardHoldersUrl = (address: string, options?: any) => { | ||
let url = `${this.coreApiUrl}/extended/v1/burnchain/reward_slot_holders/${address}`; | ||
if (options) { | ||
url = `${url}?limit=${options.limit}&offset=${options.offset}`; | ||
} | ||
return url; | ||
}; | ||
getStackerInfoUrl = (contractAddress: string, contractName: string) => | ||
`${this.coreApiUrl}${this.readOnlyFunctionCallEndpoint} | ||
${contractAddress}/${contractName}/get-stacker-info`; | ||
getDataVarUrl = (contractAddress: string, contractName: string, dataVarName: string) => | ||
`${this.coreApiUrl}/v2/data_var/${contractAddress}/${contractName}/${dataVarName}?proof=0`; | ||
getMapEntryUrl = (contractAddress: string, contractName: string, mapName: string) => | ||
`${this.coreApiUrl}/v2/map_entry/${contractAddress}/${contractName}/${mapName}?proof=0`; | ||
getNameInfo(fullyQualifiedName: string) { | ||
/* | ||
TODO: Update to v2 API URL for name lookups | ||
*/ | ||
const nameLookupURL = `${this.bnsLookupUrl}/v1/names/${fullyQualifiedName}`; | ||
return this.fetchFn(nameLookupURL) | ||
.then(resp => { | ||
if (resp.status === 404) { | ||
throw new Error('Name not found'); | ||
} else if (resp.status !== 200) { | ||
throw new Error(`Bad response status: ${resp.status}`); | ||
} else { | ||
return resp.json(); | ||
} | ||
}) | ||
.then(nameInfo => { | ||
// the returned address _should_ be in the correct network --- | ||
// stacks node gets into trouble because it tries to coerce back to mainnet | ||
// and the regtest transaction generation libraries want to use testnet addresses | ||
if (nameInfo.address) { | ||
return Object.assign({}, nameInfo, { address: nameInfo.address }); | ||
} else { | ||
return nameInfo; | ||
} | ||
}); | ||
export function networkFromName(name: StacksNetworkName) { | ||
switch (name) { | ||
case 'mainnet': | ||
return STACKS_MAINNET; | ||
case 'testnet': | ||
return STACKS_TESTNET; | ||
case 'devnet': | ||
return STACKS_DEVNET; | ||
case 'mocknet': | ||
return STACKS_MOCKNET; | ||
default: | ||
throw new Error(`Unknown network name: ${name}`); | ||
} | ||
} | ||
|
||
/** | ||
* A {@link StacksNetwork} with the parameters for the Stacks mainnet. | ||
* Pass a `url` option to override the default Hiro hosted Stacks node API. | ||
* Pass a `fetchFn` option to customize the default networking functions. | ||
* @example | ||
* ``` | ||
* const network = new StacksMainnet(); | ||
* const network = new StacksMainnet({ url: "https://api.mainnet.hiro.so" }); | ||
* const network = new StacksMainnet({ fetch: createFetchFn() }); | ||
* ``` | ||
* @related {@link createFetchFn}, {@link createApiKeyMiddleware} | ||
*/ | ||
export class StacksMainnet extends StacksNetwork { | ||
version = TransactionVersion.Mainnet; | ||
chainId = ChainID.Mainnet; | ||
|
||
constructor(opts?: Partial<NetworkConfig>) { | ||
super({ | ||
url: opts?.url ?? HIRO_MAINNET_DEFAULT, | ||
fetchFn: opts?.fetchFn, | ||
}); | ||
} | ||
export function networkFrom(network: StacksNetworkName | StacksNetwork) { | ||
if (typeof network === 'string') return networkFromName(network); | ||
return network; | ||
} | ||
|
||
/** | ||
* A {@link StacksNetwork} with the parameters for the Stacks testnet. | ||
* Pass a `url` option to override the default Hiro hosted Stacks node API. | ||
* Pass a `fetchFn` option to customize the default networking functions. | ||
* @example | ||
* ``` | ||
* const network = new StacksTestnet(); | ||
* const network = new StacksTestnet({ url: "https://api.testnet.hiro.so" }); | ||
* const network = new StacksTestnet({ fetch: createFetchFn() }); | ||
* ``` | ||
* @related {@link createFetchFn}, {@link createApiKeyMiddleware} | ||
*/ | ||
export class StacksTestnet extends StacksNetwork { | ||
version = TransactionVersion.Testnet; | ||
chainId = ChainID.Testnet; | ||
export function deriveDefaultUrl(network: StacksNetwork | StacksNetworkName) { | ||
network = networkFrom(network); | ||
|
||
constructor(opts?: Partial<NetworkConfig>) { | ||
super({ | ||
url: opts?.url ?? HIRO_TESTNET_DEFAULT, | ||
fetchFn: opts?.fetchFn, | ||
}); | ||
} | ||
return !network || network.transactionVersion === TransactionVersion.Mainnet | ||
? HIRO_MAINNET_URL // default to mainnet if no network is given or txVersion is mainnet | ||
: network.magicBytes === 'id' | ||
? DEVNET_URL // default to devnet if magicBytes are devnet | ||
: HIRO_TESTNET_URL; | ||
} | ||
|
||
/** | ||
* A {@link StacksNetwork} using the testnet parameters, but `localhost:3999` as the API URL. | ||
*/ | ||
export class StacksMocknet extends StacksNetwork { | ||
version = TransactionVersion.Testnet; | ||
chainId = ChainID.Testnet; | ||
|
||
constructor(opts?: Partial<NetworkConfig>) { | ||
super({ | ||
url: opts?.url ?? HIRO_MOCKNET_DEFAULT, | ||
fetchFn: opts?.fetchFn, | ||
}); | ||
} | ||
} | ||
|
||
/** Alias for {@link StacksMocknet} */ | ||
export const StacksDevnet = StacksMocknet; |
Oops, something went wrong.