Contract Cloud Functions¶
Overview¶
The contracts.ts module provides Parse Server cloud functions for interacting with smart contracts on various blockchain networks. These functions enable secure contract operations, diamond facet management, and flexible contract interaction patterns through a unified API.
Module Details¶
- File:
src/cloud-functions/contracts.ts - Framework: Parse Server Cloud Functions
- Language: TypeScript
- Dependencies: Contract utilities, Diamond utilities
Key Features¶
🔹 Diamond Contract Management¶
- Add facets to diamond contracts dynamically
- Support for EIP-2535 Diamond Standard operations
- Network-agnostic diamond management
🔹 Generic Contract Interaction¶
- Call state-changing contract methods
- Query read-only contract methods
- Support for custom ABIs and contract addresses
🔹 Smart Contract Loading¶
- Load contract configurations for specific networks
- Batch loading of multiple contracts
- Network-specific contract resolution
🔹 Flexible Parameter Handling¶
- Support for custom private keys
- Optional parameter validation
- Value transfer support for payable functions
Cloud Functions¶
Diamond Management Functions¶
addDiamondFacet¶
Parse.Cloud.define("addDiamondFacet", async (request: any) => {
const { networkId, diamondAddress, privateKey, facetName } = request.params;
// Implementation details...
});
Purpose: Adds a new facet to an existing diamond contract.
Parameters:
- networkId (string): Target blockchain network identifier
- diamondAddress (string): Address of the diamond contract
- privateKey (string, optional): Private key for transaction signing
- facetName (string): Name of the facet to add
Returns: Transaction result with facet addition details
Access Control: Requires valid private key with diamond owner permissions
Example Usage:
// Add marketplace facet to diamond
const result = await Parse.Cloud.run("addDiamondFacet", {
networkId: "baseSepolia",
diamondAddress: "0x1234567890123456789012345678901234567890",
facetName: "MarketplaceFacet"
});
console.log("Facet added:", result.transactionHash);
Process: 1. Validates network and diamond address 2. Resolves private key (uses default if not provided) 3. Loads facet contract for the specified network 4. Executes diamond cut operation to add facet 5. Returns transaction details
Error Conditions: - Invalid network ID - Diamond contract not found - Insufficient permissions - Facet already exists - Transaction failure
Generic Contract Functions¶
callMethod¶
Parse.Cloud.define("callMethod", async (request: any) => {
const { methodName, params, privateKey } = request.params;
// Implementation details...
});
Purpose: Calls a state-changing method on a pre-configured contract.
Parameters:
- methodName (string): Name of the contract method to call
- params (array): Array of parameters for the method
- privateKey (string, optional): Private key for transaction signing
Returns: Transaction result with method execution details
Example Usage:
// Call mint function on NFT contract
const result = await Parse.Cloud.run("callMethod", {
methodName: "mint",
params: ["0xRecipientAddress", "tokenURI"],
privateKey: "0x..." // Optional
});
console.log("Mint transaction:", result.transactionHash);
Use Cases: - Token minting operations - Marketplace listing creation - Identity claim management - Trade deal operations
viewMethod¶
Parse.Cloud.define("viewMethod", async (request: any) => {
const { methodName, params } = request.params;
// Implementation details...
});
Purpose: Calls a read-only method on a pre-configured contract.
Parameters:
- methodName (string): Name of the view method to call
- params (array): Array of parameters for the method
Returns: Method return value(s)
Example Usage:
// Get token balance
const balance = await Parse.Cloud.run("viewMethod", {
methodName: "balanceOf",
params: ["0xUserAddress"]
});
console.log("Token balance:", balance);
Use Cases: - Balance queries - Token metadata retrieval - Contract state inspection - Verification checks
Custom Contract Functions¶
callContractMethod¶
Parse.Cloud.define("callContractMethod", async (request: any) => {
const { providerUrl, privateKey, abi, address, functionName, params, value } = request.params;
// Implementation details...
});
Purpose: Calls a state-changing method on any contract with custom ABI.
Parameters:
- providerUrl (string): Blockchain RPC endpoint URL
- privateKey (string): Private key for transaction signing
- abi (array): Contract ABI definition
- address (string): Contract address
- functionName (string): Name of the function to call
- params (array): Function parameters
- value (string, optional): ETH value to send with transaction
Returns: Transaction result with execution details
Example Usage:
// Call custom contract function
const result = await Parse.Cloud.run("callContractMethod", {
providerUrl: "https://sepolia.base.org",
privateKey: "0x...",
abi: contractABI,
address: "0xContractAddress",
functionName: "customFunction",
params: ["param1", "param2"],
value: "1000000000000000000" // 1 ETH in wei
});
Use Cases: - Integration with external contracts - Custom protocol interactions - Multi-network operations - Advanced contract testing
viewContractMethod¶
Parse.Cloud.define("viewContractMethod", async (request: any) => {
const { providerUrl, abi, address, functionName, params } = request.params;
// Implementation details...
});
Purpose: Calls a read-only method on any contract with custom ABI.
Parameters:
- providerUrl (string): Blockchain RPC endpoint URL
- abi (array): Contract ABI definition
- address (string): Contract address
- functionName (string): Name of the view function to call
- params (array): Function parameters
Returns: Function return value(s)
Example Usage:
// Query custom contract state
const result = await Parse.Cloud.run("viewContractMethod", {
providerUrl: "https://sepolia.base.org",
abi: contractABI,
address: "0xContractAddress",
functionName: "getCustomData",
params: ["queryParam"]
});
console.log("Custom data:", result);
Use Cases: - External contract queries - Cross-protocol data retrieval - Multi-network state inspection - Integration testing
Contract Loading Functions¶
loadSmartContractForNetwork¶
Parse.Cloud.define("loadSmartContractForNetwork", async (request: any) => {
const { smartContractName, networkId } = request.params;
// Implementation details...
});
Purpose: Loads contract configuration for a specific contract on a specific network.
Parameters:
- smartContractName (string): Name of the smart contract
- networkId (string): Target network identifier
Returns: Contract configuration including address, ABI, and metadata
Example Usage:
// Load diamond contract for Base Sepolia
const contract = await Parse.Cloud.run("loadSmartContractForNetwork", {
smartContractName: "Diamond",
networkId: "baseSepolia"
});
console.log("Diamond address:", contract.address);
console.log("Diamond ABI:", contract.abi);
Use Cases: - Dynamic contract loading - Network-specific configurations - Contract address resolution - ABI retrieval for frontend
loadSmartContractsForNetwork¶
Parse.Cloud.define("loadSmartContractsForNetwork", async (request: any) => {
const { networkId } = request.params;
// Implementation details...
});
Purpose: Loads all available contract configurations for a specific network.
Parameters:
- networkId (string): Target network identifier
Returns: Object containing all contract configurations for the network
Example Usage:
// Load all contracts for Base Sepolia
const contracts = await Parse.Cloud.run("loadSmartContractsForNetwork", {
networkId: "baseSepolia"
});
console.log("Available contracts:", Object.keys(contracts));
console.log("Diamond address:", contracts.Diamond.address);
console.log("USDC address:", contracts.USDC.address);
Use Cases: - Bulk contract loading - Network initialization - Frontend contract registry - Development environment setup
Integration Examples¶
Diamond Facet Management¶
// Complete diamond facet management workflow
class DiamondManager {
async addFacetToDiamond(networkId, diamondAddress, facetName) {
try {
// Add the facet
const result = await Parse.Cloud.run("addDiamondFacet", {
networkId,
diamondAddress,
facetName
});
console.log(`${facetName} added to diamond:`, result.transactionHash);
// Verify facet was added
const diamond = await Parse.Cloud.run("loadSmartContractForNetwork", {
smartContractName: "Diamond",
networkId
});
// Check if facet functions are available
const facetFunctions = await Parse.Cloud.run("viewContractMethod", {
providerUrl: this.getProviderUrl(networkId),
abi: diamond.abi,
address: diamondAddress,
functionName: "facetFunctionSelectors",
params: [facetName]
});
console.log(`${facetName} functions:`, facetFunctions);
return result;
} catch (error) {
console.error("Failed to add facet:", error);
throw error;
}
}
}
Multi-Network Contract Interaction¶
// Interact with contracts across multiple networks
class MultiNetworkContractManager {
async deployToAllNetworks(contractName, constructorParams) {
const networks = ["baseSepolia", "optimismSepolia", "sepolia"];
const deployments = {};
for (const networkId of networks) {
try {
// Load network-specific configuration
const contracts = await Parse.Cloud.run("loadSmartContractsForNetwork", {
networkId
});
// Deploy contract
const result = await Parse.Cloud.run("callContractMethod", {
providerUrl: this.getProviderUrl(networkId),
privateKey: this.getPrivateKey(networkId),
abi: contracts[contractName].abi,
address: contracts.ContractFactory.address,
functionName: "deploy",
params: constructorParams
});
deployments[networkId] = result;
console.log(`Deployed to ${networkId}:`, result.contractAddress);
} catch (error) {
console.error(`Failed to deploy to ${networkId}:`, error);
deployments[networkId] = { error: error.message };
}
}
return deployments;
}
}
Custom Contract Integration¶
// Integrate with external protocols
class ExternalProtocolIntegration {
async interactWithUniswap(tokenA, tokenB, amount) {
const uniswapABI = [
// Uniswap V3 Router ABI
"function exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160)) external payable returns (uint256)"
];
const swapParams = {
tokenIn: tokenA,
tokenOut: tokenB,
fee: 3000, // 0.3%
recipient: this.userAddress,
deadline: Math.floor(Date.now() / 1000) + 3600,
amountIn: amount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
};
const result = await Parse.Cloud.run("callContractMethod", {
providerUrl: "https://mainnet.base.org",
privateKey: this.privateKey,
abi: uniswapABI,
address: "0xUniswapV3RouterAddress",
functionName: "exactInputSingle",
params: [swapParams]
});
return result;
}
}
Contract State Monitoring¶
// Monitor contract state changes
class ContractMonitor {
async monitorDiamondState(networkId, diamondAddress) {
const contracts = await Parse.Cloud.run("loadSmartContractsForNetwork", {
networkId
});
const diamond = contracts.Diamond;
// Get current facets
const facets = await Parse.Cloud.run("viewContractMethod", {
providerUrl: this.getProviderUrl(networkId),
abi: diamond.abi,
address: diamondAddress,
functionName: "facetFunctionSelectors",
params: [facetName]
});
console.log("Current facets:", facets);
// Monitor for changes
setInterval(async () => {
const currentFacets = await Parse.Cloud.run("viewContractMethod", {
providerUrl: this.getProviderUrl(networkId),
abi: diamond.abi,
address: diamondAddress,
functionName: "facetFunctionSelectors",
params: [facetName]
});
if (JSON.stringify(currentFacets) !== JSON.stringify(facets)) {
console.log("Diamond facets changed:", currentFacets);
this.handleFacetChange(currentFacets);
}
}, 30000); // Check every 30 seconds
}
}
Security Considerations¶
Private Key Management¶
- Private keys are handled securely through utility functions
- Default private key fallback for authorized operations
- No private key logging or exposure in responses
Access Control¶
- Functions validate network permissions
- Contract owner verification for sensitive operations
- Transaction signing validation
Input Validation¶
- Parameter validation for all contract interactions
- ABI validation for custom contract calls
- Network ID validation against supported networks
Error Handling¶
Network Errors¶
- Invalid network ID validation
- RPC endpoint connectivity checks
- Transaction timeout handling
Contract Errors¶
- Invalid contract address or ABI
- Non-existent method calls
- Reverted transactions
Transaction Errors¶
- Insufficient gas
- Failed nonce management
- Pending transaction conflicts
Best Practices¶
Caching¶
- Cache frequently accessed contract ABIs and addresses to reduce RPC calls.
- Implement a cache invalidation strategy for contract upgrades.
Batch Operations¶
- Use batching for multiple read-only calls to an RPC node to improve performance.
- Consider batched writes for efficient database updates.
Testing¶
Unit Tests¶
- Test individual cloud functions in isolation.
- Mock blockchain interactions and external dependencies.
- Cover all success and error scenarios.
Integration Tests¶
- Test end-to-end contract interaction flows.
- Verify correct state changes on the blockchain and in Parse Server.
- Use a dedicated test environment with deployed contracts.
Load Testing¶
- Simulate high volumes of contract calls to assess performance and identify bottlenecks.
- Measure transaction throughput and latency.