feat: reduce reliance on network package

This commit is contained in:
janniks
2022-01-28 13:54:55 +01:00
committed by Reed Rosenbluth
parent 7807809254
commit 422fda3cd4
5 changed files with 180 additions and 124 deletions

View File

@@ -1,11 +1,11 @@
import { resolveZoneFileToProfile } from '@stacks/profile'; import { resolveZoneFileToProfile } from '@stacks/profile';
import { fetchPrivate } from '@stacks/common'; import { fetchPrivate } from '@stacks/common';
import { StacksNetwork, StacksMainnet } from '@stacks/network'; import { IStacksNetwork, StacksMainnet, StacksNetwork, StacksNetworkName } from '@stacks/network';
export interface ProfileLookupOptions { export interface ProfileLookupOptions {
username: string; username: string;
zoneFileLookupURL?: string; zoneFileLookupURL?: string;
network?: StacksNetwork; network?: StacksNetworkName | IStacksNetwork;
} }
/** /**
@@ -17,11 +17,17 @@ export interface ProfileLookupOptions {
* blockstack.js [[getNameInfo]] function. * blockstack.js [[getNameInfo]] function.
* @returns {Promise} that resolves to a profile object * @returns {Promise} that resolves to a profile object
*/ */
export function lookupProfile(options: ProfileLookupOptions): Promise<Record<string, any>> { export function lookupProfile(lookupOptions: ProfileLookupOptions): Promise<Record<string, any>> {
if (!options.username) { if (!lookupOptions.username) {
return Promise.reject(); return Promise.reject(new Error('No username provided'));
} }
const network: StacksNetwork = options.network ? options.network : new StacksMainnet();
const defaultOptions = {
network: new StacksMainnet(),
};
const options = Object.assign(defaultOptions, lookupOptions);
const network = StacksNetwork.fromNameOrNetwork(options.network);
let lookupPromise; let lookupPromise;
if (options.zoneFileLookupURL) { if (options.zoneFileLookupURL) {
const url = `${options.zoneFileLookupURL.replace(/\/$/, '')}/${options.username}`; const url = `${options.zoneFileLookupURL.replace(/\/$/, '')}/${options.username}`;

View File

@@ -11,7 +11,7 @@ import {
PostCondition, PostCondition,
AnchorMode, AnchorMode,
} from '@stacks/transactions'; } from '@stacks/transactions';
import { StacksTestnet, StacksNetwork } from '@stacks/network'; import { StacksTestnet, IStacksNetwork, StacksNetworkName } from '@stacks/network';
import RPCClient from '@blockstack/rpc-client'; import RPCClient from '@blockstack/rpc-client';
import BN from 'bn.js'; import BN from 'bn.js';
@@ -25,7 +25,7 @@ interface ContractCallOptions {
nonce: IntegerType; nonce: IntegerType;
postConditions?: PostCondition[]; postConditions?: PostCondition[];
postConditionMode?: PostConditionMode; postConditionMode?: PostConditionMode;
network?: StacksNetwork; network?: StacksNetworkName | IStacksNetwork;
anchorMode: AnchorMode; anchorMode: AnchorMode;
} }
@@ -36,7 +36,7 @@ interface ContractDeployOptions {
nonce: number; nonce: number;
postConditions?: PostCondition[]; postConditions?: PostCondition[];
postConditionMode?: PostConditionMode; postConditionMode?: PostConditionMode;
network?: StacksNetwork; network?: StacksNetworkName | IStacksNetwork;
anchorMode: AnchorMode; anchorMode: AnchorMode;
} }
@@ -47,7 +47,7 @@ interface STXTransferOptions {
nonce: number; nonce: number;
postConditions?: PostCondition[]; postConditions?: PostCondition[];
postConditionMode?: PostConditionMode; postConditionMode?: PostConditionMode;
network?: StacksNetwork; network?: StacksNetworkName | IStacksNetwork;
anchorMode: AnchorMode; anchorMode: AnchorMode;
} }

View File

