@ganache/flavor
Ganache's flavor TypeScript types, helpers, and utility functions.
Ganache Flavors are plugins that can be used to launch test networks for chains
other than Ganache's built-in Ethereum networks. They are loaded at runtime via
Ganache's --flavor
flag.
Warnings and Gotchas
Ganache flavors are experimental, incomplete, and subject to change without
notice.
Ganache flavors are Ethereum JSON-RPC (2.0) inspired, and we are still working
to understand the needs of other chains and how to extend the Ganache flavor
model to support transports other than JSON-RPC 2.0 over HTTP/WS.
An example of shortcomings for non JSON-RPC chains:
- ganache only accepts POST requests over a single configurable path (defaults
to
/
). If you have a need for something else, like JSON over REST, gRPC, SOAP,
etc, please open an issue describing your use case. - websocket subscription-based messaging is very limited and only supports
responsing like Ethereum JSON-RPC does.
How to create an experimental ganache flavor
To create a new flavor, you must create a new package that exports a Flavor
. A
flavor is a JavaScript object that implements the Flavor
TypeScript interface.
Here is an example of a "Hello World"-style flavor in TypeScript:
import { Flavor, Connector, CliSettings } from "@ganache/flavor";
export type Provider = { sayHi: (name: string) => string };
export type RequestPayload = { name: string };
export type ResponsePayload = { result: string };
const provider: Provider = {
sayHi(name: string) {
return `Hello, ${name}`;
}
};
const helloConnector: Connector<Provider, RequestPayload, ResponsePayload> = {
provider,
async connect(): Promise<void> {
},
parse(message: Buffer) {
return JSON.parse(message);
},
async handle(
this: typeof helloConnector,
payload: RequestPayload
): Promise<{ value: unknown }> {
if (!payload || typeof payload.name !== "string") {
throw new Error("payload must have a `name` property of type `string`");
}
const value = this.provider.sayHi(payload.name);
return { value };
},
format(result: ResponsePayload, payload: RequestPayload) {
console.log(
`formatting result (${result.result}) for payload (${payload.name})`
);
return JSON.stringify({ result });
},
formatError(error: Error, payload: RequestPayload) {
console.log(`formatting error (${error.message}) for payload (${payload})`);
return JSON.stringify({ error: error.message });
},
async close() {
}
};
type HelloFlavor = Flavor<"hello-chain", typeof helloConnector>;
const HelloFlavor: HelloFlavor = {
flavor: "hello-chain",
options: {
},
connect(providerOptions: never) {
return helloConnector;
},
ready: ({
provider,
options
}: {
provider: Provider;
options: { server: CliSettings };
}) => {
console.log(
`Hello Chain server is running at http://${options.server.host}:${options.server.port}`
);
}
};
export default HelloFlavor;
Check out the example implementation for a more-in-depth example.