Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix!: Switch to a network and client param solution #1748

Merged
merged 3 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 58 additions & 44 deletions .github/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

- [Stacks.js (\>=5.x.x) → (7.x.x)](#stacksjs-5xx--7xx)
- [Breaking Changes](#breaking-changes)
- [Reducing Wrapper Types](#reducing-wrapper-types)
- [Stacks Network](#stacks-network)
- [Impacts](#impacts)
- [Fetch Methods](#fetch-methods)
- [Reducing Wrapper Types](#reducing-wrapper-types)
- [Stacks Network](#stacks-network-1)
- [Clarity Representation](#clarity-representation)
- [Post-conditions](#post-conditions)
- [Fetch Methods](#fetch-methods)
- [`serialize` methods](#serialize-methods)
- [Asset Helper Methods](#asset-helper-methods)
- [CLI](#cli)
Expand Down Expand Up @@ -47,6 +46,15 @@
- **Advanced:** Removes two's complement compatibilty from `intToBigInt` parser method. [Read more...](#advanced-signed-bigint)
- **Advanced:** Refactorings and less visible updates. [Read more...](#advanced-refactorings)

### Reducing Wrapper Types

With this release we are aiming to reduce unnecessary "wrapper" types, which are used in the internals of the codebase, but shouldn't be pushed onto the user/developer.

This breaks the signatures of many functions:

- `signMessageHashRsv`, `signWithKey` now return the message signature as a `string` directly.
- `nextSignature`, `nextVerification`, `publicKeyFromSignatureVrs`, `publicKeyFromSignatureRsv` now take in the message signature as a `string`.

### Stacks Network

From now on "network" objects are static (aka constants) and don't require instantiation.
Expand All @@ -62,51 +70,26 @@ The `@stacks/network` package exports the following network objects:
import { STACKS_MAINNET } from '@stacks/network';
import { STACKS_TESTNET } from '@stacks/network';
import { STACKS_DEVNET } from '@stacks/network';
```

#### Impacts

- @stacks/bns: `BnsContractAddress` was removed, since `.bootAddress` is now a part of the network objects.
- @stacks/transactions: `AddressVersion` was moved to `@stacks/network`.

### Fetch Methods

The following methods were renamed:

- `estimateFee` → `fetchFeeEstimate`
- `estimateTransfer` → `fetchFeeEstimateTransfer`
- `estimateTransaction` → `fetchFeeEstimateTransaction`
- `getAbi` → `fetchAbi`
- `getNonce` → `fetchNonce`
- `getContractMapEntry` → `fetchContractMapEntry`
- `callReadOnlyFunction` → `fetchCallReadOnlyFunction`

`broadcastTransaction` wasn't renamed to highlight the uniqueness of the method.
Namely, the node/API it is sent to will "broadcast" the transaction to the mempool.

### Reducing Wrapper Types

With this release we are aiming to reduce unnecessary "wrapper" types, which are used in the internals of the codebase, but shouldn't be pushed onto the user/developer.

This breaks the signatures of many functions:

- `signMessageHashRsv`, `signWithKey` now return the message signature as a `string` directly.
- `nextSignature`, `nextVerification`, `publicKeyFromSignatureVrs`, `publicKeyFromSignatureRsv` now take in the message signature as a `string`.

### Stacks Network

Stacks network objects are now exported by the `@stacks/common` package.
They are used to specify network settings for other functions and don't require instantiation (like the `@stacks/network` approach did).

```ts
import { STACKS_MAINNET } from '@stacks/transactions';
console.log(STACKS_MAINNET);
// {
// chainId: 1,
// transactionVersion: 0,
// peerNetworkId: 385875968,
// magicBytes: 'X2',
// bootAddress: 'SP000000000000000000002Q6VF78',
// addressVersion: { singleSig: 22, multiSig: 20 },
// client: { baseUrl: 'https://api.mainnet.hiro.so' }
// }
```

After importing the network object (e.g. `STACKS_MAINNET` here), you can use it in other functions like so:
After importing the network object (e.g. `STACKS_MAINNET` here), you can use it in other functions as the `network` parameter.
Most of the time it's easier to just set the `network` parameter to a string literal (`'mainnet'`, `'testnet'`, or `'devnet'`).

<!-- todo: update more functions, show examples -->
As part of the network object, the `client` property was added.
This contains a `baseUrl` property and can optionally contain a `fetch` property.

For easing the transition, the functions which depended on a network instance now accept an `client` parameter.
For easing the transition, the functions which depended on a network instance now accept an optional `client` parameter.
The `client` parameter can be any object containing a `baseUrl` and `fetch` property.

- The `baseUrl` property should be a string containing the base URL of the Stacks node you want to use.
Expand Down Expand Up @@ -139,7 +122,7 @@ const transaction = await makeSTXTokenTransfer({
```

> [!NOTE]
> Custom URLs and fetch functions are still supported via the `client` parameter.
> Custom URLs and fetch functions are still supported via the `client` parameter or via `network.client`.

```diff
const transaction = await makeSTXTokenTransfer({
Expand All @@ -150,6 +133,22 @@ const transaction = await makeSTXTokenTransfer({
});
```

```diff
const transaction = await makeSTXTokenTransfer({
// ...
- network: new StacksTestnet({ url: "mynode-optional.com", fetchFn: myFetch }), // optional options
+ network: {
+ ...STACKS_TESTNET,
+ client: { baseUrl: "mynode-optional.com", fetch: myFetch } // optional params
+ },
});
```

#### Impacts

- @stacks/bns: `BnsContractAddress` was removed, since `.bootAddress` is now a part of the network objects.
- @stacks/transactions: `AddressVersion` was moved to `@stacks/network`.

### Clarity Representation

The `ClarityType` enum was replaced by a readable version.
Expand Down Expand Up @@ -213,6 +212,21 @@ const nftPostCondition: NonFungiblePostCondition = {
};
```

### Fetch Methods

The following methods were renamed:

- `estimateFee` → `fetchFeeEstimate`
- `estimateTransfer` → `fetchFeeEstimateTransfer`
- `estimateTransaction` → `fetchFeeEstimateTransaction`
- `getAbi` → `fetchAbi`
- `getNonce` → `fetchNonce`
- `getContractMapEntry` → `fetchContractMapEntry`
- `callReadOnlyFunction` → `fetchCallReadOnlyFunction`

`broadcastTransaction` wasn't renamed to highlight the uniqueness of the method.
Namely, the node/API it is sent to will "broadcast" the transaction to the mempool.

### `serialize` methods

Existing methods now take or return **hex-encoded strings** _instead_ of `Uint8Array`s.
Expand Down
11 changes: 5 additions & 6 deletions packages/api/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { FetchFn, Hex, createFetchFn } from '@stacks/common';
import {
NetworkParam,
STACKS_MAINNET,
StacksNetwork,
StacksNetworkName,
Expand Down Expand Up @@ -46,11 +47,9 @@ export class StacksNodeApi {
}: {
/** The base API/node URL for the network fetch calls */
baseUrl?: string;
/** Stacks network object (defaults to {@link STACKS_MAINNET}) */
network?: StacksNetworkName | StacksNetwork;
/** An optional custom fetch function to override default behaviors */
fetch?: FetchFn;
} = {}) {
} & NetworkParam = {}) {
this.baseUrl = baseUrl ?? defaultUrlFromNetwork(network);
this.fetch = fetch ?? createFetchFn();
this.network = networkFrom(network);
Expand All @@ -67,10 +66,10 @@ export class StacksNodeApi {
*/
broadcastTransaction = async (
transaction: StacksTransaction,
attachment?: Uint8Array | string
attachment?: Uint8Array | string,
network?: StacksNetworkName | StacksNetwork
): Promise<TxBroadcastResult> => {
// todo: should we use a opts object instead of positional args here?
return broadcastTransaction({ transaction, attachment, client: this });
return broadcastTransaction({ transaction, attachment, network });
};

/**
Expand Down
21 changes: 12 additions & 9 deletions packages/auth/src/profile.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ClientOpts, ClientParam, defaultClientOpts } from '@stacks/common';
import { NetworkClientParam, clientFromNetwork, networkFrom } from '@stacks/network';
import { resolveZoneFileToProfile } from '@stacks/profile';

export interface ProfileLookupOptions {
username: string;
zoneFileLookupURL?: string;
client?: ClientOpts;
}

/**
Expand All @@ -16,19 +15,22 @@ export interface ProfileLookupOptions {
* blockstack.js [[getNameInfo]] function.
* @returns {Promise} that resolves to a profile object
*/
export function lookupProfile(options: ProfileLookupOptions): Promise<Record<string, any>> {
export function lookupProfile(
options: ProfileLookupOptions & NetworkClientParam
): Promise<Record<string, any>> {
if (!options.username) {
return Promise.reject(new Error('No username provided'));
}

const client = defaultClientOpts(options.client);
const network = networkFrom(options.network ?? 'mainnet');
const client = Object.assign({}, clientFromNetwork(network), options.client);

let lookupPromise;
if (options.zoneFileLookupURL) {
const url = `${options.zoneFileLookupURL.replace(/\/$/, '')}/${options.username}`;
lookupPromise = client.fetch(url).then(response => response.json());
} else {
lookupPromise = getNameInfo({ name: options.username, client: client });
lookupPromise = getNameInfo({ name: options.username });
}
return lookupPromise.then((responseJSON: any) => {
if (responseJSON.hasOwnProperty('zonefile') && responseJSON.hasOwnProperty('address')) {
Expand All @@ -49,12 +51,13 @@ export function getNameInfo(
opts: {
/** Fully qualified name */
name: string;
} & ClientParam
} & NetworkClientParam
) {
const api = defaultClientOpts(opts.client);
const network = networkFrom(opts.network ?? 'mainnet');
const client = Object.assign({}, clientFromNetwork(network), opts.client);

const nameLookupURL = `${api.baseUrl}/v1/names/${opts.name}`;
return api
const nameLookupURL = `${client.baseUrl}/v1/names/${opts.name}`;
return client
.fetch(nameLookupURL)
.then((resp: any) => {
if (resp.status === 404) {
Expand Down
11 changes: 4 additions & 7 deletions packages/bns/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ClientParam, IntegerType, PublicKey, intToBigInt, utf8ToBytes } from '@stacks/common';
import { StacksNetwork } from '@stacks/network';
import { IntegerType, PublicKey, intToBigInt, utf8ToBytes } from '@stacks/common';
import { NetworkClientParam, StacksNetwork } from '@stacks/network';
import {
ClarityType,
ClarityValue,
Expand Down Expand Up @@ -83,15 +83,12 @@ export interface BnsReadOnlyOptions {
}

async function callReadOnlyBnsFunction(
options: BnsReadOnlyOptions & ClientParam
options: BnsReadOnlyOptions & NetworkClientParam
): Promise<ClarityValue> {
return fetchCallReadOnlyFunction({
...options,
contractAddress: options.network.bootAddress,
contractName: BNS_CONTRACT_NAME,
functionName: options.functionName,
senderAddress: options.senderAddress,
functionArgs: options.functionArgs,
client: options.client,
});
}

Expand Down
14 changes: 7 additions & 7 deletions packages/bns/tests/bns.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ test('canRegisterName true', async () => {
bufferCV(utf8ToBytes(fullyQualifiedName.split('.')[0])),
],
senderAddress: notRandomAddress,
client: undefined,
network: STACKS_TESTNET,
};

expect(result).toEqual(true);
Expand Down Expand Up @@ -96,7 +96,7 @@ test('canRegisterName false', async () => {
bufferCV(utf8ToBytes(fullyQualifiedName.split('.')[0])),
],
senderAddress: notRandomAddress,
client: undefined,
network: STACKS_TESTNET,
};

expect(result).toEqual(false);
Expand Down Expand Up @@ -135,7 +135,7 @@ test('canRegisterName error', async () => {
bufferCV(utf8ToBytes(fullyQualifiedName.split('.')[0])),
],
senderAddress: notRandomAddress,
client: undefined,
network: STACKS_TESTNET,
};

expect(result).toEqual(false);
Expand Down Expand Up @@ -171,7 +171,7 @@ test('getNamespacePrice', async () => {
functionName: bnsFunctionName,
senderAddress: address,
functionArgs: [bufferCVFromString(namespace)],
client: undefined,
network: STACKS_TESTNET,
};

expect(result.toString()).toEqual('10');
Expand Down Expand Up @@ -206,7 +206,7 @@ test('getNamespacePrice error', async () => {
functionName: bnsFunctionName,
senderAddress: address,
functionArgs: [bufferCVFromString(namespace)],
client: undefined,
network: STACKS_TESTNET,
};

await expect(getNamespacePrice({ namespace, network })).rejects.toEqual(new Error('u1001'));
Expand Down Expand Up @@ -244,7 +244,7 @@ test('getNamePrice', async () => {
functionName: bnsFunctionName,
senderAddress: address,
functionArgs: [bufferCVFromString(namespace), bufferCVFromString(name)],
client: undefined,
network: STACKS_TESTNET,
};

expect(result.toString()).toEqual('10');
Expand Down Expand Up @@ -281,7 +281,7 @@ test('getNamePrice error', async () => {
functionName: bnsFunctionName,
senderAddress: address,
functionArgs: [bufferCVFromString(namespace), bufferCVFromString(name)],
client: undefined,
network: STACKS_TESTNET,
};

await expect(getNamePrice({ fullyQualifiedName, network })).rejects.toEqual(new Error('u2001'));
Expand Down
Loading
Loading