it's time to deploy.
Niacin is a tool for smart contract deployment, aka chain ops.
- a standard deployment format - addresses, ABI's, deploy metadata.
delegatecallproxies for upgradeable contracts, that are implemented safely and securely.- onchain dependency linking with
MixinResolver, with a resolution cache (much cheaper than a beacon). - centralized address registry in the
AddressProvider. - a drop-in CLI for upgradeable contracts - just
niacin deploy xyz. - useful tools:
- vendoring - one command to import contracts from Etherscan, and generate *.sol types for them.
- NPM package generator - generate an NPM package for your subgraphs and frontends.
- deployment docs generator - generate a website for your deployments, including an interactive UI.
- well-designed:
- automatically keeps track of git metadata, so you can revert easily
- automatically tracks the deploy block, so you don't have to copy-paste that into the subgraph
1. Install the CLI.
npm i -g niacin-cli2. Install the contracts.
forge install liamzebedee/niacin-contracts3. Start playing.
niacin initforge build
niacin deploy YourContractA YourContractBniacin statusNiacin is oriented around the manifest file, which describes all the information about a deployment to an EVM chain.
You can operate on different chains by switching the manifest you are using with the --manifest argument.
Niacin does safety checks on chain ID, so you can't overwrite your deployments by accident.
Here's an example:
export PRIVATE_KEY="0x"
# Deploy multichain.
mkdir deployments/
RPC_URL="" niacin deploy -a --manifest deployments/local.json
RPC_URL="https://polygon‑rpc.com" niacin deploy -a --manifest deployments/polygon.json --gas-estimator polygon
RPC_URL="https://arb1.arbitrum.io/rpc" niacin deploy -a --manifest deployments/arbitrum.json
# Generate index.js API's.
niacin generate-npm-pkg --manifest deployments/local.json > deployments/local.js
niacin generate-npm-pkg --manifest deployments/polygon.json > deployments/polygon.js
niacin generate-npm-pkg --manifest deployments/arbitrum.json > deployments/arbitrum.js
# Interact with contracts.
niacin call --manifest deployments/local.json
niacin call --manifest deployments/polygon.json
niacin call --manifest deployments/arbitrum.json
# Get deployments status for each chain.
niacin status --manifest deployments/local.json
niacin status --manifest deployments/polygon.json
niacin status --manifest deployments/arbitrum.jsonSay you have two contracts, a ProtocolManager and a FeePool, and you want to get the address of the FeePool contract.
// FeePool.sol
contract FeePool {
// ...
}Niacin automatically links your contract dependencies together. Just import the MixinResolver as so:
// ProtocolManager.sol
import {MixinResolver} from "niacin-contracts/mixins/MixinResolver.sol";
contract ProtocolManager is
MixinResolver
{
function getDependencies() public override pure returns (bytes32[] memory addresses) {
bytes32[] memory requiredAddresses = new bytes32[](1);
requiredAddresses[0] = bytes32("FeePool");
return requiredAddresses;
}
function feePool() internal view returns (FeePool) {
return FeePool(requireAddress(bytes32("FeePool")));
}
function swap() external {
FeePool pool = feePool();
// do whatever you want.
}
}niacin generate-npm-pkg --manifest manifest.json > index.jsYou can use this in your JS code like so:
const deployments = require('./index.js')
const ethers = require('ethers')
const provider = new ethers.providers.JsonRpcProvider()
const ProtocolManager = new ethers.Contract(
deployments.ProtocolManager.address,
deployments.ProtocolManager.abi,
provider
)You can also automatically use Niacin from the CLI:
# Show your deployments
niacin call
# Call a specific contract
niacin call ProtocolManagerBUG: Note that for now, arguments which are hexadecimal strings (addresses, bytes) must be enclosed with quotes and without the 0x prefix. e.g. 0x12312313 -> "12312313".
Import third-party contracts from Etherscan EVM chains:
niacin add-vendor --name Curve3Pool --fetch-from-etherscan https://optimistic.etherscan.io/address/0x1337BedC9D22ecbe766dF105c9623922A27963EC
niacin generate-sol-interface --name Curve3Pool > src/vendor/Curve3Pool.sol
niacin generate-npm-pkg > index.jsNot yet integrated: autogenerated contract UI's.
Due to how upgradeable contracts are implemented, you cannot use constructors. Instead, Niacin provides a simple initialize which can be called from a postdeploy script.
Import the MixinInitializable and write an initialize function with the initializer modifier:
// ProtocolManager.sol
import {MixinResolver} from "niacin-contracts/mixins/MixinResolver.sol";
import {MixinInitializable} from "niacin-contracts/mixins/MixinInitializable.sol";
contract ProtocolManager is
MixinResolver,
MixinInitializable
{
function initialize()
public
initializer
{
// ...
}
function getDependencies() public override pure returns (bytes32[] memory addresses) {
bytes32[] memory requiredAddresses = new bytes32[](1);
requiredAddresses[0] = bytes32("FeePool");
return requiredAddresses;
}
function feePool() internal view returns (FeePool) {
return FeePool(requireAddress(bytes32("FeePool")));
}
function swap() external {
FeePool pool = feePool();
// do whatever you want.
}
}I'm still working on the UX of initializers.
For now, you can write a simple postdeploy script which will run after invoking niacin deploy for a deployment.
Create a new script postdeploy.js and include this code:
module.exports = async function (niacin) {
const {
ProtocolManager
} = niacin.contracts
await niacin.initialize({
contract: ProtocolManager,
args: []
})
}Open the .niacinrc.js and paste the following:
module.exports = {
version: "0.1.0",
ignore: [],
scripts: {
initialize: require('./deploy/initialize')
}
}
Your initializer will now run as part of niacin deploy.