Skip to content

Commit

Permalink
fix: address stacking lib feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
yknl committed Nov 20, 2020
1 parent 83c78f4 commit 61f5bb8
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 77 deletions.
2 changes: 1 addition & 1 deletion packages/network/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class StacksMainnet implements StacksNetwork {
contractAbiEndpoint = '/v2/contracts/interface';
readOnlyFunctionCallEndpoint = '/v2/contracts/call-read';

isMainnet= () => this.version === TransactionVersion.Mainnet;
isMainnet = () => this.version === TransactionVersion.Mainnet;
getBroadcastApiUrl = () => `${this.coreApiUrl}${this.broadcastEndpoint}`;
getTransferFeeEstimateApiUrl = () => `${this.coreApiUrl}${this.transferFeeEstimateEndpoint}`;
getAccountApiUrl = (address: string) =>
Expand Down
89 changes: 61 additions & 28 deletions packages/stacking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,17 @@ npm install @stacks/stacking

## Initialization
```typescript
const { StacksTestnet } = require('@stacks/network');
const { Stacker } = require('@stacks/stacking');
import { StacksTestnet } from '@stacks/network';
import { StackingClient } from '@stacks/stacking';

const address = 'ST3XKKN4RPV69NN1PHFDNX3TYKXT7XPC4N8KC1ARH';
const network = new StacksTestnet();
const stacker = new Stacker(address, network);
```

## Check stacking eligibility

```typescript
const stackingEligibility = await stacker.canLockStx({poxAddress, cycles});
const stackingEligibility = await client.canStack({poxAddress, cycles});

// stackingEligibility:
// {
Expand All @@ -29,9 +28,52 @@ const stackingEligibility = await stacker.canLockStx({poxAddress, cycles});
// }
```

## Stack STX
```typescript
const poxAddress = '1Xik14zRm29UsyS6DjhYg4iZeZqsDa8D3';
const amountMicroStx = new BN(100000000000);
const cycles = 10;
const key = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const burnBlockHeight = 2000;

const stackingResults = await client.stack({
amountMicroStx,
poxAddress,
cycles,
key,
burnBlockHeight
});

// stackingResults:
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// }
```

## Will Stacking be executed in the next cycle?
```typescript
const stackingEnabledNextCycle = await client.isStackingEnabledNextCycle();

// true or false
```

## How long (in seconds) is a Stacking cycle?
```typescript
const cycleDuration = await client.getCycleDuration();

// 120
```

## How much time is left (in seconds) until the next cycle begins?
```typescript
const secondsUntilNextCycle = await client.getSecondsUntilNextCycle();

// 600000
```

## Get PoX info
```typescript
const poxInfo = await stacker.getPoxInfo();
const poxInfo = await client.getPoxInfo();

// poxInfo:
// {
Expand All @@ -47,39 +89,29 @@ const poxInfo = await stacker.getPoxInfo();
// }
```

## Get Stacks node info
```typescript
const coreInfo = await client.getCoreInfo();
```

## Get account balance
```typescript
const responseBalanceInfo = await stacker.getAccountBalance();
const responseBalanceInfo = await client.getAccountBalance();

// 800000000000
```

## Stack STX
```typescript
const poxAddress = '1Xik14zRm29UsyS6DjhYg4iZeZqsDa8D3';
const amountMicroStx = new BN(100000000000);
const cycles = 10;
const key = 'd48f215481c16cbe6426f8e557df9b78895661971d71735126545abddcd5377001';
const burnBlockHeight = 2000;
## Does account have sufficient STX to meet minimum threshold?
```js
const hasMinStxAmount = await client.hasMinimumStx();

const stackingResults = await stacker.lockStx({
amountMicroStx,
poxAddress,
cycles,
key,
burnBlockHeight
});

// stackingResults:
// {
// txid: '0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481',
// transaction: 'https://testnet-explorer.now.sh/txid/0xf6e9dbf6a26c1b73a14738606cb2232375d1b440246e6bbc14a45b3a66618481'
// }
// true or false
```


## Get account stacking status
```typescript
const stackingStatus = await stacker.getStatus();
const stackingStatus = await client.getStatus();

// stackingStatus:
// {
Expand All @@ -91,4 +123,5 @@ const stackingStatus = await stacker.getStatus();
// hashbytes: '05cf52a44bf3e6829b4f8c221cc675355bf83b7d'
// }
// }
```
```

35 changes: 17 additions & 18 deletions packages/stacking/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,12 @@ import {
TupleCV
} from '@stacks/transactions';
import {
StacksNetwork,
StacksMainnet
StacksNetwork
} from '@stacks/network';
import BN from 'bn.js';
import { StackingErrors } from './constants';
import { fetchPrivate } from '@stacks/common';
import { convertBTCAddress } from './utils';
import { decodeBtcAddress } from './utils';

export interface PoxInfo {
contract_id: string;
Expand Down Expand Up @@ -93,20 +92,20 @@ export interface CanLockStxOptions {
* @param {String} poxAddress - the reward Bitcoin address
* @param {number} cycles - number of cycles to lock
* @param {BigNum} amountMicroStx - number of microstacks to lock
* @param {number} burnBlockHeight -the burnchain block height to begin lock
* @param {number} burnBlockHeight - the burnchain block height to begin lock
*/
export interface LockStxOptions {
key: string;
privateKey: string;
cycles: number;
poxAddress: string;
amountMicroStx: BN;
burnBlockHeight: number;
}

export class Stacker {
export class StackingClient {
constructor(
public address: string,
public network: StacksNetwork = new StacksMainnet()) {}
public network: StacksNetwork) {}

/**
* Get stacks node info
Expand Down Expand Up @@ -178,7 +177,7 @@ export class Stacker {
*
* @returns {Promise<number>} that resolves to a number if the operation succeeds
*/
async secondsUntilNextCycle(): Promise<number> {
async getSecondsUntilNextCycle(): Promise<number> {
const poxInfoPromise = this.getPoxInfo();
const targetBlockTimePromise = await this.getTargetBlockTime();
const coreInfoPromise = this.getCoreInfo();
Expand All @@ -199,7 +198,7 @@ export class Stacker {
*
* @returns {Promise<boolean>} that resolves to a bool if the operation succeeds
*/
async stackingEnabledNextCycle(): Promise<boolean> {
async isStackingEnabledNextCycle(): Promise<boolean> {
return (await this.getPoxInfo()).rejection_votes_left_required > 0;
}

Expand All @@ -208,7 +207,7 @@ export class Stacker {
*
* @returns {Promise<boolean>} that resolves to a bool if the operation succeeds
*/
async hasMinimumRequiredStxAmount(): Promise<boolean> {
async hasMinimumStx(): Promise<boolean> {
const balance: BN = await this.getAccountBalance();
// TODO pox info should use string type instead of number
const min: BN = new BN((await this.getPoxInfo()).min_amount_ustx.toString());
Expand All @@ -222,7 +221,7 @@ export class Stacker {
*
* @returns {Promise<StackingEligibility>} that resolves to a StackingEligibility object if the operation succeeds
*/
async canLockStx({
async canStack({
poxAddress,
cycles
}: CanLockStxOptions
Expand All @@ -232,7 +231,7 @@ export class Stacker {

return Promise.all([balancePromise, poxInfoPromise])
.then(([balance, poxInfo]) => {
const { hashMode, data } = convertBTCAddress(poxAddress);
const { hashMode, data } = decodeBtcAddress(poxAddress);
const hashModeBuffer = bufferCV(new BN(hashMode, 10).toBuffer());
const hashbytes = bufferCV(data);
const poxAddressCV = tupleCV({
Expand Down Expand Up @@ -278,18 +277,18 @@ export class Stacker {
*
* @returns {Promise<string>} that resolves to a broadcasted txid if the operation succeeds
*/
async lockStx({
async stack({
amountMicroStx,
poxAddress,
cycles,
key,
privateKey,
burnBlockHeight,
}: LockStxOptions
) {
const poxInfo = await this.getPoxInfo();
const contract = poxInfo.contract_id;

const txOptions = this.getLockTxOptions({
const txOptions = this.getStackOptions({
amountMicroStx,
cycles,
poxAddress,
Expand All @@ -298,7 +297,7 @@ export class Stacker {
});
const tx = await makeContractCall({
...txOptions,
senderKey: key,
senderKey: privateKey,
});

const res = await broadcastTransaction(tx, txOptions.network as StacksNetwork);
Expand All @@ -308,7 +307,7 @@ export class Stacker {
throw new Error(`${res.error} - ${res.reason}`);
}

getLockTxOptions({
getStackOptions({
amountMicroStx,
poxAddress,
cycles,
Expand All @@ -321,7 +320,7 @@ export class Stacker {
contract: string;
burnBlockHeight: number;
}) {
const { hashMode, data } = convertBTCAddress(poxAddress);
const { hashMode, data } = decodeBtcAddress(poxAddress);
const hashModeBuffer = bufferCV(new BN(hashMode, 10).toBuffer());
const hashbytes = bufferCV(data);
const address = tupleCV({
Expand Down
46 changes: 45 additions & 1 deletion packages/stacking/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AddressHashMode } from '@stacks/transactions';
import { address } from 'bitcoinjs-lib';
import BN from 'bn.js';
import { StackingErrors } from './constants';

export function getAddressHashMode(btcAddress: string) {
if (btcAddress.startsWith('bc1') || btcAddress.startsWith('tb1')) {
Expand All @@ -27,7 +28,7 @@ export function getAddressHashMode(btcAddress: string) {
}
}

export function convertBTCAddress(btcAddress: string) {
export function decodeBtcAddress(btcAddress: string) {
const hashMode = getAddressHashMode(btcAddress);
if (btcAddress.startsWith('bc1') || btcAddress.startsWith('tb1')) {
const { data } = address.fromBech32(btcAddress);
Expand All @@ -47,4 +48,47 @@ export function convertBTCAddress(btcAddress: string) {
export function getBTCAddress(version: Buffer, checksum: Buffer) {
const btcAddress = address.toBase58Check(checksum, new BN(version).toNumber());
return btcAddress;
}

export function getErrorString(error: StackingErrors): string {
switch(error) {
case StackingErrors.ERR_STACKING_UNREACHABLE:
return 'Stacking unreachable';
case StackingErrors.ERR_STACKING_INSUFFICIENT_FUNDS:
return 'Insufficient funds';
case StackingErrors.ERR_STACKING_INVALID_LOCK_PERIOD:
return 'Invalid lock period';
case StackingErrors.ERR_STACKING_ALREADY_STACKED:
return 'Account already stacked. Concurrent stacking not allowed.';
case StackingErrors.ERR_STACKING_NO_SUCH_PRINCIPAL:
return 'Principal does not exist';
case StackingErrors.ERR_STACKING_EXPIRED:
return 'Stacking expired';
case StackingErrors.ERR_STACKING_STX_LOCKED:
return 'STX balance is locked';
case StackingErrors.ERR_STACKING_PERMISSION_DENIED:
return 'Permission denied';
case StackingErrors.ERR_STACKING_THRESHOLD_NOT_MET:
return 'Stacking threshold not met';
case StackingErrors.ERR_STACKING_POX_ADDRESS_IN_USE:
return 'PoX address already in use';
case StackingErrors.ERR_STACKING_INVALID_POX_ADDRESS:
return 'Invalid PoX address';
case StackingErrors.ERR_STACKING_ALREADY_REJECTED:
return 'Stacking already rejected';
case StackingErrors.ERR_STACKING_INVALID_AMOUNT:
return 'Invalid amount';
case StackingErrors.ERR_NOT_ALLOWED:
return 'Stacking not allowed';
case StackingErrors.ERR_STACKING_ALREADY_DELEGATED:
return 'Already delegated';
case StackingErrors.ERR_DELEGATION_EXPIRES_DURING_LOCK:
return 'Delegation expires during lock period'
case StackingErrors.ERR_DELEGATION_TOO_MUCH_LOCKED:
return 'Delegation too much locked';
case StackingErrors.ERR_DELEGATION_POX_ADDR_REQUIRED:
return 'PoX address required for delegation';
case StackingErrors.ERR_INVALID_START_BURN_HEIGHT:
return 'Invalid start burn height';
}
}

0 comments on commit 61f5bb8

Please sign in to comment.