UNPKG

@hashgraph/sdk

Version:
452 lines (412 loc) 15.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _FileCreateTransaction = _interopRequireDefault(require("../file/FileCreateTransaction.cjs")); var _FileAppendTransaction = _interopRequireDefault(require("../file/FileAppendTransaction.cjs")); var _FileDeleteTransaction = _interopRequireDefault(require("../file/FileDeleteTransaction.cjs")); var _ContractCreateTransaction = _interopRequireDefault(require("./ContractCreateTransaction.cjs")); var utf8 = _interopRequireWildcard(require("../encoding/utf8.cjs")); var hex = _interopRequireWildcard(require("../encoding/hex.cjs")); var _PublicKey = _interopRequireDefault(require("../PublicKey.cjs")); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // SPDX-License-Identifier: Apache-2.0 /** * @typedef {import("../account/AccountId.js").default} AccountId * @typedef {import("../file/FileId.js").default} FileId * @typedef {import("../Key.js").default} Key * @typedef {import("./ContractFunctionParameters.js").default} ContractFunctionParameters * @typedef {import("../Hbar.js").default} Hbar * @typedef {import("../Duration.js").default} Duration * @typedef {import("../channel/Channel.js").default} Channel * @typedef {import("../channel/MirrorChannel.js").default} MirrorChannel * @typedef {import("../transaction/TransactionId.js").default} TransactionId * @typedef {import("../transaction/TransactionResponse.js").default} TransactionResponse * @typedef {import("../transaction/TransactionReceipt.js").default} TransactionReceipt * @typedef {import("../client/Client.js").ClientOperator} ClientOperator * @typedef {import("../Signer.js").Signer} Signer * @typedef {import("../PrivateKey.js").default} PrivateKey * @typedef {import("../transaction/Transaction.js").default} Transaction */ /** * @typedef {import("bignumber.js").BigNumber} BigNumber * @typedef {import("long")} Long */ /** * A convenience flow that handles the creation of a smart contract on the Hedera network. * This flow abstracts away the complexity of the contract creation process by: * * 1. Creating a file to store the contract bytecode * 2. Uploading the contract bytecode in chunks if necessary * 3. Creating the contract instance using the uploaded bytecode * 4. Cleaning up by deleting the bytecode file (if operator key is available) * * This flow is particularly useful when deploying large contracts that exceed the 2048 byte * limit of a single transaction. */ class ContractCreateFlow { constructor() { /** @type {Uint8Array | null} */ this._bytecode = null; this._contractCreate = new _ContractCreateTransaction.default(); /** * Read `Transaction._signerPublicKeys` * * @internal * @type {Set<string>} */ this._signerPublicKeys = new Set(); /** * Read `Transaction._publicKeys` * * @private * @type {PublicKey[]} */ this._publicKeys = []; /** * Read `Transaction._transactionSigners` * * @private * @type {((message: Uint8Array) => Promise<Uint8Array>)[]} */ this._transactionSigners = []; this._maxChunks = null; } /** * @returns {number | null} */ get maxChunks() { return this._maxChunks; } /** * @param {number} maxChunks * @returns {this} */ setMaxChunks(maxChunks) { this._maxChunks = maxChunks; return this; } /** * @returns {?Uint8Array} */ get bytecode() { return this._bytecode; } /** * @param {string | Uint8Array} bytecode * @returns {this} */ setBytecode(bytecode) { this._bytecode = bytecode instanceof Uint8Array ? bytecode : utf8.encode(bytecode); return this; } /** * @returns {?Key} */ get adminKey() { return this._contractCreate.adminKey; } /** * @param {Key} adminKey * @returns {this} */ setAdminKey(adminKey) { this._contractCreate.setAdminKey(adminKey); return this; } /** * @returns {?Long} */ get gas() { return this._contractCreate.gas; } /** * @param {number | Long} gas * @returns {this} */ setGas(gas) { this._contractCreate.setGas(gas); return this; } /** * @returns {?Hbar} */ get initialBalance() { return this._contractCreate.initialBalance; } /** * Set the initial amount to transfer into this contract. * * @param {number | string | Long | BigNumber | Hbar} initialBalance * @returns {this} */ setInitialBalance(initialBalance) { this._contractCreate.setInitialBalance(initialBalance); return this; } /** * @deprecated * @returns {?AccountId} */ get proxyAccountId() { // eslint-disable-next-line deprecation/deprecation return this._contractCreate.proxyAccountId; } /** * @deprecated * @param {AccountId | string} proxyAccountId * @returns {this} */ setProxyAccountId(proxyAccountId) { // eslint-disable-next-line deprecation/deprecation this._contractCreate.setProxyAccountId(proxyAccountId); return this; } /** * @returns {Duration} */ get autoRenewPeriod() { return this._contractCreate.autoRenewPeriod; } /** * @param {Duration | Long | number} autoRenewPeriod * @returns {this} */ setAutoRenewPeriod(autoRenewPeriod) { this._contractCreate.setAutoRenewPeriod(autoRenewPeriod); return this; } /** * @returns {?Uint8Array} */ get constructorParameters() { return this._contractCreate.constructorParameters; } /** * @param {Uint8Array | ContractFunctionParameters} constructorParameters * @returns {this} */ setConstructorParameters(constructorParameters) { this._contractCreate.setConstructorParameters(constructorParameters); return this; } /** * @returns {?string} */ get contractMemo() { return this._contractCreate.contractMemo; } /** * @param {string} contractMemo * @returns {this} */ setContractMemo(contractMemo) { this._contractCreate.setContractMemo(contractMemo); return this; } /** * @returns {?number} */ get maxAutomaticTokenAssociation() { return this._contractCreate.maxAutomaticTokenAssociations; } /** * @param {number} maxAutomaticTokenAssociation * @returns {this} */ setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociation) { this._contractCreate.setMaxAutomaticTokenAssociations(maxAutomaticTokenAssociation); return this; } /** * @returns {?AccountId} */ get stakedAccountId() { return this._contractCreate.stakedAccountId; } /** * @param {AccountId | string} stakedAccountId * @returns {this} */ setStakedAccountId(stakedAccountId) { this._contractCreate.setStakedAccountId(stakedAccountId); return this; } /** * @returns {?Long} */ get stakedNodeId() { return this._contractCreate.stakedNodeId; } /** * @param {Long | number} stakedNodeId * @returns {this} */ setStakedNodeId(stakedNodeId) { this._contractCreate.setStakedNodeId(stakedNodeId); return this; } /** * @returns {boolean} */ get declineStakingRewards() { return this._contractCreate.declineStakingRewards; } /** * @param {boolean} declineStakingReward * @returns {this} */ setDeclineStakingReward(declineStakingReward) { this._contractCreate.setDeclineStakingReward(declineStakingReward); return this; } /** * @returns {?AccountId} */ get autoRenewAccountId() { return this._contractCreate.autoRenewAccountId; } /** * @param {string | AccountId} autoRenewAccountId * @returns {this} */ setAutoRenewAccountId(autoRenewAccountId) { this._contractCreate.setAutoRenewAccountId(autoRenewAccountId); return this; } /** * Sign the transaction with the private key * **NOTE**: This is a thin wrapper around `.signWith()` * * @param {PrivateKey} privateKey * @returns {this} */ sign(privateKey) { return this.signWith(privateKey.publicKey, message => Promise.resolve(privateKey.sign(message))); } /** * Sign the transaction with the public key and signer function * * If sign on demand is enabled no signing will be done immediately, instead * the private key signing function and public key are saved to be used when * a user calls an exit condition method (not sure what a better name for this is) * such as `toBytes[Async]()`, `getTransactionHash[PerNode]()` or `execute()`. * * @param {PublicKey} publicKey * @param {(message: Uint8Array) => Promise<Uint8Array>} transactionSigner * @returns {this} */ signWith(publicKey, transactionSigner) { const publicKeyData = publicKey.toBytesRaw(); const publicKeyHex = hex.encode(publicKeyData); if (this._signerPublicKeys.has(publicKeyHex)) { // this public key has already signed this transaction return this; } this._publicKeys.push(publicKey); this._transactionSigners.push(transactionSigner); return this; } /** * @template {Channel} ChannelT * @template {MirrorChannel} MirrorChannelT * @param {import("../client/Client.js").default<ChannelT, MirrorChannelT>} client * @param {number=} requestTimeout * @returns {Promise<TransactionResponse>} */ async execute(client, requestTimeout) { if (this._bytecode == null) { throw new Error("cannot create contract with no bytecode"); } const key = client.operatorPublicKey; const fileCreateTransaction = new _FileCreateTransaction.default().setKeys(key != null ? [key] : []).setContents(this._bytecode.subarray(0, Math.min(this._bytecode.length, 2048))).freezeWith(client); await addSignersToTransaction(fileCreateTransaction, this._publicKeys, this._transactionSigners); let response = await fileCreateTransaction.execute(client, requestTimeout); const receipt = await response.getReceipt(client); const fileId = /** @type {FileId} */receipt.fileId; if (this._bytecode.length > 2048) { const fileAppendTransaction = new _FileAppendTransaction.default().setFileId(fileId).setContents(this._bytecode.subarray(2048)).freezeWith(client); await addSignersToTransaction(fileAppendTransaction, this._publicKeys, this._transactionSigners); await fileAppendTransaction.execute(client, requestTimeout); } this._contractCreate.setBytecodeFileId(fileId).freezeWith(client); await addSignersToTransaction(this._contractCreate, this._publicKeys, this._transactionSigners); response = await this._contractCreate.execute(client, requestTimeout); await response.getReceipt(client); if (key != null) { const fileDeleteTransaction = new _FileDeleteTransaction.default().setFileId(fileId).freezeWith(client); await addSignersToTransaction(fileDeleteTransaction, this._publicKeys, this._transactionSigners); await (await fileDeleteTransaction.execute(client, requestTimeout)).getReceipt(client); } return response; } /** * @param {Signer} signer * @returns {Promise<TransactionResponse>} */ async executeWithSigner(signer) { if (this._bytecode == null) { throw new Error("cannot create contract with no bytecode"); } if (signer.getAccountKey == null) { throw new Error("`Signer.getAccountKey()` is not implemented, but is required for `ContractCreateFlow`"); } // eslint-disable-next-line @typescript-eslint/await-thenable const key = await signer.getAccountKey(); let formattedPublicKey; if (key instanceof _PublicKey.default) { formattedPublicKey = key; } else { const propertyValues = Object.values( // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access key._key._key._keyData); const keyArray = new Uint8Array(propertyValues); formattedPublicKey = _PublicKey.default.fromBytes(keyArray); } const fileCreateTransaction = await new _FileCreateTransaction.default().setKeys(formattedPublicKey != null ? [formattedPublicKey] : []).setContents(this._bytecode.subarray(0, Math.min(this._bytecode.length, 2048))).freezeWithSigner(signer); await fileCreateTransaction.signWithSigner(signer); await addSignersToTransaction(fileCreateTransaction, this._publicKeys, this._transactionSigners); let response = await fileCreateTransaction.executeWithSigner(signer); const receipt = await response.getReceiptWithSigner(signer); const fileId = /** @type {FileId} */receipt.fileId; if (this._bytecode.length > 2048) { let fileAppendTransaction = new _FileAppendTransaction.default().setFileId(fileId).setContents(this._bytecode.subarray(2048)); if (this._maxChunks != null) { fileAppendTransaction.setMaxChunks(this._maxChunks); } fileAppendTransaction = await fileAppendTransaction.freezeWithSigner(signer); await fileAppendTransaction.signWithSigner(signer); await addSignersToTransaction(fileAppendTransaction, this._publicKeys, this._transactionSigners); await fileAppendTransaction.executeWithSigner(signer); } this._contractCreate = await this._contractCreate.setBytecodeFileId(fileId).freezeWithSigner(signer); this._contractCreate = await this._contractCreate.signWithSigner(signer); await addSignersToTransaction(this._contractCreate, this._publicKeys, this._transactionSigners); response = await this._contractCreate.executeWithSigner(signer); await response.getReceiptWithSigner(signer); if (key != null) { const fileDeleteTransaction = await new _FileDeleteTransaction.default().setFileId(fileId).freezeWithSigner(signer); await fileDeleteTransaction.signWithSigner(signer); await addSignersToTransaction(fileDeleteTransaction, this._publicKeys, this._transactionSigners); await (await fileDeleteTransaction.executeWithSigner(signer)).getReceiptWithSigner(signer); } return response; } } /** * @template {Transaction} T * @param {T} transaction * @param {PublicKey[]} publicKeys * @param {((message: Uint8Array) => Promise<Uint8Array>)[]} transactionSigners * @returns {Promise<void>} */ exports.default = ContractCreateFlow; async function addSignersToTransaction(transaction, publicKeys, transactionSigners) { for (let i = 0; i < publicKeys.length; i++) { await transaction.signWith(publicKeys[i], transactionSigners[i]); } }