locklift
Version:
Node JS framework for working with Ever contracts. Inspired by Truffle and Hardhat. Helps you to build, test, run and maintain your smart contracts.
243 lines (214 loc) • 7.66 kB
text/typescript
import { ProviderRpcClient } from "everscale-inpage-provider";
import {
Clock,
EverscaleStandaloneClient,
SimpleAccountsStorage,
SimpleKeystore,
} from "everscale-standalone-client/nodejs";
import chalk from "chalk";
import { Keys } from "./internal/keys";
import { ConfigState, LockliftConfig, NetworkValue } from "./internal/config";
import * as utils from "./utils";
import { Transactions } from "./utils";
import { Factory, FactoryType, Giver } from "./internal/factory";
import { createTracing, Tracing } from "./internal/tracing";
import { createTimeMovement, TimeMovement } from "./internal/timeMovement";
import { LockliftContext } from "./internal/context/lockliftContext";
import "./chaiPlugin/types";
import { initializeExtenders } from "./plugins/utils";
import { getGiverKeyPair } from "./internal/giver/utils";
import { getGiver } from "./internal/giver";
import { logger } from "./internal/logger";
import { TracingTransport } from "./internal/tracing/transport";
import { LockliftNetwork } from "@broxus/locklift-network";
import { ConnectionData } from "everscale-standalone-client";
import { Network } from "./internal/network";
import { ForkService } from "./internal/network/forkService";
export * from "everscale-inpage-provider";
export type { Signer } from "everscale-standalone-client";
export { Dimension, zeroAddress } from "./constants";
export type { LockliftConfig } from "./internal/config";
export type { Giver } from "./internal/factory";
export { toNano, fromNano, getRandomNonce, convertAmount } from "./utils";
export { WalletTypes } from "./types";
export { TraceType, InteractionType } from "./internal/tracing/types";
export { lockliftChai } from "./chaiPlugin";
export { NetworkValue, ConfigState } from "./internal/config";
export class Locklift<FactorySource extends FactoryType> {
public readonly utils = utils;
#giver?: Giver;
#factory: Factory<FactorySource> | undefined;
#context: LockliftContext | undefined;
#testing: TimeMovement | undefined;
#tracing: Tracing | undefined;
#network: Network | undefined;
private constructor(
public readonly provider: ProviderRpcClient,
public readonly keystore: SimpleKeystore,
private readonly clock: Clock,
public readonly transactions: Transactions,
) {}
set tracing(tracing: Tracing) {
this.#tracing = tracing;
}
get tracing(): Tracing {
if (!this.#tracing) {
throw new Error("Testing module not provided");
}
return this.#tracing;
}
set testing(testing: TimeMovement) {
this.#testing = testing;
}
get testing(): TimeMovement {
if (!this.#testing) {
throw new Error("Testing module not provided");
}
return this.#testing;
}
set context(context: LockliftContext) {
this.#context = context;
}
get context(): LockliftContext {
if (!this.#context) {
throw new Error("Context not provided, need to provide the network name");
}
return this.#context;
}
set factory(factory: Factory<FactorySource>) {
this.#factory = factory;
}
get factory(): Factory<FactorySource> {
if (!this.#factory) {
throw new Error("Factory didn't provided");
}
return this.#factory;
}
set network(network: Network) {
this.#network = network;
}
get network(): Network {
if (!this.#network) {
throw new Error("Network didn't provided");
}
return this.#network;
}
set giver(giver: Giver) {
this.#giver = giver;
}
get giver(): Giver {
if (!this.#giver) {
throw new Error("Giver not initialized, need to provide the network name");
}
return this.#giver;
}
public static async setup<T extends FactoryType>(
config: LockliftConfig<ConfigState.INTERNAL>,
network?: keyof LockliftConfig["networks"],
): Promise<Locklift<T>> {
if (network && !config.networks[network]) {
throw new Error(`Network ${network} not found in config`);
}
const networkConfig = config.networks[network as string] as NetworkValue<ConfigState.INTERNAL> | undefined;
let keystore = new SimpleKeystore();
if (networkConfig) {
const giverKeys = getGiverKeyPair(networkConfig.giver);
const keys = await Keys.generate(networkConfig.keys);
keystore = new SimpleKeystore(
[...keys].reduce(
(acc, keyPair, idx) => ({
...acc,
[idx]: keyPair,
}),
{},
),
);
keystore.addKeyPair("giver", giverKeys);
}
const forkService =
networkConfig?.fork &&
(await ForkService.init({
forkSource: networkConfig.fork.source,
forkContractsConfig: networkConfig.fork.contracts,
}));
const proxyNetwork = new LockliftNetwork({
accountFetcher: networkConfig?.fork && forkService?.accountFetcher,
networkConfig: networkConfig?.blockchainConfig,
});
await proxyNetwork.initialize();
if (
!!networkConfig &&
isProxyConnection(networkConfig?.connection) &&
networkConfig?.connection?.data?.connectionFactory === undefined
) {
networkConfig.connection.data.connectionFactory = proxyNetwork.connectionFactory;
}
const accountsStorage = new SimpleAccountsStorage();
const clock = new Clock();
const provider = new ProviderRpcClient({
provider: EverscaleStandaloneClient.create({
connection: networkConfig?.connection,
keystore,
clock,
accountsStorage,
message: networkConfig?.clientConfig?.message,
initInput: networkConfig?.clientConfig?.initInput,
}).then(client => {
if (isProxyConnection(networkConfig?.connection)) {
client.setPollingInterval(5);
}
return client;
}),
});
try {
await provider.ensureInitialized();
} catch (e: any) {
logger.printError(`${chalk.bold(`${e.message}`)}`);
process.exit(1);
}
const transactions = new Transactions(provider);
const locklift = new Locklift<T>(provider, keystore, clock, transactions);
const giver = networkConfig && (await getGiver(networkConfig.giver, provider, accountsStorage));
if (giver) {
locklift.giver = giver;
}
const factory = await Factory.setup<T>(
provider,
() => locklift.giver,
accountsStorage,
forkService?.preFetchedAccounts,
);
locklift.factory = factory;
const tracingTransport = (() => {
switch (networkConfig?.connection.type) {
case "graphql":
return TracingTransport.fromGqlConnection(networkConfig.connection.data.endpoints[0], provider);
case "jrpc":
return TracingTransport.fromJrpcConnection(provider);
case "proxy":
return TracingTransport.fromProxyConnection(provider);
}
})() as TracingTransport;
locklift.network = new Network(proxyNetwork, (await keystore.getSigner("0"))!, accountsStorage, provider);
locklift.tracing = createTracing({
ever: provider,
features: transactions,
network: proxyNetwork,
factory,
tracingTransport,
});
if (networkConfig && network) {
const timeMovement = await createTimeMovement(clock, networkConfig);
const context = new LockliftContext({ config: networkConfig, name: network });
locklift.testing = timeMovement;
locklift.context = context;
}
await initializeExtenders({ locklift, config, network });
return locklift;
}
}
const isProxyConnection = <T extends ConnectionData>(
connectionData: T | undefined,
): connectionData is Extract<T, { type: "proxy" }> => {
return connectionData?.type === "proxy";
};