@@ -8,7 +8,10 @@ export interface NetworkConfig {
url: string; url: string;
} }
export interface StacksNetwork { export const StacksNetworks = ['mainnet', 'testnet'] as const;
export type StacksNetworkName = typeof StacksNetworks[number];
export interface IStacksNetwork {
version: TransactionVersion; version: TransactionVersion;
chainId: ChainID; chainId: ChainID;
bnsLookupUrl: string; bnsLookupUrl: string;
@@ -19,6 +22,7 @@ export interface StacksNetwork {
accountEndpoint: string; accountEndpoint: string;
contractAbiEndpoint: string; contractAbiEndpoint: string;
readOnlyFunctionCallEndpoint: string; readOnlyFunctionCallEndpoint: string;
isMainnet(): boolean; isMainnet(): boolean;
getBroadcastApiUrl: () => string; getBroadcastApiUrl: () => string;
getTransferFeeEstimateApiUrl: () => string; getTransferFeeEstimateApiUrl: () => string;
@@ -49,7 +53,7 @@ export interface StacksNetwork {
getNameInfo: (fullyQualifiedName: string) => any; getNameInfo: (fullyQualifiedName: string) => any;
} }
export class StacksMainnet implements StacksNetwork { export class StacksNetwork implements IStacksNetwork {
version = TransactionVersion.Mainnet; version = TransactionVersion.Mainnet;
chainId = ChainID.Mainnet; chainId = ChainID.Mainnet;
bnsLookupUrl = 'https://stacks-node-api.mainnet.stacks.co'; bnsLookupUrl = 'https://stacks-node-api.mainnet.stacks.co';
@@ -62,10 +66,35 @@ export class StacksMainnet implements StacksNetwork {
readonly coreApiUrl: string; readonly coreApiUrl: string;
constructor(networkUrl: NetworkConfig = { url: HIRO_MAINNET_DEFAULT }) { constructor(networkConfig: NetworkConfig) {
this.coreApiUrl = networkUrl.url; this.coreApiUrl = networkConfig.url;
} }
static fromName = (networkName: StacksNetworkName): IStacksNetwork => {
switch (networkName) {
case 'mainnet':
return new StacksMainnet();
case 'testnet':
return new StacksTestnet();
default:
throw new Error(
`Invalid network name provided. Must be one of the following: ${StacksNetworks.join(
', '
)}`
);
}
};
static fromNameOrNetwork = (network: StacksNetworkName | IStacksNetwork) => {
if (StacksNetworks.includes(network as StacksNetworkName)) {
// network is StacksNetworkName
return StacksNetwork.fromName(network as StacksNetworkName);
}
// network is IStacksNetwork
return network as IStacksNetwork;
};
isMainnet = () => this.version === TransactionVersion.Mainnet; isMainnet = () => this.version === TransactionVersion.Mainnet;
getBroadcastApiUrl = () => `${this.coreApiUrl}${this.broadcastEndpoint}`; getBroadcastApiUrl = () => `${this.coreApiUrl}${this.broadcastEndpoint}`;
getTransferFeeEstimateApiUrl = () => `${this.coreApiUrl}${this.transferFeeEstimateEndpoint}`; getTransferFeeEstimateApiUrl = () => `${this.coreApiUrl}${this.transferFeeEstimateEndpoint}`;
@@ -133,7 +162,16 @@ export class StacksMainnet implements StacksNetwork {
} }
} }
export class StacksTestnet extends StacksMainnet implements StacksNetwork { export class StacksMainnet extends StacksNetwork implements IStacksNetwork {
version = TransactionVersion.Mainnet;
chainId = ChainID.Mainnet;
constructor(networkUrl: NetworkConfig = { url: HIRO_MAINNET_DEFAULT }) {
super(networkUrl);
}
}
export class StacksTestnet extends StacksNetwork implements IStacksNetwork {
version = TransactionVersion.Testnet; version = TransactionVersion.Testnet;
chainId = ChainID.Testnet; chainId = ChainID.Testnet;
@@ -142,7 +180,7 @@ export class StacksTestnet extends StacksMainnet implements StacksNetwork {
} }
} }
export class StacksMocknet extends StacksMainnet implements StacksNetwork { export class StacksMocknet extends StacksNetwork implements IStacksNetwork {
version = TransactionVersion.Testnet; version = TransactionVersion.Testnet;
chainId = ChainID.Testnet; chainId = ChainID.Testnet;

View File

@@ -1,84 +1,84 @@
import { Buffer, IntegerType, intToBigInt } from '@stacks/common'; import { Buffer, fetchPrivate, IntegerType, intToBigInt } from '@stacks/common';
import { StacksTransaction } from './transaction';
import { StacksNetwork, StacksMainnet, StacksTestnet } from '@stacks/network';
import { import {
createTokenTransferPayload, IStacksNetwork,
createSmartContractPayload, StacksMainnet,
createContractCallPayload, StacksNetwork,
serializePayload, StacksNetworkName,
Payload, StacksTestnet,
} from './payload'; } from '@stacks/network';
import { c32address } from 'c32check';
import { import {
createSingleSigSpendingCondition,
createMultiSigSpendingCondition, createMultiSigSpendingCondition,
createSingleSigSpendingCondition,
createSponsoredAuth, createSponsoredAuth,
createStandardAuth, createStandardAuth,
} from './authorization'; } from './authorization';
import { ClarityValue, PrincipalCV } from './clarity';
import {
publicKeyToString,
createStacksPrivateKey,
getPublicKey,
publicKeyToAddress,
pubKeyfromPrivKey,
publicKeyFromBuffer,
createStacksPublicKey,
} from './keys';
import { TransactionSigner } from './signer';
import {
createSTXPostCondition,
createFungiblePostCondition,
createNonFungiblePostCondition,
} from './postcondition';
import {
PostCondition,
STXPostCondition,
FungiblePostCondition,
NonFungiblePostCondition,
} from './postcondition-types';
import { import {
AddressHashMode, AddressHashMode,
AddressVersion, AddressVersion,
AnchorMode,
FungibleConditionCode, FungibleConditionCode,
NonFungibleConditionCode, NonFungibleConditionCode,
PostConditionMode,
PayloadType, PayloadType,
AnchorMode, PostConditionMode,
SingleSigHashMode,
TransactionVersion, TransactionVersion,
TxRejectedReason, TxRejectedReason,
SingleSigHashMode,
} from './constants'; } from './constants';
import { ClarityAbi, validateContractCall } from './contract-abi';
import {
createStacksPrivateKey,
createStacksPublicKey,
getPublicKey,
pubKeyfromPrivKey,
publicKeyFromBuffer,
publicKeyToAddress,
publicKeyToString,
} from './keys';
import {
createContractCallPayload,
createSmartContractPayload,
createTokenTransferPayload,
Payload,
serializePayload,
} from './payload';
import {
createFungiblePostCondition,
createNonFungiblePostCondition,
createSTXPostCondition,
} from './postcondition';
import {
AssetInfo,
createContractPrincipal,
createStandardPrincipal,
FungiblePostCondition,
NonFungiblePostCondition,
PostCondition,
STXPostCondition,
} from './postcondition-types';
import { TransactionSigner } from './signer';
import { StacksTransaction } from './transaction';
import { createLPList } from './types'; import { createLPList } from './types';
import { AssetInfo, createStandardPrincipal, createContractPrincipal } from './postcondition-types'; import { cvToHex, omit, parseReadOnlyResponse, validateTxId } from './utils';
import { cvToHex, parseReadOnlyResponse, omit, validateTxId } from './utils';
import { fetchPrivate } from '@stacks/common';
import { ClarityValue, PrincipalCV } from './clarity';
import { validateContractCall, ClarityAbi } from './contract-abi';
import { c32address } from 'c32check';
/** /**
* Lookup the nonce for an address from a core node * Lookup the nonce for an address from a core node
* *
* @param {string} address - the c32check address to look up * @param {string} address - the c32check address to look up
* @param {StacksNetwork} network - the Stacks network to look up address on * @param {StacksNetworkName | IStacksNetwork} network - the Stacks network to look up address on
* *
* @return a promise that resolves to an integer * @return a promise that resolves to an integer
*/ */
export async function getNonce(address: string, network?: StacksNetwork): Promise<bigint> { export async function getNonce(
address: string,
network?: StacksNetworkName | IStacksNetwork
): Promise<bigint> {
const defaultNetwork = new StacksMainnet(); const defaultNetwork = new StacksMainnet();
const url = network const url = network
? network.getAccountApiUrl(address) ? StacksNetwork.fromNameOrNetwork(network).getAccountApiUrl(address)
: defaultNetwork.getAccountApiUrl(address); : defaultNetwork.getAccountApiUrl(address);
const response = await fetchPrivate(url); const response = await fetchPrivate(url);
if (!response.ok) { if (!response.ok) {
let msg = ''; let msg = '';
@@ -100,13 +100,13 @@ export async function getNonce(address: string, network?: StacksNetwork): Promis
* Estimate the total transaction fee in microstacks for a token transfer * Estimate the total transaction fee in microstacks for a token transfer
* *
* @param {StacksTransaction} transaction - the token transfer transaction to estimate fees for * @param {StacksTransaction} transaction - the token transfer transaction to estimate fees for
* @param {StacksNetwork} network - the Stacks network to estimate transaction for * @param {StacksNetworkName | IStacksNetwork} network - the Stacks network to estimate transaction for
* *
* @return a promise that resolves to number of microstacks per byte * @return a promise that resolves to number of microstacks per byte
*/ */
export async function estimateTransfer( export async function estimateTransfer(
transaction: StacksTransaction, transaction: StacksTransaction,
network?: StacksNetwork network?: StacksNetworkName | IStacksNetwork
): Promise<bigint> { ): Promise<bigint> {
if (transaction.payload.payloadType !== PayloadType.TokenTransfer) { if (transaction.payload.payloadType !== PayloadType.TokenTransfer) {
throw new Error( throw new Error(
@@ -125,10 +125,9 @@ export async function estimateTransfer(
headers: requestHeaders, headers: requestHeaders,
}; };
const defaultNetwork = new StacksMainnet(); const derivedNetwork = network ?? deriveNetwork(transaction);
const url = network const url = StacksNetwork.fromNameOrNetwork(derivedNetwork).getTransferFeeEstimateApiUrl();
? network.getTransferFeeEstimateApiUrl()
: defaultNetwork.getTransferFeeEstimateApiUrl();
const response = await fetchPrivate(url, fetchOptions); const response = await fetchPrivate(url, fetchOptions);
if (!response.ok) { if (!response.ok) {
let msg = ''; let msg = '';
@@ -169,14 +168,14 @@ interface FeeEstimateResponse {
* @param {number} estimatedLen - is an optional argument that provides the endpoint with an * @param {number} estimatedLen - is an optional argument that provides the endpoint with an
* estimation of the final length (in bytes) of the transaction, including any post-conditions * estimation of the final length (in bytes) of the transaction, including any post-conditions
* and signatures * and signatures
* @param {StacksNetwork} network - the Stacks network to estimate transaction fees for * @param {StacksNetworkName | IStacksNetwork} network - the Stacks network to estimate transaction fees for
* *
* @return a promise that resolves to FeeEstimate * @return a promise that resolves to FeeEstimate
*/ */
export async function estimateTransaction( export async function estimateTransaction(
transactionPayload: Payload, transactionPayload: Payload,
estimatedLen?: number, estimatedLen?: number,
network?: StacksNetwork network?: StacksNetworkName | IStacksNetwork
): Promise<[FeeEstimation, FeeEstimation, FeeEstimation]> { ): Promise<[FeeEstimation, FeeEstimation, FeeEstimation]> {
const options = { const options = {
method: 'POST', method: 'POST',
@@ -188,9 +187,8 @@ export async function estimateTransaction(
}; };
const defaultNetwork = new StacksMainnet(); const defaultNetwork = new StacksMainnet();
const url = network const url = network
? network.getTransactionFeeEstimateApiUrl() ? StacksNetwork.fromNameOrNetwork(network).getTransactionFeeEstimateApiUrl()
: defaultNetwork.getTransactionFeeEstimateApiUrl(); : defaultNetwork.getTransactionFeeEstimateApiUrl();
const response = await fetchPrivate(url, options); const response = await fetchPrivate(url, options);
@@ -393,17 +391,18 @@ export type TxBroadcastResult = TxBroadcastResultOk | TxBroadcastResultRejected;
* Broadcast the signed transaction to a core node * Broadcast the signed transaction to a core node
* *
* @param {StacksTransaction} transaction - the token transfer transaction to broadcast * @param {StacksTransaction} transaction - the token transfer transaction to broadcast
* @param {StacksNetwork} network - the Stacks network to broadcast transaction to * @param {StacksNetworkName | IStacksNetwork} network - the Stacks network to broadcast transaction to
* *
* @returns {Promise} that resolves to a response if the operation succeeds * @returns {Promise} that resolves to a response if the operation succeeds
*/ */
export async function broadcastTransaction( export async function broadcastTransaction(
transaction: StacksTransaction, transaction: StacksTransaction,
network: StacksNetwork, network?: StacksNetworkName | IStacksNetwork,
attachment?: Buffer attachment?: Buffer
): Promise<TxBroadcastResult> { ): Promise<TxBroadcastResult> {
const rawTx = transaction.serialize(); const rawTx = transaction.serialize();
const url = network.getBroadcastApiUrl(); const derivedNetwork = network ?? deriveNetwork(transaction);
const url = StacksNetwork.fromNameOrNetwork(derivedNetwork).getBroadcastApiUrl();
return broadcastRawTransaction(rawTx, url, attachment); return broadcastRawTransaction(rawTx, url, attachment);
} }
@@ -459,20 +458,20 @@ export async function broadcastRawTransaction(
* *
* @param {string} address - the contracts address * @param {string} address - the contracts address
* @param {string} contractName - the contracts name * @param {string} contractName - the contracts name
* @param {StacksNetwork} network - the Stacks network to broadcast transaction to * @param {StacksNetworkName | IStacksNetwork} network - the Stacks network to broadcast transaction to
* *
* @returns {Promise} that resolves to a ClarityAbi if the operation succeeds * @returns {Promise} that resolves to a ClarityAbi if the operation succeeds
*/ */
export async function getAbi( export async function getAbi(
address: string, address: string,
contractName: string, contractName: string,
network: StacksNetwork network: StacksNetworkName | IStacksNetwork
): Promise<ClarityAbi> { ): Promise<ClarityAbi> {
const options = { const options = {
method: 'GET', method: 'GET',
}; };
const url = network.getAbiApiUrl(address, contractName); const url = StacksNetwork.fromNameOrNetwork(network).getAbiApiUrl(address, contractName);
const response = await fetchPrivate(url, options); const response = await fetchPrivate(url, options);
if (!response.ok) { if (!response.ok) {
@@ -488,6 +487,15 @@ export async function getAbi(
return JSON.parse(await response.text()) as ClarityAbi; return JSON.parse(await response.text()) as ClarityAbi;
} }
function deriveNetwork(transaction: StacksTransaction) {
switch (transaction.version) {
case TransactionVersion.Mainnet:
return new StacksMainnet();
case TransactionVersion.Testnet:
return new StacksTestnet();
}
}
export interface MultiSigOptions { export interface MultiSigOptions {
numSignatures: number; numSignatures: number;
publicKeys: string[]; publicKeys: string[];
@@ -507,7 +515,7 @@ export interface TokenTransferOptions {
/** the transaction nonce, which must be increased monotonically with each new transaction */ /** the transaction nonce, which must be increased monotonically with each new transaction */
nonce?: IntegerType; nonce?: IntegerType;
/** the network that the transaction will ultimately be broadcast to */ /** the network that the transaction will ultimately be broadcast to */
network?: StacksNetwork; network?: StacksNetworkName | IStacksNetwork;
/** the transaction anchorMode, which specifies whether it should be /** the transaction anchorMode, which specifies whether it should be
* included in an anchor block or a microblock */ * included in an anchor block or a microblock */
anchorMode: AnchorMode; anchorMode: AnchorMode;
@@ -548,7 +556,7 @@ export interface SignedMultiSigTokenTransferOptions extends TokenTransferOptions
* *
* @param {UnsignedTokenTransferOptions | UnsignedMultiSigTokenTransferOptions} txOptions - an options object for the token transfer * @param {UnsignedTokenTransferOptions | UnsignedMultiSigTokenTransferOptions} txOptions - an options object for the token transfer
* *
* @return {Promis<StacksTransaction>} * @return {Promise<StacksTransaction>}
*/ */
export async function makeUnsignedSTXTokenTransfer( export async function makeUnsignedSTXTokenTransfer(
txOptions: UnsignedTokenTransferOptions | UnsignedMultiSigTokenTransferOptions txOptions: UnsignedTokenTransferOptions | UnsignedMultiSigTokenTransferOptions
@@ -594,22 +602,24 @@ export async function makeUnsignedSTXTokenTransfer(
authorization = createStandardAuth(spendingCondition); authorization = createStandardAuth(spendingCondition);
} }
const network = StacksNetwork.fromNameOrNetwork(options.network);
const postConditions: PostCondition[] = []; const postConditions: PostCondition[] = [];
if (options.postConditions && options.postConditions.length > 0) { if (options.postConditions && options.postConditions.length > 0) {
options.postConditions.forEach(postCondition => { options.postConditions.forEach(postCondition => {
postConditions.push(postCondition); postConditions.push(postCondition);
}); });
} }
const lpPostConditions = createLPList(postConditions); const lpPostConditions = createLPList(postConditions);
const transaction = new StacksTransaction( const transaction = new StacksTransaction(
options.network.version, network.version,
authorization, authorization,
payload, payload,
lpPostConditions, lpPostConditions,
options.postConditionMode, options.postConditionMode,
options.anchorMode, options.anchorMode,
options.network.chainId network.chainId
); );
if (txOptions.fee === undefined || txOptions.fee === null) { if (txOptions.fee === undefined || txOptions.fee === null) {
@@ -685,7 +695,7 @@ export interface BaseContractDeployOptions {
/** the transaction nonce, which must be increased monotonically with each new transaction */ /** the transaction nonce, which must be increased monotonically with each new transaction */
nonce?: IntegerType; nonce?: IntegerType;
/** the network that the transaction will ultimately be broadcast to */ /** the network that the transaction will ultimately be broadcast to */
network?: StacksNetwork; network?: StacksNetworkName | IStacksNetwork;
/** the transaction anchorMode, which specifies whether it should be /** the transaction anchorMode, which specifies whether it should be
* included in an anchor block or a microblock */ * included in an anchor block or a microblock */
anchorMode: AnchorMode; anchorMode: AnchorMode;
@@ -714,13 +724,13 @@ export interface UnsignedContractDeployOptions extends BaseContractDeployOptions
* Estimate the total transaction fee in microstacks for a contract deploy * Estimate the total transaction fee in microstacks for a contract deploy
* *
* @param {StacksTransaction} transaction - the token transfer transaction to estimate fees for * @param {StacksTransaction} transaction - the token transfer transaction to estimate fees for
* @param {StacksNetwork} network - the Stacks network to estimate transaction for * @param {StacksNetworkName | IStacksNetwork} network - the Stacks network to estimate transaction for
* *
* @return a promise that resolves to number of microstacks per byte * @return a promise that resolves to number of microstacks per byte
*/ */
export async function estimateContractDeploy( export async function estimateContractDeploy(
transaction: StacksTransaction, transaction: StacksTransaction,
network?: StacksNetwork network?: StacksNetworkName | IStacksNetwork
): Promise<bigint> { ): Promise<bigint> {
if (transaction.payload.payloadType !== PayloadType.SmartContract) { if (transaction.payload.payloadType !== PayloadType.SmartContract) {
throw new Error( throw new Error(
@@ -741,10 +751,8 @@ export async function estimateContractDeploy(
// Place holder estimate until contract deploy fee estimation is fully implemented on Stacks // Place holder estimate until contract deploy fee estimation is fully implemented on Stacks
// blockchain core // blockchain core
const defaultNetwork = new StacksMainnet(); const derivedNetwork = network ?? deriveNetwork(transaction);
const url = network const url = StacksNetwork.fromNameOrNetwork(derivedNetwork).getTransferFeeEstimateApiUrl();
? network.getTransferFeeEstimateApiUrl()
: defaultNetwork.getTransferFeeEstimateApiUrl();
const response = await fetchPrivate(url, fetchOptions); const response = await fetchPrivate(url, fetchOptions);
if (!response.ok) { if (!response.ok) {
@@ -821,22 +829,24 @@ export async function makeUnsignedContractDeploy(
authorization = createStandardAuth(spendingCondition); authorization = createStandardAuth(spendingCondition);
} }
const network = StacksNetwork.fromNameOrNetwork(options.network);
const postConditions: PostCondition[] = []; const postConditions: PostCondition[] = [];
if (options.postConditions && options.postConditions.length > 0) { if (options.postConditions && options.postConditions.length > 0) {
options.postConditions.forEach(postCondition => { options.postConditions.forEach(postCondition => {
postConditions.push(postCondition); postConditions.push(postCondition);
}); });
} }
const lpPostConditions = createLPList(postConditions); const lpPostConditions = createLPList(postConditions);
const transaction = new StacksTransaction( const transaction = new StacksTransaction(
options.network.version, network.version,
authorization, authorization,
payload, payload,
lpPostConditions, lpPostConditions,
options.postConditionMode, options.postConditionMode,
options.anchorMode, options.anchorMode,
options.network.chainId network.chainId
); );
if (txOptions.fee === undefined || txOptions.fee === null) { if (txOptions.fee === undefined || txOptions.fee === null) {
@@ -873,7 +883,7 @@ export interface ContractCallOptions {
/** the transaction nonce, which must be increased monotonically with each new transaction */ /** the transaction nonce, which must be increased monotonically with each new transaction */
nonce?: IntegerType; nonce?: IntegerType;
/** the Stacks blockchain network that will ultimately be used to broadcast this transaction */ /** the Stacks blockchain network that will ultimately be used to broadcast this transaction */
network?: StacksNetwork; network?: StacksNetworkName | IStacksNetwork;
/** the transaction anchorMode, which specifies whether it should be /** the transaction anchorMode, which specifies whether it should be
* included in an anchor block or a microblock */ * included in an anchor block or a microblock */
anchorMode: AnchorMode; anchorMode: AnchorMode;
@@ -914,13 +924,13 @@ export interface SignedMultiSigContractCallOptions extends ContractCallOptions {
* Estimate the total transaction fee in microstacks for a contract function call * Estimate the total transaction fee in microstacks for a contract function call
* *
* @param {StacksTransaction} transaction - the token transfer transaction to estimate fees for * @param {StacksTransaction} transaction - the token transfer transaction to estimate fees for
* @param {StacksNetwork} network - the Stacks network to estimate transaction for * @param {StacksNetworkName | IStacksNetwork} network - the Stacks network to estimate transaction for
* *
* @return a promise that resolves to number of microstacks per byte * @return a promise that resolves to number of microstacks per byte
*/ */
export async function estimateContractFunctionCall( export async function estimateContractFunctionCall(
transaction: StacksTransaction, transaction: StacksTransaction,
network?: StacksNetwork network?: StacksNetworkName | IStacksNetwork
): Promise<bigint> { ): Promise<bigint> {
if (transaction.payload.payloadType !== PayloadType.ContractCall) { if (transaction.payload.payloadType !== PayloadType.ContractCall) {
throw new Error( throw new Error(
@@ -941,10 +951,8 @@ export async function estimateContractFunctionCall(
// Place holder estimate until contract call fee estimation is fully implemented on Stacks // Place holder estimate until contract call fee estimation is fully implemented on Stacks
// blockchain core // blockchain core
const defaultNetwork = new StacksMainnet(); const derivedNetwork = network ?? deriveNetwork(transaction);
const url = network const url = StacksNetwork.fromNameOrNetwork(derivedNetwork).getTransferFeeEstimateApiUrl();
? network.getTransferFeeEstimateApiUrl()
: defaultNetwork.getTransferFeeEstimateApiUrl();
const response = await fetchPrivate(url, fetchOptions); const response = await fetchPrivate(url, fetchOptions);
if (!response.ok) { if (!response.ok) {
@@ -1032,6 +1040,8 @@ export async function makeUnsignedContractCall(
authorization = createStandardAuth(spendingCondition); authorization = createStandardAuth(spendingCondition);
} }
const network = StacksNetwork.fromNameOrNetwork(options.network);
const postConditions: PostCondition[] = []; const postConditions: PostCondition[] = [];
if (options.postConditions && options.postConditions.length > 0) { if (options.postConditions && options.postConditions.length > 0) {
options.postConditions.forEach(postCondition => { options.postConditions.forEach(postCondition => {
@@ -1041,28 +1051,28 @@ export async function makeUnsignedContractCall(
const lpPostConditions = createLPList(postConditions); const lpPostConditions = createLPList(postConditions);
const transaction = new StacksTransaction( const transaction = new StacksTransaction(
options.network.version, network.version,
authorization, authorization,
payload, payload,
lpPostConditions, lpPostConditions,
options.postConditionMode, options.postConditionMode,
options.anchorMode, options.anchorMode,
options.network.chainId network.chainId
); );
if (txOptions.fee === undefined || txOptions.fee === null) { if (txOptions.fee === undefined || txOptions.fee === null) {
const estimatedLen = transaction.serialize().byteLength; const estimatedLen = transaction.serialize().byteLength;
const txFee = await estimateTransaction(payload, estimatedLen, options.network); const txFee = await estimateTransaction(payload, estimatedLen, network);
transaction.setFee(txFee[1].fee); transaction.setFee(txFee[1].fee);
} }
if (txOptions.nonce === undefined || txOptions.nonce === null) { if (txOptions.nonce === undefined || txOptions.nonce === null) {
const addressVersion = const addressVersion =
options.network.version === TransactionVersion.Mainnet network.version === TransactionVersion.Mainnet
? AddressVersion.MainnetSingleSig ? AddressVersion.MainnetSingleSig
: AddressVersion.TestnetSingleSig; : AddressVersion.TestnetSingleSig;
const senderAddress = c32address(addressVersion, transaction.auth.spendingCondition!.signer); const senderAddress = c32address(addressVersion, transaction.auth.spendingCondition!.signer);
const txNonce = await getNonce(senderAddress, options.network); const txNonce = await getNonce(senderAddress, network);
transaction.setNonce(txNonce); transaction.setNonce(txNonce);
} }
@@ -1264,7 +1274,7 @@ export function makeContractNonFungiblePostCondition(
* @param {String} contractName - the contract name * @param {String} contractName - the contract name
* @param {String} functionName - name of the function to be called * @param {String} functionName - name of the function to be called
* @param {[ClarityValue]} functionArgs - an array of Clarity values as arguments to the function call * @param {[ClarityValue]} functionArgs - an array of Clarity values as arguments to the function call
* @param {StacksNetwork} network - the Stacks blockchain network this transaction is destined for * @param {IStacksNetwork} network - the Stacks blockchain network this transaction is destined for
* @param {String} senderAddress - the c32check address of the sender * @param {String} senderAddress - the c32check address of the sender
*/ */
@@ -1274,7 +1284,7 @@ export interface ReadOnlyFunctionOptions {
functionName: string; functionName: string;
functionArgs: ClarityValue[]; functionArgs: ClarityValue[];
/** the network that the contract which contains the function is deployed to */ /** the network that the contract which contains the function is deployed to */
network?: StacksNetwork; network?: StacksNetworkName | IStacksNetwork;
/** address of the sender */ /** address of the sender */
senderAddress: string; senderAddress: string;
} }
@@ -1297,9 +1307,9 @@ export async function callReadOnlyFunction(
const options = Object.assign(defaultOptions, readOnlyFunctionOptions); const options = Object.assign(defaultOptions, readOnlyFunctionOptions);
const { contractName, contractAddress, functionName, functionArgs, network, senderAddress } = const { contractName, contractAddress, functionName, functionArgs, senderAddress } = options;
options;
const network = StacksNetwork.fromNameOrNetwork(options.network);
const url = network.getReadOnlyFunctionCallApiUrl(contractAddress, contractName, functionName); const url = network.getReadOnlyFunctionCallApiUrl(contractAddress, contractName, functionName);
const args = functionArgs.map(arg => cvToHex(arg)); const args = functionArgs.map(arg => cvToHex(arg));
@@ -1345,7 +1355,7 @@ export interface SponsorOptionsOpts {
/** the hashmode of the sponsor's address */ /** the hashmode of the sponsor's address */
sponsorAddressHashmode?: AddressHashMode; sponsorAddressHashmode?: AddressHashMode;
/** the Stacks blockchain network that this transaction will ultimately be broadcast to */ /** the Stacks blockchain network that this transaction will ultimately be broadcast to */
network?: StacksNetwork; network?: StacksNetworkName | IStacksNetwork;
} }
/** /**
@@ -1364,14 +1374,15 @@ export async function sponsorTransaction(
fee: 0 as IntegerType, fee: 0 as IntegerType,
sponsorNonce: 0 as IntegerType, sponsorNonce: 0 as IntegerType,
sponsorAddressHashmode: AddressHashMode.SerializeP2PKH as SingleSigHashMode, sponsorAddressHashmode: AddressHashMode.SerializeP2PKH as SingleSigHashMode,
network:
sponsorOptions.transaction.version === TransactionVersion.Mainnet
? new StacksMainnet()
: new StacksTestnet(),
}; };
const options = Object.assign(defaultOptions, sponsorOptions); const options = Object.assign(defaultOptions, sponsorOptions);
const network =
sponsorOptions.network ?? const network = StacksNetwork.fromNameOrNetwork(options.network);
(options.transaction.version === TransactionVersion.Mainnet
? new StacksMainnet()
: new StacksTestnet());
const sponsorPubKey = pubKeyfromPrivKey(options.sponsorPrivateKey); const sponsorPubKey = pubKeyfromPrivKey(options.sponsorPrivateKey);
if (sponsorOptions.fee === undefined || sponsorOptions.fee === null) { if (sponsorOptions.fee === undefined || sponsorOptions.fee === null) {

View File

@@ -20,7 +20,8 @@ import {
sponsorTransaction, sponsorTransaction,
makeSTXTokenTransfer, makeSTXTokenTransfer,
makeUnsignedContractCall, makeUnsignedContractCall,
estimateTransaction estimateTransaction,
SignedTokenTransferOptions
} from '../src/builders'; } from '../src/builders';
import { deserializeTransaction, StacksTransaction } from '../src/transaction'; import { deserializeTransaction, StacksTransaction } from '../src/transaction';