R3E Smart Contract Deploy v1.0.2 - Simplified deployment toolkit for Neo smart contracts. Features environment management, multi-contract deployment, verification tools, and deployment automation. Perfect for mainnet and testnet deployments
$ dotnet add package R3E.SmartContract.DeployA comprehensive deployment toolkit for Neo N3 smart contracts, providing infrastructure for compiling, deploying, initializing, and managing smart contracts.
Deploy a contract in just 2 lines:
var toolkit = new DeploymentToolkit().SetNetwork("testnet");
var result = await toolkit.DeployAsync("MyContract.csproj");
That's it! The toolkit automatically handles:
dotnet add package R3E.SmartContract.Deploy
using R3E.SmartContract.Deploy;
var toolkit = new DeploymentToolkit().SetNetwork("testnet");
var result = await toolkit.DeployAsync("MyContract.csproj");
var deployerAccount = await toolkit.GetDeployerAccountAsync();
// Approach 1: Compile and deploy from source
var compilationOptions = new CompilationOptions
{
SourcePath = "MyContract.cs",
OutputDirectory = "bin/contracts",
ContractName = "MyContract"
};
var deploymentOptions = new DeploymentOptions
{
RpcUrl = "http://localhost:10332",
DeployerAccount = deployerAccount,
GasLimit = 50_000_000 // 0.5 GAS
};
var result = await toolkit.CompileAndDeployAsync(compilationOptions, deploymentOptions);
Console.WriteLine($"Contract deployed: {result.ContractHash}");
// Initialize the deployed contract
var invocationOptions = new InvocationOptions
{
RpcUrl = "http://localhost:10332",
SignerAccount = deployerAccount,
GasLimit = 20_000_000 // 0.2 GAS
};
await toolkit.InvokeContractAsync(
result.ContractHash,
"initialize",
new object[] { "setup parameter" },
invocationOptions
);
// Approach 2: Deploy from pre-compiled artifacts
var result = await toolkit.DeployFromArtifactsAsync(
"MyContract.nef",
"MyContract.manifest.json",
deploymentOptions
);
// Deploy Token Contract first
var tokenResult = await toolkit.CompileAndDeployAsync(tokenOptions, deploymentOptions);
// Deploy Governance Contract
var govResult = await toolkit.CompileAndDeployAsync(govOptions, deploymentOptions);
// Initialize Governance with Token reference
await toolkit.InvokeContractAsync(
govResult.ContractHash,
"initialize",
new object[] { tokenResult.ContractHash }, // Pass token hash as dependency
invocationOptions
);
// Deploy Main Contract that depends on both
var mainResult = await toolkit.CompileAndDeployAsync(mainOptions, deploymentOptions);
// Initialize Main Contract with both dependencies
await toolkit.InvokeContractAsync(
mainResult.ContractHash,
"initialize",
new object[] {
tokenResult.ContractHash,
govResult.ContractHash
},
invocationOptions
);
public class TokenAndGovernanceDeploymentStep : BaseDeploymentStep
{
private readonly IContractInvoker _invoker;
public override string Name => "Deploy Token and Governance";
public override int Order => 20;
public override async Task<bool> ExecuteAsync(DeploymentContext context)
{
// Deploy token contract
var tokenContract = await context.ContractLoader.LoadContractAsync("TokenContract");
var tokenResult = await context.DeploymentService.DeployContractAsync(
tokenContract.Name,
tokenContract.NefBytes,
tokenContract.Manifest
);
// Deploy governance contract
var govContract = await context.ContractLoader.LoadContractAsync("GovernanceContract");
var govResult = await context.DeploymentService.DeployContractAsync(
govContract.Name,
govContract.NefBytes,
govContract.Manifest
);
// Configure governance to manage token
await context.DeploymentService.InvokeContractAsync(
govResult.Hash!,
"setTokenContract",
tokenResult.Hash!
);
return true;
}
}
var invoker = serviceProvider.GetRequiredService<IContractInvoker>();
// Call without transaction (read-only)
var balance = await invoker.CallAsync<BigInteger>(
contractHash,
"balanceOf",
accountHash
);
// Send transaction
var txHash = await invoker.SendAsync(
contractHash,
"transfer",
fromAccount,
toAccount,
amount
);
// Wait for confirmation
await invoker.WaitForConfirmationAsync(txHash);
WaitForConfirmation: Whether to wait for transaction confirmationConfirmationRetries: Number of retries when waitingConfirmationDelaySeconds: Delay between confirmation checksContractsPath: Path to compiled contracts directoryRpcUrl: Neo RPC endpoint URLNetwork: Network name (private, testnet, mainnet)WalletPath: Path to NEP-6 wallet filePassword: Wallet password (or use WALLET_PASSWORD env var)DefaultAccount: Default account addressThe deployment toolkit has optional support for Neo Express, making local development and debugging easier. Neo Express is NOT required - you can use any Neo node (local or remote).
Neo Express is recommended for:
For production deployments, use:
Install Neo Express (if desired):
dotnet tool install -g Neo.Express
Enable in your code:
// In your Program.cs or Startup.cs
services.AddNeoDeploymentServices(configuration);
// Optionally add Neo Express support
if (configuration.GetValue<bool>("Deployment:UseNeoExpress"))
{
services.AddNeoExpressSupport(configuration);
}
Configure in appsettings.json:
{
"Deployment": {
"UseNeoExpress": true // Set to true only if you want to use Neo Express
},
"NeoExpress": {
"ConfigFile": "neo-express.json",
"AutoStart": true,
"AutoCreate": true,
"EnableCheckpoints": true,
"SecondsPerBlock": 1
}
}
When enabled, the deployment toolkit will:
Transaction Details: Enable debug logging to see detailed transaction information
dotnet run --Logging:LogLevel:Default=Debug
Invoke Results: All contract invocations show detailed results in debug mode
Storage Inspection: Use DebugHelper to inspect contract storage
await DebugHelper.PrintStorageAsync(logger, blockchain, contractHash, storageKeys);
Checkpoints: Create checkpoints at any point during deployment
await DebugHelper.CreateDebugCheckpointAsync(logger, neoExpress, "checkpoint_name", "description");
Useful Neo Express commands for debugging:
# Show blockchain info
neoxp show
# List wallets
neoxp wallet list
# Show transaction
neoxp show transaction <txid>
# Create checkpoint
neoxp checkpoint create <name>
# Restore checkpoint
neoxp checkpoint restore <name>
# Reset blockchain
neoxp reset -f
The deployment toolkit tracks all deployments and supports updating existing contracts:
Use DeployOrUpdateContractStep to automatically deploy new contracts or update existing ones:
services.AddTransient<IDeploymentStep>(sp =>
new DeployOrUpdateContractStep(
sp.GetRequiredService<ILogger<DeployOrUpdateContractStep>>(),
sp.GetRequiredService<IDeploymentService>(),
sp.GetRequiredService<IContractUpdateService>(),
sp.GetRequiredService<IDeploymentRecordService>(),
sp.GetRequiredService<IContractLoader>(),
sp.GetRequiredService<IOptions<NetworkOptions>>(),
"MyContract",
"2.0.0" // Optional version
));
var updateService = serviceProvider.GetRequiredService<IContractUpdateService>();
// Check if update is possible
var checkResult = await updateService.CheckUpdateAsync("MyContract", "testnet");
if (checkResult.CanUpdate)
{
// Update the contract
var result = await updateService.UpdateContractAsync(
"MyContract",
"testnet",
nefBytes,
manifest,
updateData: null,
version: "2.0.0"
);
}
All deployments are tracked in .deployments/ directory with network-specific records:
{
"testnet": {
"contractHash": "0x1234...",
"transactionHash": "0x5678...",
"deployedAt": "2024-01-01T00:00:00Z",
"version": "1.0.0",
"updateHistory": [
{
"transactionHash": "0x9abc...",
"updatedAt": "2024-01-02T00:00:00Z",
"previousVersion": "1.0.0",
"newVersion": "1.1.0"
}
]
}
}
Use DeploymentManager utility:
var manager = serviceProvider.GetRequiredService<DeploymentManager>();
// List all deployments on a network
await manager.ListDeploymentsAsync("testnet");
// Show detailed deployment info
await manager.ShowDeploymentAsync("MyContract", "testnet");
// Export deployment records
var json = await manager.ExportDeploymentsAsync();
// Create deployment report
var report = await manager.CreateDeploymentReportAsync();
update method to be updatableVerifyAfterDeploy = true// Use environment variables (recommended)
export NEO_WALLET_PASSWORD="your-secure-password"
// Or use secure credential provider
services.AddSingleton<ICredentialProvider, SecureCredentialProvider>();
var options = new DeploymentOptions
{
DryRun = true, // Test without deploying
VerifyAfterDeploy = true, // Verify contract exists
VerificationDelayMs = 5000, // Wait before verifying
EnableRollback = true // Enable rollback support
};
// Dry run first
var dryRun = await toolkit.DeployAsync("Contract.csproj", options);
Console.WriteLine($"Would deploy to: {dryRun.ContractHash}");
// Then actual deployment
options.DryRun = false;
var result = await toolkit.DeployAsync("Contract.csproj", options);
// Check overall health
var health = await toolkit.CheckHealthAsync();
Console.WriteLine($"Status: {health.Status}");
foreach (var (component, result) in health.Results)
{
Console.WriteLine($"{component}: {result.Status} - {result.Description}");
}
// Check specific component
var rpcHealth = await toolkit.CheckHealthAsync("rpc");
// Get deployment metrics
var metrics = toolkit.GetMetrics();
Console.WriteLine($"Total Deployments: {metrics.TotalDeployments}");
Console.WriteLine($"Success Rate: {metrics.SuccessRate:F2}%");
Console.WriteLine($"Average Gas: {metrics.AverageGasPerDeployment / 100_000_000m} GAS");
// Metrics by network
foreach (var (network, count) in metrics.DeploymentsByNetwork)
{
Console.WriteLine($"{network}: {count} deployments");
}
// Implement custom health check
public class CustomHealthCheck : IHealthCheck
{
public async Task<HealthCheckResult> CheckHealthAsync(CancellationToken ct)
{
// Your health check logic
if (everythingIsGood)
return HealthCheckResult.Healthy("All systems operational");
else
return HealthCheckResult.Degraded("Service is slow");
}
}
// Register custom health check
var healthService = toolkit.GetService<HealthCheckService>();
healthService.RegisterHealthCheck("custom", new CustomHealthCheck());
MIT License - see LICENSE file for details