Skip to content

Commit

Permalink
Replace internal Starknet CLI usage with starknet.js
Browse files Browse the repository at this point in the history
  • Loading branch information
penovicp committed Jul 11, 2023
1 parent c66fe23 commit 09b4e37
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 44 deletions.
10 changes: 5 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import { DevnetUtils } from "./devnet-utils";
import { ExternalServer } from "./external-server";
import { ArgentAccount, OpenZeppelinAccount } from "./account";
import { AmarnaDocker } from "./external-server/docker-amarna";
import { StarknetLegacyWrapper } from "./starknet-js-wrapper";

exitHook(() => {
ExternalServer.cleanAll();
Expand Down Expand Up @@ -150,11 +151,8 @@ extendConfig((config: HardhatConfig) => {

// set network as specified in userConfig
extendConfig((config: HardhatConfig, userConfig: Readonly<HardhatUserConfig>) => {
if (userConfig.starknet && userConfig.starknet.network) {
config.starknet.network = userConfig.starknet.network;
} else {
config.starknet.network = DEFAULT_STARKNET_NETWORK;
}
config.starknet.network = userConfig.starknet?.network ?? DEFAULT_STARKNET_NETWORK;

const networkConfig = getNetwork(
config.starknet.network,
config.networks,
Expand All @@ -174,6 +172,8 @@ function setVenvWrapper(hre: HardhatRuntimeEnvironment, venvPath: string) {

// add venv wrapper or docker wrapper of starknet
extendEnvironment((hre) => {
hre.starknetJs = new StarknetLegacyWrapper(hre.config.starknet.networkConfig);

const venvPath = hre.config.starknet.venv;
if (venvPath) {
setVenvWrapper(hre, venvPath);
Expand Down
91 changes: 91 additions & 0 deletions src/starknet-js-wrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { ProcessResult } from "@nomiclabs/hardhat-docker";
import { promises as fsp } from "fs";
import { NetworkConfig } from "hardhat/types/config";
import { BigNumberish, BlockIdentifier, json, provider as providerUtil, SequencerProvider } from "starknet";

export class StarknetJsWrapper {
public provider: SequencerProvider;

constructor(networkConfig: NetworkConfig) {
this.provider = new SequencerProvider({
baseUrl: networkConfig.url
});
}
}

/**
* StarknetLegacyWrapper is meant to facilitate the discontinuation of the Starknet CLI usage within StarknetWrapper
*/
export class StarknetLegacyWrapper extends StarknetJsWrapper {
private async readContract (contractPath: string) {
return json.parse((await fsp.readFile(contractPath)).toString("ascii"));
}

private stringifyResponse (r: unknown) {
return typeof r !== "string"
?`${json.stringify(r, undefined, "\n").replace(/\n+/g, "\n")}\n`
: r;
}

private generateProcessResult (statusCode: number, stdout: string, stderr: string): ProcessResult {
return {
statusCode,
stdout,
stderr
} as unknown as ProcessResult;
}

private async wrapProcessResult(p: Promise<unknown>): Promise<ProcessResult> {
return p
.then((a) => this.generateProcessResult(0, this.stringifyResponse(a), ""))
.catch((e) => this.generateProcessResult(1, "", this.stringifyResponse(e)));
}

public async declare(
contractPath: string,
senderAddress: string,
signature: string[],
nonce: string,
maxFee: string
): Promise<ProcessResult> {
const contractJson = await this.readContract(contractPath);
const contract = providerUtil.parseContract(contractJson);

return this.wrapProcessResult(this.provider.declareContract({
contract,
senderAddress,
signature
}, {
nonce,
maxFee
}).then(({ class_hash, transaction_hash }) =>
"DeprecatedDeclare transaction was sent.\n"
+ `Contract class hash: ${class_hash}\n`
+ `Transaction hash: ${transaction_hash}\n`
));
}

public async getTxStatus(txHash: BigNumberish): Promise<ProcessResult> {
return this.wrapProcessResult(this.provider.getTransactionStatus(txHash));
}

public async getTransactionTrace(txHash: BigNumberish): Promise<ProcessResult> {
return this.wrapProcessResult(this.provider.getTransactionTrace(txHash));
}

public async getTransactionReceipt(txHash: BigNumberish): Promise<ProcessResult> {
return this.wrapProcessResult(this.provider.getTransactionReceipt(txHash));
}

public async getTransaction(txHash: BigNumberish): Promise<ProcessResult> {
return this.wrapProcessResult(this.provider.getTransaction(txHash));
}

public async getBlock(blockIdentifier?: BlockIdentifier): Promise<ProcessResult> {
return this.wrapProcessResult(this.provider.getBlock(blockIdentifier));
}

public async getNonce(address: string, blockIdentifier?: BlockIdentifier): Promise<ProcessResult> {
return this.wrapProcessResult(this.provider.getNonceForAddress(address, blockIdentifier).then(BigInt));
}
}
36 changes: 15 additions & 21 deletions src/starknet-wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export abstract class StarknetWrapper {
// it's dangerous because in getters (e.g. get gatewayUrl) we rely on it being initialized
}

// _TODO: check if this is necessary and should be applied to starknet.js
protected get gatewayUrl(): string {
const url = this.hre.starknet.networkConfig.url;
if (this.externalServer.isDockerDesktop) {
Expand Down Expand Up @@ -127,6 +128,7 @@ export abstract class StarknetWrapper {

public async execute(
command:
// _TODO: remove
| "starknet"
| "starknet-compile-deprecated"
| "get_class_hash"
Expand Down Expand Up @@ -214,9 +216,13 @@ export abstract class StarknetWrapper {
}

public async declare(options: DeclareWrapperOptions): Promise<ProcessResult> {
const preparedOptions = this.prepareDeclareOptions(options);
const executed = await this.execute("starknet", preparedOptions);
return executed;
return this.hre.starknetJs.declare(
options.contract,
options.sender,
options.signature,
options.nonce,
options.maxFee
);
}

protected prepareCairoToSierraOptions(options: CairoToSierraOptions): string[] {
Expand Down Expand Up @@ -336,27 +342,19 @@ export abstract class StarknetWrapper {
}

public async getTxStatus(options: TxHashQueryWrapperOptions): Promise<ProcessResult> {
const preparedOptions = this.prepareTxQueryOptions("tx_status", options);
const executed = await this.execute("starknet", preparedOptions);
return executed;
return this.hre.starknetJs.getTxStatus(options.hash);
}

public async getTransactionTrace(options: TxHashQueryWrapperOptions): Promise<ProcessResult> {
const preparedOptions = this.prepareTxQueryOptions("get_transaction_trace", options);
const executed = await this.execute("starknet", preparedOptions);
return executed;
return this.hre.starknetJs.getTransactionTrace(options.hash);
}

public async getTransactionReceipt(options: TxHashQueryWrapperOptions): Promise<ProcessResult> {
const preparedOptions = this.prepareTxQueryOptions("get_transaction_receipt", options);
const executed = await this.execute("starknet", preparedOptions);
return executed;
return this.hre.starknetJs.getTransactionReceipt(options.hash);
}

public async getTransaction(options: TxHashQueryWrapperOptions): Promise<ProcessResult> {
const preparedOptions = this.prepareTxQueryOptions("get_transaction", options);
const executed = await this.execute("starknet", preparedOptions);
return executed;
return await this.hre.starknetJs.getTransaction(options.hash);
}

protected prepareBlockQueryOptions(options: BlockQueryWrapperOptions): string[] {
Expand All @@ -382,9 +380,7 @@ export abstract class StarknetWrapper {
}

public async getBlock(options: BlockQueryWrapperOptions): Promise<ProcessResult> {
const preparedOptions = this.prepareBlockQueryOptions(options);
const executed = await this.execute("starknet", preparedOptions);
return executed;
return this.hre.starknetJs.getBlock(options.hash ?? options.number);
}

protected prepareNonceQueryOptions(options: NonceQueryWrapperOptions): string[] {
Expand All @@ -408,9 +404,7 @@ export abstract class StarknetWrapper {
}

public async getNonce(options: NonceQueryWrapperOptions): Promise<ProcessResult> {
const preparedOptions = this.prepareNonceQueryOptions(options);
const executed = await this.execute("starknet", preparedOptions);
return executed;
return this.hre.starknetJs.getNonce(options.address, options.blockHash ?? options.blockNumber);
}

public async getClassHash(artifactPath: string): Promise<string> {
Expand Down
2 changes: 2 additions & 0 deletions src/type-extensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Account } from "./account";
import { Transaction, TransactionReceipt, Block, TransactionTrace } from "./starknet-types";
import { StarknetChainId } from "./constants";
import { AmarnaDocker } from "./external-server/docker-amarna";
import { StarknetLegacyWrapper } from "./starknet-js-wrapper";

declare module "hardhat/types/config" {
export interface ProjectPathsUserConfig {
Expand Down Expand Up @@ -87,6 +88,7 @@ declare module "hardhat/types/runtime" {
starknetWrapper: StarknetWrapper;
amarnaDocker: AmarnaDocker;
starknet: StarknetTypes.Starknet;
starknetJs: StarknetLegacyWrapper
}

type StarknetContract = StarknetContractType;
Expand Down
71 changes: 53 additions & 18 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as fs from "fs";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { selector } from "starknet";
import { SequencerProvider, selector } from "starknet";

import { adaptInputUtil, adaptOutputUtil } from "../adapt";
import {
Expand Down Expand Up @@ -525,6 +525,10 @@ export class StarknetContract {
return;
}

get provider(): SequencerProvider {
return this.hre.starknetJs.provider;
}

/**
* Set a custom abi and abi path to the contract
* @param implementation the contract factory of the implementation to be set
Expand Down Expand Up @@ -591,6 +595,19 @@ export class StarknetContract {
args?: StringMap,
options: InvokeOptions = {}
): Promise<InvokeResponse> {

// _TODO: revisit
// const { transaction_hash: txHash } = await this.provider.invokeFunction({
// contractAddress: this.address,
// entrypoint: functionName,
// calldata: args,
// signature: options.signature.map(String)
// }, {
// nonce: options.nonce ?? await this.provider.getNonceForAddress(this.address),
// maxFee: options.maxFee,
// version: InteractChoice.INVOKE.transactionVersion
// });

const executed = await this.interact(InteractChoice.INVOKE, functionName, args, options);
const txHash = extractTxHash(executed.stdout.toString());

Expand Down Expand Up @@ -638,17 +655,25 @@ export class StarknetContract {
args?: StringMap,
options: CallOptions = {}
): Promise<StringMap> {
const adaptedOptions = defaultToPendingBlock(options);
const executed = await this.interact(
InteractChoice.CALL,
functionName,
args,
adaptedOptions
);
if (options.rawOutput) {
return { response: executed.stdout.toString().split(" ") };
try {
const adaptedOptions = defaultToPendingBlock(options);

const { result } = await this.provider.callContract({
contractAddress: this.address,
entrypoint: functionName,
calldata: args
}, adaptedOptions.blockNumber);
// align to legacy stdout output
const response = result.join(" ");

if (options.rawOutput) {
return { response };
}
return this.adaptOutput(functionName, response);

} catch (error) {
throw new StarknetPluginError("Contract call failure", error as Error);
}
return this.adaptOutput(functionName, executed.stdout.toString());
}

/**
Expand Down Expand Up @@ -690,13 +715,23 @@ export class StarknetContract {
options: EstimateFeeOptions = {}
): Promise<starknet.FeeEstimation> {
const adaptedOptions = defaultToPendingBlock(options);
const executed = await this.interact(
InteractChoice.ESTIMATE_FEE,
functionName,
args,
adaptedOptions
);
return parseFeeEstimation(executed.stdout.toString());

const result = await this.provider.getInvokeEstimateFee({
contractAddress: this.address,
calldata: args,
signature: adaptedOptions.signature.map(String)
}, {
nonce: adaptedOptions.nonce ?? await this.provider.getNonceForAddress(this.address),
maxFee: adaptedOptions.maxFee,
version: InteractChoice.ESTIMATE_FEE.transactionVersion
}, options.blockNumber);

return {
amount: result.overall_fee,
unit: "wei",
gas_price: result.gas_price,
gas_usage: result.gas_consumed
};
}

/**
Expand Down

0 comments on commit 09b4e37

Please sign in to comment.