common.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import { StackingClient } from '@stacks/stacking';
  2. import { StacksTestnet } from '@stacks/network';
  3. import {
  4. getAddressFromPrivateKey,
  5. TransactionVersion,
  6. createStacksPrivateKey,
  7. StacksPrivateKey,
  8. } from '@stacks/transactions';
  9. import { getPublicKeyFromPrivate, publicKeyToBtcAddress } from '@stacks/encryption';
  10. import {
  11. InfoApi,
  12. Configuration,
  13. BlocksApi,
  14. TransactionsApi,
  15. SmartContractsApi,
  16. AccountsApi,
  17. } from '@stacks/blockchain-api-client';
  18. import pino, { Logger } from 'pino';
  19. const serviceName = process.env.SERVICE_NAME || 'JS';
  20. export let logger: Logger;
  21. if (process.env.STACKS_LOG_JSON === '1') {
  22. logger = pino({
  23. level: process.env.LOG_LEVEL || 'info',
  24. name: serviceName,
  25. });
  26. } else {
  27. logger = pino({
  28. name: serviceName,
  29. level: process.env.LOG_LEVEL || 'info',
  30. transport: {
  31. target: 'pino-pretty',
  32. },
  33. // @ts-ignore
  34. options: {
  35. colorize: true,
  36. },
  37. });
  38. }
  39. export const nodeUrl = `http://${process.env.STACKS_CORE_RPC_HOST}:${process.env.STACKS_CORE_RPC_PORT}`;
  40. export const network = new StacksTestnet({ url: nodeUrl });
  41. const apiConfig = new Configuration({
  42. basePath: nodeUrl,
  43. });
  44. export const infoApi = new InfoApi(apiConfig);
  45. export const blocksApi = new BlocksApi(apiConfig);
  46. export const txApi = new TransactionsApi(apiConfig);
  47. export const contractsApi = new SmartContractsApi(apiConfig);
  48. export const accountsApi = new AccountsApi(apiConfig);
  49. export const EPOCH_30_START = parseEnvInt('STACKS_30_HEIGHT', true);
  50. export const EPOCH_25_START = parseEnvInt('STACKS_25_HEIGHT', true);
  51. export const POX_PREPARE_LENGTH = parseEnvInt('POX_PREPARE_LENGTH', true);
  52. export const POX_REWARD_LENGTH = parseEnvInt('POX_REWARD_LENGTH', true);
  53. export type Account = {
  54. privKey: string;
  55. pubKey: string;
  56. stxAddress: string;
  57. btcAddr: string;
  58. signerPrivKey: StacksPrivateKey;
  59. signerPubKey: string;
  60. targetSlots: number;
  61. index: number;
  62. client: StackingClient;
  63. logger: Logger;
  64. };
  65. export const getAccounts = (stackingKeys: string[], stackingSlotDistribution: number[]) =>
  66. stackingKeys.map((privKey, index) => {
  67. const pubKey = getPublicKeyFromPrivate(privKey);
  68. const stxAddress = getAddressFromPrivateKey(privKey, TransactionVersion.Testnet);
  69. const signerPrivKey = createStacksPrivateKey(privKey);
  70. const signerPubKey = getPublicKeyFromPrivate(signerPrivKey.data);
  71. return {
  72. privKey,
  73. pubKey,
  74. stxAddress,
  75. btcAddr: publicKeyToBtcAddress(pubKey),
  76. signerPrivKey: signerPrivKey,
  77. signerPubKey: signerPubKey,
  78. targetSlots: stackingSlotDistribution[index]!,
  79. index,
  80. client: new StackingClient(stxAddress, network),
  81. logger: logger.child({
  82. account: stxAddress,
  83. index: index,
  84. }),
  85. };
  86. });
  87. export const MAX_U128 = 2n ** 128n - 1n;
  88. export const maxAmount = MAX_U128;
  89. export async function waitForSetup(stackingKeys: string[], stackingSlotDistribution: number[]) {
  90. try {
  91. await getAccounts(stackingKeys, stackingSlotDistribution)[0].client.getPoxInfo();
  92. } catch (error) {
  93. if (/(ECONNREFUSED|ENOTFOUND|SyntaxError)/.test(error.cause?.message)) {
  94. console.log(`Stacks node not ready, waiting...`);
  95. }
  96. await new Promise(resolve => setTimeout(resolve, 3000));
  97. return waitForSetup(stackingKeys, stackingSlotDistribution);
  98. }
  99. }
  100. export function parseEnvInt<T extends boolean = false>(
  101. envKey: string,
  102. required?: T
  103. ): T extends true ? number : number | undefined {
  104. let value = process.env[envKey];
  105. if (typeof value === 'undefined') {
  106. if (required) {
  107. throw new Error(`Missing required env var: ${envKey}`);
  108. }
  109. return undefined as T extends true ? number : number | undefined;
  110. }
  111. return parseInt(value, 10);
  112. }
  113. export function burnBlockToRewardCycle(burnBlock: number) {
  114. const cycleLength = BigInt(POX_REWARD_LENGTH);
  115. return Number(BigInt(burnBlock) / cycleLength) + 1;
  116. }
  117. export const EPOCH_30_START_CYCLE = burnBlockToRewardCycle(EPOCH_30_START);
  118. export function isPreparePhase(burnBlock: number) {
  119. return POX_REWARD_LENGTH - (burnBlock % POX_REWARD_LENGTH) < POX_PREPARE_LENGTH;
  120. }
  121. export function didCrossPreparePhase(lastBurnHeight: number, newBurnHeight: number) {
  122. return isPreparePhase(newBurnHeight) && !isPreparePhase(lastBurnHeight);
  123. }