Deploy Functions¶
The Deploy Functions module provides comprehensive deployment capabilities for smart contracts, diamond proxies, and complete project infrastructures on multiple blockchain networks. It handles automated deployment pipelines, configuration management, and post-deployment verification.
Overview¶
The Deploy Functions provide:
- Smart Contract Deployment: Deploy individual contracts and contract suites
- Diamond Deployment: Deploy and configure diamond proxy contracts
- Multi-Network Support: Deploy across multiple blockchain networks
- Configuration Management: Manage deployment configurations and parameters
- Verification: Automated contract verification and validation
- Rollback Support: Rollback capabilities for failed deployments
Key Features¶
Deployment Types¶
- Single Contract: Deploy individual smart contracts
- Diamond Proxy: Deploy upgradeable diamond contracts
- Project Suite: Deploy complete project infrastructures
- Template Deployment: Deploy from predefined templates
Network Management¶
- Multi-Network: Support for multiple blockchain networks
- Gas Optimization: Automatic gas price optimization
- Network Validation: Pre-deployment network validation
- Cross-Chain Coordination: Coordinate deployments across networks
Automation Features¶
- Pipeline Automation: Automated deployment pipelines
- Configuration Validation: Validate deployment configurations
- Post-Deployment Testing: Automated testing after deployment
- Monitoring Integration: Integration with monitoring systems
Core Functions¶
deployContract()¶
Deploys a single smart contract to a specified network.
Parameters:
interface DeployContractRequest {
contractName: string;
network: string;
constructorArgs?: any[];
deploymentConfig?: {
gasLimit?: string;
gasPrice?: string;
confirmations?: number;
timeout?: number;
};
verificationConfig?: {
verify: boolean;
apiKey?: string;
constructorArgsEncoded?: string;
};
metadata?: {
projectId?: string;
version?: string;
description?: string;
};
}
Returns:
interface DeployContractResponse {
success: boolean;
contractAddress?: string;
transactionHash?: string;
gasUsed?: string;
deploymentCost?: string;
verificationStatus?: 'pending' | 'verified' | 'failed';
message: string;
}
Usage:
const result = await Parse.Cloud.run('deployContract', {
contractName: 'MyToken',
network: 'polygon',
constructorArgs: ['My Token', 'MTK', 18, '1000000000000000000000000'],
deploymentConfig: {
gasLimit: '2000000',
confirmations: 3
},
verificationConfig: {
verify: true
},
metadata: {
projectId: 'proj_123',
version: '1.0.0',
description: 'ERC20 token for my project'
}
});
deployDiamond()¶
Deploys a diamond proxy contract with specified facets.
Parameters:
interface DeployDiamondRequest {
network: string;
diamondName: string;
facets: FacetDeployment[];
initContract?: string;
initData?: string;
owner?: string;
deploymentConfig?: DeploymentConfig;
verificationConfig?: VerificationConfig;
}
interface FacetDeployment {
name: string;
address?: string; // If already deployed
constructorArgs?: any[];
functionSelectors?: string[];
}
Returns:
interface DeployDiamondResponse {
success: boolean;
diamondAddress?: string;
facetAddresses?: { [facetName: string]: string };
transactionHashes?: string[];
totalGasUsed?: string;
deploymentCost?: string;
message: string;
}
deployProject()¶
Deploys a complete project infrastructure including all contracts and configurations.
Parameters:
interface DeployProjectRequest {
projectId: string;
network: string;
deploymentTemplate: string;
configuration: ProjectConfiguration;
deploymentOptions?: {
skipVerification?: boolean;
dryRun?: boolean;
parallelDeployment?: boolean;
rollbackOnFailure?: boolean;
};
}
interface ProjectConfiguration {
contracts: ContractConfig[];
diamonds: DiamondConfig[];
initializations: InitializationStep[];
permissions: PermissionConfig[];
}
Returns:
interface DeployProjectResponse {
success: boolean;
deploymentId: string;
deployedContracts?: { [name: string]: string };
deployedDiamonds?: { [name: string]: string };
totalGasUsed?: string;
deploymentCost?: string;
deploymentTime?: number;
message: string;
}
getDeploymentStatus()¶
Retrieves the status of an ongoing or completed deployment.
Parameters:
interface DeploymentStatusRequest {
deploymentId: string;
}
Returns:
interface DeploymentStatusResponse {
success: boolean;
deployment?: {
id: string;
status: 'pending' | 'deploying' | 'completed' | 'failed' | 'rolled_back';
network: string;
progress: number;
currentStep?: string;
deployedContracts: { [name: string]: string };
errors?: string[];
startedAt: Date;
completedAt?: Date;
gasUsed: string;
cost: string;
};
message: string;
}
rollbackDeployment()¶
Rolls back a failed or problematic deployment.
Parameters:
interface RollbackDeploymentRequest {
deploymentId: string;
reason: string;
preserveData?: boolean;
}
Returns:
interface RollbackDeploymentResponse {
success: boolean;
rollbackTransactionHashes?: string[];
message: string;
}
Implementation Example¶
Smart Contract Deployment¶
Parse.Cloud.define('deployContract', async (request) => {
const { contractName, network, constructorArgs, deploymentConfig, verificationConfig, metadata } = request.params;
const user = request.user;
if (!user) {
throw new Error('Authentication required');
}
try {
// Validate deployment permissions
const hasPermission = await checkDeploymentPermissions(user.id, network);
if (!hasPermission) {
throw new Error('Insufficient permissions for deployment');
}
// Validate network
if (!isSupportedNetwork(network)) {
throw new Error('Unsupported network');
}
// Get contract artifacts
const contractArtifacts = await getContractArtifacts(contractName);
if (!contractArtifacts) {
throw new Error('Contract artifacts not found');
}
// Validate constructor arguments
if (constructorArgs) {
await validateConstructorArgs(contractArtifacts.abi, constructorArgs);
}
// Create deployment record
const deployment = new Parse.Object('Deployment');
deployment.set('type', 'contract');
deployment.set('contractName', contractName);
deployment.set('network', network);
deployment.set('deployer', user);
deployment.set('status', 'pending');
deployment.set('constructorArgs', constructorArgs);
deployment.set('deploymentConfig', deploymentConfig);
deployment.set('metadata', metadata);
deployment.set('createdAt', new Date());
await deployment.save();
// Get network configuration
const networkConfig = await getNetworkConfig(network);
const provider = getProvider(networkConfig);
const wallet = getDeploymentWallet(networkConfig);
// Estimate gas
const gasEstimate = await estimateDeploymentGas(
contractArtifacts,
constructorArgs,
provider
);
// Configure deployment transaction
const deploymentTx = {
gasLimit: deploymentConfig?.gasLimit || gasEstimate.mul(120).div(100), // 20% buffer
gasPrice: deploymentConfig?.gasPrice || await provider.getGasPrice(),
...networkConfig.deploymentDefaults
};
deployment.set('estimatedGas', gasEstimate.toString());
deployment.set('gasPrice', deploymentTx.gasPrice.toString());
deployment.set('status', 'deploying');
await deployment.save();
// Deploy contract
const contractFactory = new ethers.ContractFactory(
contractArtifacts.abi,
contractArtifacts.bytecode,
wallet
);
const contract = await contractFactory.deploy(...(constructorArgs || []), deploymentTx);
deployment.set('transactionHash', contract.deployTransaction.hash);
await deployment.save();
// Wait for deployment confirmation
const confirmations = deploymentConfig?.confirmations || 3;
const receipt = await contract.deployTransaction.wait(confirmations);
deployment.set('contractAddress', contract.address);
deployment.set('blockNumber', receipt.blockNumber);
deployment.set('gasUsed', receipt.gasUsed.toString());
deployment.set('deploymentCost', receipt.gasUsed.mul(deploymentTx.gasPrice).toString());
deployment.set('status', 'deployed');
deployment.set('deployedAt', new Date());
await deployment.save();
// Verify contract if requested
let verificationStatus = 'not_requested';
if (verificationConfig?.verify) {
try {
const verificationResult = await verifyContract({
contractAddress: contract.address,
contractName,
network,
constructorArgs,
apiKey: verificationConfig.apiKey
});
verificationStatus = verificationResult.success ? 'verified' : 'failed';
deployment.set('verificationStatus', verificationStatus);
deployment.set('verificationResult', verificationResult);
await deployment.save();
} catch (verificationError) {
console.error('Contract verification failed:', verificationError);
verificationStatus = 'failed';
}
}
// Update project if specified
if (metadata?.projectId) {
await updateProjectDeployment(metadata.projectId, {
contractName,
contractAddress: contract.address,
network,
deploymentId: deployment.id
});
}
// Send deployment notification
await sendDeploymentNotification(deployment.id, 'completed');
return {
success: true,
contractAddress: contract.address,
transactionHash: contract.deployTransaction.hash,
gasUsed: receipt.gasUsed.toString(),
deploymentCost: receipt.gasUsed.mul(deploymentTx.gasPrice).toString(),
verificationStatus,
message: 'Contract deployed successfully'
};
} catch (error) {
console.error('Contract deployment error:', error);
// Update deployment record with error
const deployment = await new Parse.Query('Deployment')
.equalTo('contractName', contractName)
.equalTo('deployer', user)
.equalTo('status', 'deploying')
.first({ useMasterKey: true });
if (deployment) {
deployment.set('status', 'failed');
deployment.set('errorMessage', error.message);
deployment.set('failedAt', new Date());
await deployment.save();
}
return {
success: false,
message: error.message || 'Contract deployment failed'
};
}
});
Diamond Deployment¶
Parse.Cloud.define('deployDiamond', async (request) => {
const { network, diamondName, facets, initContract, initData, owner, deploymentConfig, verificationConfig } = request.params;
const user = request.user;
if (!user) {
throw new Error('Authentication required');
}
try {
// Validate deployment permissions
const hasPermission = await checkDeploymentPermissions(user.id, network);
if (!hasPermission) {
throw new Error('Insufficient permissions for deployment');
}
// Create deployment record
const deployment = new Parse.Object('Deployment');
deployment.set('type', 'diamond');
deployment.set('diamondName', diamondName);
deployment.set('network', network);
deployment.set('deployer', user);
deployment.set('status', 'pending');
deployment.set('facets', facets);
deployment.set('createdAt', new Date());
await deployment.save();
const deployedContracts: { [name: string]: string } = {};
const transactionHashes: string[] = [];
let totalGasUsed = ethers.BigNumber.from(0);
// Get network configuration
const networkConfig = await getNetworkConfig(network);
const provider = getProvider(networkConfig);
const wallet = getDeploymentWallet(networkConfig);
deployment.set('status', 'deploying');
await deployment.save();
// Deploy core diamond contracts first
const diamondCutFacet = await deployCoreFacet('DiamondCutFacet', wallet, deploymentConfig);
deployedContracts['DiamondCutFacet'] = diamondCutFacet.address;
transactionHashes.push(diamondCutFacet.deployTransaction.hash);
totalGasUsed = totalGasUsed.add(await diamondCutFacet.deployTransaction.wait().then(r => r.gasUsed));
const diamondLoupeFacet = await deployCoreFacet('DiamondLoupeFacet', wallet, deploymentConfig);
deployedContracts['DiamondLoupeFacet'] = diamondLoupeFacet.address;
transactionHashes.push(diamondLoupeFacet.deployTransaction.hash);
totalGasUsed = totalGasUsed.add(await diamondLoupeFacet.deployTransaction.wait().then(r => r.gasUsed));
const ownershipFacet = await deployCoreFacet('OwnershipFacet', wallet, deploymentConfig);
deployedContracts['OwnershipFacet'] = ownershipFacet.address;
transactionHashes.push(ownershipFacet.deployTransaction.hash);
totalGasUsed = totalGasUsed.add(await ownershipFacet.deployTransaction.wait().then(r => r.gasUsed));
// Deploy custom facets
for (const facet of facets) {
if (!facet.address) {
const facetContract = await deployFacet(facet, wallet, deploymentConfig);
deployedContracts[facet.name] = facetContract.address;
transactionHashes.push(facetContract.deployTransaction.hash);
totalGasUsed = totalGasUsed.add(await facetContract.deployTransaction.wait().then(r => r.gasUsed));
} else {
deployedContracts[facet.name] = facet.address;
}
}
// Prepare diamond cut for initialization
const diamondCut = prepareDiamondCut([
{
facetAddress: diamondCutFacet.address,
action: 0, // Add
functionSelectors: getDiamondCutSelectors()
},
{
facetAddress: diamondLoupeFacet.address,
action: 0, // Add
functionSelectors: getDiamondLoupeSelectors()
},
{
facetAddress: ownershipFacet.address,
action: 0, // Add
functionSelectors: getOwnershipSelectors()
},
...facets.map(facet => ({
facetAddress: deployedContracts[facet.name],
action: 0, // Add
functionSelectors: facet.functionSelectors || getFacetSelectors(facet.name)
}))
]);
// Deploy diamond
const diamondFactory = await getDiamondFactory(wallet);
const diamondTx = await diamondFactory.deployDiamond(
owner || wallet.address,
diamondCut,
initContract || ethers.constants.AddressZero,
initData || '0x',
deploymentConfig || {}
);
const diamondReceipt = await diamondTx.wait();
const diamondAddress = await getDiamondAddressFromReceipt(diamondReceipt);
deployedContracts['Diamond'] = diamondAddress;
transactionHashes.push(diamondTx.hash);
totalGasUsed = totalGasUsed.add(diamondReceipt.gasUsed);
// Update deployment record
deployment.set('diamondAddress', diamondAddress);
deployment.set('deployedContracts', deployedContracts);
deployment.set('transactionHashes', transactionHashes);
deployment.set('totalGasUsed', totalGasUsed.toString());
deployment.set('status', 'deployed');
deployment.set('deployedAt', new Date());
await deployment.save();
// Verify contracts if requested
if (verificationConfig?.verify) {
await verifyDiamondContracts(deployedContracts, network, verificationConfig);
}
// Send deployment notification
await sendDeploymentNotification(deployment.id, 'completed');
return {
success: true,
diamondAddress,
facetAddresses: deployedContracts,
transactionHashes,
totalGasUsed: totalGasUsed.toString(),
deploymentCost: totalGasUsed.mul(await provider.getGasPrice()).toString(),
message: 'Diamond deployed successfully'
};
} catch (error) {
console.error('Diamond deployment error:', error);
// Update deployment record with error
const deployment = await new Parse.Query('Deployment')
.equalTo('diamondName', diamondName)
.equalTo('deployer', user)
.equalTo('status', 'deploying')
.first({ useMasterKey: true });
if (deployment) {
deployment.set('status', 'failed');
deployment.set('errorMessage', error.message);
deployment.set('failedAt', new Date());
await deployment.save();
}
return {
success: false,
message: error.message || 'Diamond deployment failed'
};
}
});
Project Deployment¶
Parse.Cloud.define('deployProject', async (request) => {
const { projectId, network, deploymentTemplate, configuration, deploymentOptions } = request.params;
const user = request.user;
if (!user) {
throw new Error('Authentication required');
}
try {
// Get project
const project = await new Parse.Query('Project')
.equalTo('objectId', projectId)
.first({ useMasterKey: true });
if (!project) {
throw new Error('Project not found');
}
// Check project access
const hasAccess = await checkProjectAccess(projectId, user.id);
if (!hasAccess) {
throw new Error('Access denied');
}
// Create deployment record
const deploymentId = generateDeploymentId();
const deployment = new Parse.Object('ProjectDeployment');
deployment.set('deploymentId', deploymentId);
deployment.set('project', project);
deployment.set('network', network);
deployment.set('template', deploymentTemplate);
deployment.set('configuration', configuration);
deployment.set('deployer', user);
deployment.set('status', 'pending');
deployment.set('createdAt', new Date());
await deployment.save();
// Validate deployment configuration
await validateProjectConfiguration(configuration, deploymentTemplate);
// Perform dry run if requested
if (deploymentOptions?.dryRun) {
const dryRunResult = await performDryRun(configuration, network);
return {
success: true,
deploymentId,
dryRunResult,
message: 'Dry run completed successfully'
};
}
deployment.set('status', 'deploying');
await deployment.save();
const deployedContracts: { [name: string]: string } = {};
const deployedDiamonds: { [name: string]: string } = {};
let totalGasUsed = ethers.BigNumber.from(0);
const startTime = Date.now();
try {
// Deploy contracts in dependency order
for (const contractConfig of configuration.contracts) {
const contractResult = await Parse.Cloud.run('deployContract', {
contractName: contractConfig.name,
network,
constructorArgs: contractConfig.constructorArgs,
deploymentConfig: contractConfig.deploymentConfig,
verificationConfig: { verify: !deploymentOptions?.skipVerification }
});
if (!contractResult.success) {
throw new Error(`Failed to deploy ${contractConfig.name}: ${contractResult.message}`);
}
deployedContracts[contractConfig.name] = contractResult.contractAddress;
totalGasUsed = totalGasUsed.add(contractResult.gasUsed || '0');
// Update deployment progress
deployment.set('currentStep', `Deployed ${contractConfig.name}`);
deployment.set('deployedContracts', deployedContracts);
await deployment.save();
}
// Deploy diamonds
for (const diamondConfig of configuration.diamonds) {
const diamondResult = await Parse.Cloud.run('deployDiamond', {
network,
diamondName: diamondConfig.name,
facets: diamondConfig.facets,
initContract: diamondConfig.initContract,
initData: diamondConfig.initData,
owner: diamondConfig.owner,
verificationConfig: { verify: !deploymentOptions?.skipVerification }
});
if (!diamondResult.success) {
throw new Error(`Failed to deploy ${diamondConfig.name}: ${diamondResult.message}`);
}
deployedDiamonds[diamondConfig.name] = diamondResult.diamondAddress;
totalGasUsed = totalGasUsed.add(diamondResult.totalGasUsed || '0');
// Update deployment progress
deployment.set('currentStep', `Deployed ${diamondConfig.name}`);
deployment.set('deployedDiamonds', deployedDiamonds);
await deployment.save();
}
// Execute initialization steps
for (const initStep of configuration.initializations) {
await executeInitializationStep(initStep, deployedContracts, deployedDiamonds, network);
deployment.set('currentStep', `Executed ${initStep.name}`);
await deployment.save();
}
// Configure permissions
for (const permissionConfig of configuration.permissions) {
await configurePermissions(permissionConfig, deployedContracts, deployedDiamonds, network);
deployment.set('currentStep', `Configured ${permissionConfig.name}`);
await deployment.save();
}
const deploymentTime = Date.now() - startTime;
// Update deployment record
deployment.set('status', 'completed');
deployment.set('deployedContracts', deployedContracts);
deployment.set('deployedDiamonds', deployedDiamonds);
deployment.set('totalGasUsed', totalGasUsed.toString());
deployment.set('deploymentTime', deploymentTime);
deployment.set('completedAt', new Date());
await deployment.save();
// Update project with deployment information
project.set('deployments', [...(project.get('deployments') || []), {
deploymentId,
network,
status: 'completed',
deployedAt: new Date(),
contracts: deployedContracts,
diamonds: deployedDiamonds
}]);
await project.save();
// Send deployment notification
await sendDeploymentNotification(deploymentId, 'completed');
return {
success: true,
deploymentId,
deployedContracts,
deployedDiamonds,
totalGasUsed: totalGasUsed.toString(),
deploymentCost: totalGasUsed.mul(await getNetworkGasPrice(network)).toString(),
deploymentTime,
message: 'Project deployed successfully'
};
} catch (deploymentError) {
// Handle deployment failure
if (deploymentOptions?.rollbackOnFailure) {
await rollbackProjectDeployment(deploymentId, deployedContracts, deployedDiamonds);
}
deployment.set('status', 'failed');
deployment.set('errorMessage', deploymentError.message);
deployment.set('failedAt', new Date());
await deployment.save();
throw deploymentError;
}
} catch (error) {
console.error('Project deployment error:', error);
return {
success: false,
message: error.message || 'Project deployment failed'
};
}
});
Deployment Templates¶
NFT Collection Template¶
const nftCollectionTemplate = {
name: 'NFT Collection',
description: 'Complete NFT collection with marketplace',
contracts: [
{
name: 'CollectionToken',
type: 'ERC721',
constructorArgs: ['{{tokenName}}', '{{tokenSymbol}}', '{{baseURI}}'],
deploymentConfig: {
gasLimit: '3000000'
}
}
],
diamonds: [
{
name: 'CollectionDiamond',
facets: [
{ name: 'ERC721Facet' },
{ name: 'MarketplaceFacet' },
{ name: 'MetadataFacet' },
{ name: 'OwnershipFacet' }
],
initContract: 'CollectionInit',
initData: '{{initData}}'
}
],
initializations: [
{
name: 'SetupMarketplace',
contract: 'CollectionDiamond',
function: 'setupMarketplace',
args: ['{{marketplaceFee}}', '{{feeRecipient}}']
}
],
permissions: [
{
name: 'MinterRole',
contract: 'CollectionDiamond',
role: 'MINTER_ROLE',
accounts: ['{{owner}}']
}
]
};
DeFi Protocol Template¶
const defiProtocolTemplate = {
name: 'DeFi Protocol',
description: 'DeFi protocol with staking and governance',
contracts: [
{
name: 'GovernanceToken',
type: 'ERC20',
constructorArgs: ['{{tokenName}}', '{{tokenSymbol}}', '{{totalSupply}}']
},
{
name: 'Timelock',
type: 'TimelockController',
constructorArgs: ['{{timelockDelay}}', ['{{admin}}'], ['{{admin}}'], '{{admin}}']
}
],
diamonds: [
{
name: 'ProtocolDiamond',
facets: [
{ name: 'StakingFacet' },
{ name: 'GovernanceFacet' },
{ name: 'FeeDistributorFacet' },
{ name: 'OwnershipFacet' }
]
}
],
initializations: [
{
name: 'SetupStaking',
contract: 'ProtocolDiamond',
function: 'setupStaking',
args: ['{{stakingToken}}', '{{rewardRate}}']
}
]
};
Verification and Monitoring¶
Contract Verification¶
async function verifyContract(params: {
contractAddress: string;
contractName: string;
network: string;
constructorArgs?: any[];
apiKey?: string;
}): Promise<{ success: boolean; message: string }> {
try {
const { contractAddress, contractName, network, constructorArgs, apiKey } = params;
// Get network verification configuration
const verificationConfig = getVerificationConfig(network);
const explorerApiKey = apiKey || verificationConfig.defaultApiKey;
if (!explorerApiKey) {
throw new Error('API key required for verification');
}
// Get contract source code and metadata
const contractArtifacts = await getContractArtifacts(contractName);
const sourceCode = await getContractSourceCode(contractName);
// Prepare verification request
const verificationRequest = {
apikey: explorerApiKey,
module: 'contract',
action: 'verifysourcecode',
contractaddress: contractAddress,
sourceCode: sourceCode,
codeformat: 'solidity-single-file',
contractname: contractName,
compilerversion: contractArtifacts.compiler.version,
optimizationUsed: contractArtifacts.compiler.optimizer.enabled ? 1 : 0,
runs: contractArtifacts.compiler.optimizer.runs,
constructorArguements: constructorArgs ? encodeConstructorArgs(constructorArgs) : ''
};
// Submit verification request
const response = await submitVerificationRequest(verificationConfig.apiUrl, verificationRequest);
if (response.status !== '1') {
throw new Error(`Verification submission failed: ${response.result}`);
}
// Poll for verification result
const verificationResult = await pollVerificationResult(
verificationConfig.apiUrl,
explorerApiKey,
response.result
);
return {
success: verificationResult.status === '1',
message: verificationResult.result
};
} catch (error) {
console.error('Contract verification error:', error);
return {
success: false,
message: error.message || 'Verification failed'
};
}
}
Deployment Monitoring¶
```typescript Parse.Cloud.define('getDeploymentStatus', async (request) => { const { deploymentId } = request.params; const user = request.user;
if (!user) { throw new Error('Authentication required'); }
try { // Get deployment record const deployment = await new Parse.Query('Deployment') .equalTo('deploymentId', deploymentId) .first({ useMasterKey: true });
if (!deployment) {
throw new Error('Deployment not found');
}
// Check access permissions
if (deployment.get('deployer').id !== user.id) {
const hasAccess = await checkDeploymentAccess(deploymentId, user.id);
if (!hasAccess) {
throw new Error('Access denied');
}
}
// Calculate progress
const progress = calculateDeploymentProgress(deployment);
return {
success: true,
deployment: {
id: deployment.get('deploymentId'),
status: deployment.get('status'),
network: deployment.get('network'),
progress,
currentStep: deployment.get('currentStep'),
deployedContracts: deployment.get('deployedContracts') || {},
errors: deployment.get('errors') || [],
startedAt: deployment.get('createdAt'),
completedAt: deployment.get('completedAt