Config File Reference
All cubist applications have a JSON config file cubist-config.json
, which specifies:
"type"
: the kind of project the off-chain application code is written in (valid values:"TypeScript"
,"JavaScript"
, and"Rust"
),"build_dir"
: where Cubist will write build output (can be overridden viaCUBIST_BUILD_DIR
env var),"deploy_dir"
: where Cubist will generate deployment scripts and information (can be overridden viaCUBIST_DEPLOY_DIR
env var),"contracts"
: assignment of contracts to chains (currently per contract source file, not per contract)"network_profiles"
: named profiles containing network/chain configurations"current_network_profile"
: currently selected network profile (can be overridden viaCUBIST_NETWORK_PROFILE
env var).
Example JSON file:
{
"type": "TypeScript",
"build_dir": "./build",
"deploy_dir": "./deploy",
"contracts": {
"root_dir": "./contracts",
"targets": {
"avalanche": { "files": [ "./contracts/ava.sol" ] },
"polygon": { "files": [ "./contracts/poly.sol" ] },
"ethereum": { "files": [ "./contracts/eth1.sol", "./contracts/eth2.sol" ] }
}
},
"network_profiles": {
"default": {
"avalanche": { "url": "http://localhost:9560", "autostart": true },
"ethereum": { "url": "http://localhost:7545", "autostart": true },
"polygon": { "url": "http://localhost:9545", "autostart": true }
},
"dev": {
"avalanche": { "url": "http://otherhost:9560" },
"ethereum": { "url": "http://otherhost:7545" },
"polygon": {
"url": "wss://polygon-mumbai.g.alchemy.com/v2/${{env.ALCHEMY_MUMBAI_API_KEY}}",
"proxy": {
"port": 9545,
"chain_id": 80001,
"creds": [{
"mnemonic": { "seed": { "env": "MUMBAI_ACCOUNT_MNEMONIC" } }
}]
}
}
}
},
"current_network_profile": "dev"
}
In the following, we explain the different pieces of the configuration using this example.
Contract Configuration
First off, the user must specify a root directory under which all contract files live. At the moment, Cubist requires a single such root directory.
Next, each contract file must be assigned to a target chain. Contract file paths must be specified relative to the project root directory, and not the contracts root directory (hence the repetition of "./contracts/" in the example above); this is intentional, mimicking package imports in TypeScript.
Optionally, "import_dirs"
may be provided as well (when omitted, defaults to ["node_modules"]
).
As the name suggests, import directories are only used when resolving non-relative imports from
contract source files. In other words, contracts from import directories are not automatically
compiled and included in the final Cubist dapp.
Network Configuration
Each network profile (under the "network_profiles"
element) must provide configuration for every
target chain in use.
A portion of that configuration is common to all chains and it includes:
"url"
: URL of the chain endpoint"autostart"
: whether to start a local instance of the chain aturl
(applies only ifurl
is a localhost address)"proxy"
: whether and how to start a Cubist Proxy in front orurl
(applies only ifautostart
is false)
On some platforms, using localhost
directly in the configuration file leads
to errors. If you're having problems, changing the URL to http://127.0.0.1:...
should fix the issue.
(Using localhost
can cause issues because some operating systems will resolve
localhost
to the IPv6 address ::1
instead of the IPv4 address 127.0.0.1
. This
causes errors because the Cubist background processes listen only on the IPv4
address.)
Autostart Local Networks
If autostart
is true (default) and url
is a localhost address, Cubist (the cubist start
command, to be
precise) will automatically start a local instance of the specified chain. For example,
{
"ethereum": { "url": "http://localhost:8545", "autostart": true },
"polygon": { "url": "http://localhost:9545", "autostart": true }
}
will instruct Cubist to launch anvil
(an Ethereum implementation) and make its JSON RPC endpoint
available at http://localhost:8545
, as well as bor
(a Polygon implementation) and make its JSON
RPC endpoint available at http://localhost:9545
.
Cubist exposes different customization options for different target chains; here are some examples:
Example: Create funded accounts on Ethereum
The following configuration snippet will bootstrap anvil
with 2 funded accounts generated
from a specific mnemonic. The mnemonic is read from the ETHEREUM_MNEMONIC
environment
variable (.env
files are supported).
{
"url": "http://localhost:8545",
"autostart": true,
"bootstrap_mnemonic": {
"seed": { "env": "ETHEREUM_MNEMONIC" },
"account_count": 2
}
}
Example: Create funded accounts on Polygon
The following configuration snippet will bootstrap bor
with 2 funded accounts,
one generated from a given mnemonic (read from the POLYGON_MNEMONIC
env var)
and one from a given private key (read from the .polygon.secret
file).
{
"url": "http://localhost:8545",
"autostart": true,
"local_accounts": [
{ "mnemonic": { "seed": { "env": "POLYGON_MNEMONIC" } } },
{ "private_key": { "hex": { "file": ".polygon.secret" } } }
]
}
Connect to a Public Network
Before discussing how to configure Cubist to connect to public networks, we must first briefly introduce Cubist Proxy.
Cubist Proxy
Cubist Proxy is a component that sits between the client dapp and an actual chain endpoint node and automatically signs every transaction submitted via eth_sendTransaction. All other JSON RPC requests (with a few exceptions) are forwarded to the real endpoint as is.
This is very useful when connecting to a public network, because all credentials can be securely managed by Cubist Proxy. Consequently, the client-side app doesn't have to worry about managing secrets, signing, computing nonces, etc. Proxy configuration includes:
"port"
: localhost port on which the proxy will be listening for requests"chain_id"
: id of the target chain (so that Cubist Proxy can automatically set it if not set by the client app)"creds"
: credentials to use.
For every eth_sendTransaction request it receives, Cubist Proxy:
- extracts the
sender
field from it, - looks up the credentials for that sender,
- populates any missing transaction parameters (e.g., chain id, nonce, etc.),
- signs the transaction on behalf of
sender
, and finally - forwards the signed transaction to the endpoint via eth_sendRawTransaction.
Example: Connecting to a Public Testnet
When using local chains only, no explicit account/authentication configuration needs to be provided by the user. That's because Cubist will use a default account to start each local chain, and Cubist Proxy will use that same account to automatically sign transactions targeting that chain.
In contrast, when connecting to a public network, the user must already have an account set up with that network provider. If that network only accepts signed transactions (and most likely it does), Cubist must be configured to sign transactions using that existing account. Because the Cubist SDK does not deal with signing at all (by design!), the solution is to configure and run a Cubist Proxy in front of the remote endpoint.
The url
field is used to specify a public network endpoint, e.g.,
wss://polygon-mumbai.g.alchemy.com/v2/${{env.ALCHEMY_MUMBAI_API_KEY}}
api-key.
Additionally, Cubist Proxy must be configured with at least one
credential, which it will use to sign all transactions. The following example specifies a bip39
mnemonic:
{
"url": "wss://polygon-mumbai.g.alchemy.com/v2/${{env.ALCHEMY_MUMBAI_API_KEY}}",
"proxy": {
"port": 9545,
"chain_id": 80001,
"creds": [{
"mnemonic": { "seed": { "env": "MUMBAI_ACCOUNT_MNEMONIC" } }
}]
}
}
- Note the use of
${{env.ALCHEMY_MUMBAI_API_KEY}}
: instead of hardcoding the secret API key into the plain-text config file, we use the special${{env.VAR_NAME}}
syntax to instruct Cubist to read it from the environment.↩