Deployment scripts¶
Note
Hey! Thanks for trying out Mars. We are still working on this section, so expect more content here in the future.
While we build the documentation you are welcome to check out the code at our github repository. Maybe you fancy contributing?
Deployment scripts are… well, they are scripts that declare how contracts should be deployed.
Let’s learn Mars features syntax by looking at the example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import { contract, createProxy, deploy, runIf, debug, reduce } from 'ethereum-mars' import { Market, Token, UpgradeabilityProxy } from '../build/artifacts' import { Address } from 'ethereum-mars/build/src/symbols' void deploy({}, (deployer) => { const appleImplementation = contract('apple', Token) const orangeImplementation = contract('orange', Token, { gasLimit: 1000000 }) const deployBehindProxy = createProxy(UpgradeabilityProxy, 'upgradeTo') const apple = deployBehindProxy(appleImplementation, 'initialize', [100]) const orange = deployBehindProxy(orangeImplementation, 'initialize', [200]) const market = contract(Market, [apple, orange]) debug('Apple', apple) debug('Allowances', [apple.allowance(deployer, market), orange.allowance(deployer, market)]) runIf(apple.allowance(deployer, market).equals(0), () => apple.approve(market, 100)).else(() => orange.approve(market, 100) ) reduce([apple[Address], orange[Address]], (appleAddress, orangeAddress) => console.log(`${appleAddress}${orangeAddress}`) ) apple.approve(market, apple.totalSupply().add(42), { gasLimit: 1000000 }) orange.approve(market, orange.totalSupply()) }) |
Just like any TS file, Mars scripts start with imports. On line 1, we import Mars helper functions that we aim to use in the script. On second line, we can see import of generated artifacts.
deploy(options: Options, callback: (deployer: string, config: Config) => void)
¶
On line 4, the deploy
function is called. This is the main entry point of the script.
options
¶
interface Options {
privateKey?: string
network?: string
infuraApiKey?: string
alchemyApiKey?: string
outputFile?: string
gasPrice?: BigNumber
dryRun?: boolean
noConfirm?: boolean
verify?: boolean
etherscanApiKey?: string
sources?: string
waffleConfig?: string
}
Note
Options passed through code have lower priority than those passed with CLI.
callback
¶
type Callback = (deployer: string, options: ExecuteOptions) => void
Function where deployment logic is passed. It may provide an address of the deployer and options of the deployment execution.
ExecuteOptions
¶
interface ExecuteOptions extends TransactionOptions {
network: string
deploymentsFile: string
dryRun: boolean
verification?: {
etherscanApiKey: string
jsonInputs: JsonInputs
waffleConfig: string
}
}
Important
All transactions will be performed after callback
is executed. This means that
no data is available during runtime. As a consequence, for example if
statements may result in unexpected behaviour.
To learn more, see Futures
contract(name?: string, artifact: Artifact, constructorArgs?: Array<any>, options?: TransactionOptions): ContractInstance
¶
1 2 3 4 5 6 | void deploy({}, (deployer) => { const appleImplementation = contract('apple', Token) const orangeImplementation = contract('orange', Token, { gasLimit: 1000000 }) const deployBehindProxy = createProxy(UpgradeabilityProxy, 'upgradeTo') const apple = deployBehindProxy(appleImplementation, 'initialize', [100]) const orange = deployBehindProxy(orangeImplementation, 'initialize', [200]) |
Deploy contract with corresponding artifact
, named name
. Before performing deploy, mars will check deployments.json
file for already deployed contracts with same name.
If this contract has been deployed previously, it will compare their bytecode. If contract hasn’t changed, deploy will be skipped.
Warning
Changing compiler version or optimisation options may result in bytecode change and therefore contract redeploy.
name
¶
First (optional) argument specifies name under which contract will be saved in deployment result file. If name is skipped, it will be named same as a contract but with lowercase first letter.
artifact
¶
Artifacts of the contract we want to deploy.
constructorArgs
¶
List of arguments passed to contract’s constructor. If constructor is missing or is parameterless, this argument can be skipped.
options
¶
Optional argument that allows to override some parameters of the transaction.
interface TransactionOptions {
wallet?: Wallet
gasPrice?: BigNumber
gasLimit?: number | BigNumber
noConfirm?: boolean
}
- wallet
Wallet that will sign the transaction.
- gasPrice
Gas price in gwei
- gasLimit
Gas limit for the transaction. Usually limit is correctly estimated by Mars, but sometimes it needs to be specified explicitly.
- noConfirm
Send transaction without confirmation
returns
¶
Future ContractInstance, i.e. contract that will get eventually deployed. We can perform transactions on this contract, call view functions and pass it as argument. To be more precise, we can only declare our intent of doing so (remember, nothing is yet deployed when deploy script is executed)
Hint
With every state-changing transaction, Mars will print message similar to this:
Transaction: Deploy apple
Fee: $1.01, Ξ0.000875217
Balance: $728.54, Ξ0.630241122
ENTER to submit, Ctrl+C to exit...
If you don’t want to confirm every transaction, set --yes
flag.
createProxy(artifact: Artifact, constructorArgs?: Array<any>, onUpgrade?: MethodCall): ProxyFactory
¶
Create factory allowing us to deploy upgradeable contracts.
The proxy contract is expected to have implementation()
view method, that will return current implementation’s address and a way to change implementation.
artifact
¶
Artifacts of the proxy contract.
constructorArgs
¶
List of arguments passed to contract’s constructor. If constructor is missing or is parameterless, this argument can be skipped.
onUpgrade
¶
What will happen when contract is upgraded. This may be passed in two ways:
- As a method name (
upgradeTo
by default) When contract behind proxy changes, Mars will call
upgradeMethod(newImplementation)
- As a method name (
- As a callback (proxyContract) => void
When contract behind proxy changes, the callback with current proxy instance will be called
returns
¶
Function proxyFactory(name?: string, contract: ContractInstance, initializer: MethodCall, args: Array<any>)
This function takes contract and hides it behind proxy. initializer
is function that will be called only once, when proxy is first deployed. Initializer’s format is similar to onUpgrade
of createProxy
.
args
is a list of arguments passed into initializer.
Hint
If implementation lacks initializer, pass empty function instead () => {}
.
Note
If name
is missing, proxy will be named {contractName}_proxy
runIf(predicate: Predicate, action: () => void)
¶
Since we don’t get results of our actions during runtime, we cannot do any logic using build-in Javascript expressions (like if
or switch
statesments).
That’s where runIf
function comes with rescue. The way it works is very simple: if predicate
is true, it executes action
; otherwise it checks else
calls.
runIf(someFutureBoolean, () => /* some action */)
.elseIf(someFutureNumber.equals(2), () => /* another action */)
// stops if someFutureNumber == 2
.elseIf(false, () => {})
.else(() => /* executes only if every predicate was false */)
debug(...messages: any[]`
¶
If we want to print some results of deployment process, we probably don’t want to use normal console.log
s.
After all, we will send our first transaction only after console.logs are executed. That’s where debug
is handy - it takes any amount of Future or not values and
formats them nicely and prints only during deployment process.