UNPKG

@vechain/sdk-network

Version:

This module serves as the standard interface connecting decentralized applications (dApps) and users to the VeChainThor blockchain

184 lines (164 loc) 6.71 kB
import { Clause, HexUInt, Transaction, type DeployParams, type TransactionClause } from '@vechain/sdk-core'; import { CannotFindTransaction, ContractDeploymentFailed } from '@vechain/sdk-errors'; import { type Abi } from 'abitype'; import { signerUtils, type VeChainSigner } from '../../../signer'; import { type SendTransactionResult } from '../../transactions/types'; import type { ContractTransactionOptions } from '../types'; import { Contract } from './contract'; import { type ContractsModule } from '../contracts-module'; /** * A factory class for deploying smart contracts to a blockchain using a ThorClient. */ class ContractFactory<TAbi extends Abi> { /** * The ABI (Application Binary Interface) of the contract. */ private readonly abi: Abi; /** * The bytecode of the smart contract. */ private readonly bytecode: string; /** * The signer used for signing transactions. */ private readonly signer: VeChainSigner; /** * The result of the deployment transaction, undefined until a deployment is started. */ private deployTransaction: SendTransactionResult | undefined; readonly contractsModule: ContractsModule; /** * Initializes a new instance of the `ContractFactory` class. * @param abi The Application Binary Interface (ABI) of the contract, which defines the contract's methods and events. * @param bytecode The compiled bytecode of the contract, representing the contract's executable code. * @param signer The signer used for signing transactions during contract deployment, ensuring the deployer's identity. * @param contractsModule An instance of the module to interact with the blockchain. */ constructor( abi: Abi, bytecode: string, signer: VeChainSigner, contractsModule: ContractsModule ) { this.abi = abi; this.bytecode = bytecode; this.signer = signer; this.contractsModule = contractsModule; } /** * Initiates the deployment of a smart contract. * * This method performs several steps to deploy a smart contract: * 1. Builds a transaction clause for deploying the contract. * 2. Estimates the gas cost required for the transaction. * 3. Constructs the transaction body with the estimated gas cost. * 4. Signs the transaction using the provided signer. * 5. Sends the signed transaction to the blockchain. * * @param {DeployParams?} deployParams (Optional) parameters for contract deployment. * @param {ContractTransactionOptions?} options (Optional) transaction options, such as gas limit. * @returns {Promise<ContractFactory>} A promise that resolves to the instance of `ContractFactory`, * allowing for fluent chaining of further actions or queries. * @throws {Error} Throws an error if any step in the deployment process fails. */ public async startDeployment( deployParams?: DeployParams, options?: ContractTransactionOptions ): Promise<ContractFactory<TAbi>> { // Build a transaction for deploying the smart contract const deployContractClause = Clause.deployContract( HexUInt.of(this.bytecode), deployParams ) as TransactionClause; // Estimate the gas cost of the transaction const gasResult = await this.contractsModule.transactionsModule.estimateGas( [deployContractClause], await this.signer.getAddress(), options // Pass through EstimateGasOptions including gasPadding ); const txBody = await this.contractsModule.transactionsModule.buildTransactionBody( [deployContractClause], gasResult.totalGas, options ); // Sign the transaction const signedTx = await this.signer.signTransaction( signerUtils.transactionBodyToTransactionRequestInput( txBody, await this.signer.getAddress() ) ); // Send the signed transaction to the blockchain this.deployTransaction = await this.contractsModule.transactionsModule.sendTransaction( Transaction.decode(HexUInt.of(signedTx.slice(2)).bytes, true) ); return this; } /** * Waits for the completion of a contract deployment transaction. * * This method checks for the presence of a deployed transaction result and then * waits for the transaction to be processed. Upon successful processing, it * constructs and returns a new `Contract` instance based on the transaction receipt. * * @returns {Promise<Contract>} A promise that resolves to a `Contract` instance * once the deployment transaction is completed. * @throws {CannotFindTransaction, ContractDeploymentFailed} */ public async waitForDeployment(): Promise<Contract<TAbi>> { // Check if the deploy transaction result is available if (this.deployTransaction?.id === undefined) { throw new CannotFindTransaction( 'ContractFactory.waitForDeployment()', 'Cannot find a contract deployment transaction', { networkUrl: this.contractsModule.transactionsModule.blocksModule .httpClient.baseURL } ); } // Wait for the transaction to be processed const transactionReceipt = await this.deployTransaction.wait(); // Ensure that the transaction receipt is valid if ( transactionReceipt?.outputs[0]?.contractAddress === null || transactionReceipt?.outputs[0]?.contractAddress === undefined ) { throw new ContractDeploymentFailed( 'ContractFactory.waitForDeployment()', 'Contract deployment failed.', { deployTransaction: this.deployTransaction } ); } // Construct and return a new Contract instance return new Contract<TAbi>( transactionReceipt?.outputs[0].contractAddress, this.abi, this.contractsModule, this.signer, transactionReceipt ); } /** * Returns the deploy transaction result, if available. */ public getDeployTransaction(): SendTransactionResult | undefined { return this.deployTransaction; } } export { ContractFactory };