UNPKG

@archwayhq/arch3-core

Version:

Core library to interact with Archway Network

384 lines 20.6 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SigningArchwayClient = void 0; const amino_1 = require("@cosmjs/amino"); const cosmwasm_stargate_1 = require("@cosmjs/cosmwasm-stargate"); const modules_1 = require("@cosmjs/cosmwasm-stargate/build/modules"); const math_1 = require("@cosmjs/math"); const proto_signing_1 = require("@cosmjs/proto-signing"); const stargate_1 = require("@cosmjs/stargate"); const tendermint_rpc_1 = require("@cosmjs/tendermint-rpc"); const utils_1 = require("@cosmjs/utils"); const lodash_1 = __importDefault(require("lodash")); const modules_2 = require("./modules"); const queryclient_1 = require("./queryclient"); const utils_2 = require("./utils"); const defaultGasAdjustment = 1.5; function buildResult(response) { const { height, transactionHash, gasWanted, gasUsed, events, parsedLogs } = response; return { logs: parsedLogs, height, transactionHash, events, gasWanted, gasUsed, }; } const flatFeeRequiredTypes = [ '/cosmwasm.wasm.v1.MsgExecuteContract', '/cosmwasm.wasm.v1.MsgMigrateContract', ]; /** * Extension to the {@link SigningCosmWasmClient} for transacting with Archway's modules. */ class SigningArchwayClient extends cosmwasm_stargate_1.SigningCosmWasmClient { constructor(cometClient, signer, options) { const { registry = new proto_signing_1.Registry([...stargate_1.defaultRegistryTypes, ...modules_1.wasmTypes, ...modules_2.rewardsTypes]), aminoTypes = new stargate_1.AminoTypes({ ...(0, stargate_1.createDefaultAminoConverters)(), ...(0, cosmwasm_stargate_1.createWasmAminoConverters)(), ...(0, modules_2.createRewardsAminoConverters)(), ...(0, modules_2.createAuthzAminoConverters)(), }), gasAdjustment = defaultGasAdjustment, } = options; super(cometClient, signer, { ...options, registry, aminoTypes }); this.archwaySigner = signer; this.archwayQueryClient = (0, queryclient_1.createArchwayQueryClient)(cometClient); this.gasAdjustment = gasAdjustment; } /** * Creates an instance by connecting to the given Tendermint RPC endpoint. * * @param endpoint - String URL of the RPC endpoint to connect or an {@link HttpEndpoint} object. * @param signer - The transaction signer configuration. * @param options - Options for the signing client. * @returns A {@link SigningArchwayClient} connected to the endpoint. */ static async connectWithSigner(endpoint, signer, options = {}) { const cometClient = await (0, tendermint_rpc_1.connectComet)(endpoint); return SigningArchwayClient.createWithSigner(cometClient, signer, options); } /** * Creates an instance by connecting to the given Tendermint RPC endpoint using an HttpBatchClient to batch * multiple requests and reduce queries to the server. * * @param endpoint - String URL of the RPC endpoint to connect or an {@link HttpEndpoint} object. * @param signer - The transaction signer configuration. * @param options - Options for the signing client. * @param batchClientOptions - Optional configuration to control how the HttpBatchClient will batch requests. * @returns A {@link SigningArchwayClient} connected to the endpoint. * * @remarks This factory method doesn't support WebSocket endpoints. */ static async connectWithSignerAndBatchClient(endpoint, signer, options, batchClientOptions) { const cometBatchClient = await (0, utils_2.connectCometWithBatchClient)(endpoint, batchClientOptions); return SigningArchwayClient.createWithSigner(cometBatchClient, signer, options); } /** * Creates an instance from a manually created CometBFT client. * * @param cometClient - A CometBFT client for a given endpoint. * @param signer - The transaction signer configuration. * @param options - Options for the signing client. * @returns A {@link SigningArchwayClient} connected to the endpoint. */ /* eslint-disable-next-line @typescript-eslint/require-await */ static async createWithSigner(cometClient, signer, options = {}) { return new SigningArchwayClient(cometClient, signer, options); } /** * Creates a client in offline mode. * * @remarks * This should only be used in niche cases where you know exactly what you're doing, * e.g. when building an offline signing application. * * When you try to use online functionality with such a signer, an * exception will be raised. * * @param signer - the transaction signer configuration. * @param options - options for signing and broadcasting transactions. * @returns An offline {@link SigningArchwayClient}. */ /* eslint-disable-next-line @typescript-eslint/require-await */ static async offline(signer, options = {}) { return new SigningArchwayClient(undefined, signer, options); } /** * Updates the rewards metadata of a contract. * * @param senderAddress - Address of the message sender. * @param metadata - The rewards metadata. * @param fee - Fee to pay for the transaction. Use 'auto' or a number to calculate the fees automatically. * When a number is set, it will be used as a gas adjustment multiplier for the estimated fees. * @param memo - Optional memo to add to the transaction. * @returns A {@link SetContractMetadataResult} with the contract's metadata. * @throws Error if the transaction fails. * * @see {@link SigningArchwayClient.withdrawContractRewards} for details on how to withdraw rewards. * @see Check the [Archway Bindings](https://github.com/archway-network/archway-bindings) repository * for more information on how to withdraw rewards from a contract. */ async setContractMetadata(senderAddress, metadata, fee, memo) { var _a, _b, _c; const message = modules_2.RewardsMsgEncoder.setContractMetadata({ senderAddress, metadata: { contractAddress: metadata.contractAddress, ownerAddress: (_a = metadata.ownerAddress) !== null && _a !== void 0 ? _a : '', rewardsAddress: (_b = metadata.rewardsAddress) !== null && _b !== void 0 ? _b : '', withdrawToWallet: (_c = metadata.withdrawToWallet) !== null && _c !== void 0 ? _c : false, }, }); const response = await this.assertSignAndBroadcast(senderAddress, [message], fee, memo); const metadataAttr = stargate_1.logs.findAttribute(response.parsedLogs, 'archway.rewards.v1.ContractMetadataSetEvent', 'metadata'); /* eslint-disable @typescript-eslint/naming-convention */ const contractMetadata = JSON.parse(metadataAttr.value); /* eslint-enable */ return { ...buildResult(response), metadata: { contractAddress: contractMetadata.contract_address, ownerAddress: contractMetadata.owner_address, rewardsAddress: contractMetadata.rewards_address, withdrawToWallet: contractMetadata.withdraw_to_wallet, }, }; } /** * Updates the contract's premium fee. Only the owner of the contract metadata can update the fee. * * @param senderAddress - Address of the message sender. * @param contractAddress - Contract address to set the premium fee. * @param flatFee - The contract premium fee. To disable the fee, set its `amount` to `0`. * @param fee - Fee to pay for the transaction. Use 'auto' or a number to calculate the fees automatically. * When a number is set, it will be used as a gas adjustment multiplier for the estimated fees. * @param memo - Optional memo to add to the transaction. * @returns A {@link SetContractPremiumResult} with the contract's premium fee. * @throws Error if the transaction fails. * * @see {@link SigningArchwayClient.withdrawContractRewards} for details on how to withdraw rewards. * @see Check the [Archway Bindings](https://github.com/archway-network/archway-bindings) repository * for more information on how to withdraw rewards from a contract. */ async setContractPremium(senderAddress, contractAddress, flatFee, fee, memo) { const message = modules_2.RewardsMsgEncoder.setFlatFee({ senderAddress, contractAddress, flatFeeAmount: flatFee, }); const response = await this.assertSignAndBroadcast(senderAddress, [message], fee, memo); const flatFeeAttr = stargate_1.logs.findAttribute(response.parsedLogs, 'archway.rewards.v1.ContractFlatFeeSetEvent', 'flat_fee'); return { ...buildResult(response), premium: { contractAddress, flatFee: JSON.parse(flatFeeAttr.value), }, }; } /** * Withdraws rewards for the `senderAddress` up to the given `limit` of records to process. * If the limit is set to `0`, it will use the default limit from the protocol. * The default limit is a parameter on the rewards module and it can be updated via governance. * * @remarks * This method is useful when the contract has a large number of rewards to withdraw, * so they can be processed in batches. * * @param senderAddress - Address of the message sender and rewards destination. * @param limit - Maximum number of rewards to withdraw. * @param fee - Fee to pay for the transaction. Use 'auto' or a number to calculate the fees automatically. * When a number is set, it will be used as a gas adjustment multiplier for the estimated fees. * @param memo - Optional memo to add to the transaction. * @returns A {@link WithdrawContractRewardsResult} with information about the rewards withdrawn. * @throws Error if the transaction fails. * * @see Check the [Archway Bindings](https://github.com/archway-network/archway-bindings) repository * for more information on how to withdraw rewards from a contract. */ async withdrawContractRewards(senderAddress, limit, fee, memo) { var _a, _b; const rewardsAddress = senderAddress; const message = modules_2.RewardsMsgEncoder.withdrawRewards({ rewardsAddress, recordsLimit: { limit: BigInt(limit), }, }); const response = await this.assertSignAndBroadcast(senderAddress, [message], fee, memo); const firstLogs = response.parsedLogs.find(() => true); const rewardsAttr = (_b = (_a = firstLogs === null || firstLogs === void 0 ? void 0 : firstLogs.events.find(event => event.type === 'archway.rewards.v1.RewardsWithdrawEvent')) === null || _a === void 0 ? void 0 : _a.attributes.find(attr => attr.key === 'rewards')) === null || _b === void 0 ? void 0 : _b.value; const rewards = rewardsAttr ? JSON.parse(rewardsAttr) : []; return { ...buildResult(response), rewardsAddress, rewards, }; } /** * Creates a transaction with the given messages, fee and memo. Then signs and broadcasts the transaction. * * When setting the fee to 'auto' or a number, the fee will be calculated automatically based on the messages, * the minimum price of gas (mPoG) and the minimum consensus fee. If the messages include a contract execution * or migration, the contract premium fee will be added to the transaction fee. * * @param signerAddress - The address that will sign transactions using this instance. * The signer must be able to sign with this address. * @param messages - The messages to include in the transaction. The messages types should be registered in the * {@link SigningArchwayClient.registry} when the client is instantiated. * @param fee - Fee to pay for the transaction. Use 'auto' or a number to calculate the fees automatically. * When a number is set, it will be used as a gas adjustment multiplier for the estimated fees. * @param memo - Optional memo to add to the transaction. * @returns A {@link DeliverTxResponse} after successfully broadcasting the transaction. * * @see {@link SigningArchwayClient.calculateFee} for calculating the fees before broadcasting. */ async signAndBroadcast(signerAddress, messages, fee, memo) { let usedFee; if (fee === 'auto' || typeof fee === 'number') { const gasAdjustment = typeof fee === 'number' ? fee : this.gasAdjustment; usedFee = await this.calculateFee(signerAddress, messages, memo, gasAdjustment); } else { usedFee = fee; } return super.signAndBroadcast(signerAddress, messages, usedFee, memo); } /** * Calculates tx fees by simulating the execution of a transaction with the given messages. * The fee will be calculated based on the minimum price of gas (mPoG) and the minimum consensus * fee of the network. If the messages include a contract execution or migration, the contract * premium fee will be added to the calculation. * * @param signerAddress - Address used in the gas simulation that will sign transactions. * The signer must be able to sign with this address. * @param messages - The messages to include in the transaction for simulating the gas wanted. * The messages types should be registered in the {@link SigningArchwayClient.registry} * when the client is instantiated. * @param memo - Optional memo to add to the transaction. * @param gasAdjustment - Adjustment factor to be multiplied against the gas estimate. * @param granter - The granter address that is used for paying with feegrants. * @param payer - The fee payer address. The payer must have signed the transaction. * @returns A {@link StdFee} with the estimated fee for the transaction. * * @see {@link SigningArchwayClient.simulate} for simulating the execution of a transaction. * @see {@link SigningArchwayClient.getEstimateTxFees} for getting the minimum price of gas (mPoG) and the minimum * consensus fee of the network. */ async calculateFee(signerAddress, messages, memo, gasAdjustment = this.gasAdjustment, granter, payer) { const gasEstimation = await this.simulate(signerAddress, messages, memo, granter, payer); const gas = Math.round(gasEstimation * gasAdjustment); const { estimatedFee } = await this.getEstimateTxFees(gas); const fee = await this.includeFlatFees(messages, estimatedFee); return { ...fee, granter, payer, }; } async includeFlatFees(messages, fee) { // We memoize the contract premium fee to avoid querying the same contract multiple times. const _getContractPremium = lodash_1.default.memoize((contractAddress) => this.getContractPremium(contractAddress)); const flatFees = await Promise.all(messages .filter(({ typeUrl }) => flatFeeRequiredTypes.includes(typeUrl)) .map(async ({ value }) => { const contractAddress = lodash_1.default.get(value, 'contract'); const { flatFee } = await _getContractPremium(contractAddress); return flatFee; })).then(lodash_1.default.compact); // eslint-disable-line @typescript-eslint/unbound-method const amount = [...fee.amount, ...flatFees].reduce(amino_1.addCoins); return { ...fee, amount: [amount], }; } async assertSignAndBroadcast(signerAddress, messages, fee, memo) { const response = await this.signAndBroadcast(signerAddress, messages, fee, memo); (0, stargate_1.assertIsDeliverTxSuccess)(response); const parsedLogs = stargate_1.logs.parseRawLog(response.rawLog); return { ...response, parsedLogs, }; } /** * Withdraws staking rewards. * * @param delegatorAddress - Address of the delegator withdrawing the staking rewards. * @param validatorAddress - Address of the validator in the format `archwayval` + hex encoded public key. * @param fee - Fee to pay for the transaction. Use 'auto' to calculate the fee automatically. * @param memo - Optional memo to add to the transaction. * @returns A {@link DeliverTxResponse} with information about the the withdraw tx. */ /* istanbul ignore next */ async withdrawRewards(delegatorAddress, validatorAddress, fee, memo) { return await super.withdrawRewards(delegatorAddress, validatorAddress, fee, memo); } /** * Simulates the transaction and estimates the gas it uses. * Unlike {@link SigningCosmWasmClient.simulate}, it uses `payer` and `granter` * arguments during the simulation, to support Archway-specific `cw-fees` mechanics * and include gas the `SudoMsg::CwGrant` message consumes into the estimation. * * @param signerAddress - Address used in the gas simulation that will sign transactions. * The signer must be able to sign with this address. * @param messages - The messages to include in the transaction for simulating the gas wanted. * The messages types should be registered in the {@link SigningArchwayClient.registry} * when the client is instantiated. * @param memo - Optional memo to add to the transaction. * @param granter - The granter address that is used for paying with feegrants. * @param payer - The fee payer address. The payer must have signed the transaction. * @returns A number of gas the tx used during the simulation. * * @see https://docs.archway.io/developers/guides/cw-fees/introduction */ async simulate(signerAddress, messages, memo, granter, payer) { const anyMsgs = messages.map(m => this.registry.encodeAsAny(m)); const accountFromSigner = (await this.archwaySigner.getAccounts()).find(account => account.address === signerAddress); if (!accountFromSigner) { throw new Error('Failed to retrieve account from signer'); } const pubkey = (0, amino_1.encodeSecp256k1Pubkey)(accountFromSigner.pubkey); const { sequence } = await this.getSequence(signerAddress); const { gasInfo } = await this.archwayQueryClient.simulateTx(anyMsgs, memo, pubkey, sequence, granter, payer); (0, utils_1.assertDefined)(gasInfo); return math_1.Uint53.fromString(gasInfo.gasUsed.toString()).toNumber(); } /* istanbul ignore next */ async getBlockRewardsTracking() { return await this.archwayQueryClient.getBlockRewardsTracking(); } /* istanbul ignore next */ async getContractMetadata(contractAddress) { return await this.archwayQueryClient.getContractMetadata(contractAddress); } /* istanbul ignore next */ async getContractPremium(contractAddress) { return await this.archwayQueryClient.getContractPremium(contractAddress); } /* istanbul ignore next */ async getEstimateTxFees(gasLimit, contractAddress) { return await this.archwayQueryClient.getEstimateTxFees(gasLimit, contractAddress); } /* istanbul ignore next */ async getOutstandingRewards(rewardsAddress) { return await this.archwayQueryClient.getOutstandingRewards(rewardsAddress); } /* istanbul ignore next */ async getRewardsPool() { return await this.archwayQueryClient.getRewardsPool(); } /* istanbul ignore next */ async getAllRewardsRecords(rewardsAddress) { return await this.archwayQueryClient.getAllRewardsRecords(rewardsAddress); } /* istanbul ignore next */ async simulateTx(messages, memo, signer, sequence, granter, payer) { return await this.archwayQueryClient.simulateTx(messages, memo, signer, sequence, granter, payer); } } exports.SigningArchwayClient = SigningArchwayClient; //# sourceMappingURL=signingarchwayclient.js.map