diff --git a/.github/workflows/cheqd-api-tests.yml b/.github/workflows/cheqd-api-tests.yml index c79a6d379..0cceb0542 100644 --- a/.github/workflows/cheqd-api-tests.yml +++ b/.github/workflows/cheqd-api-tests.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest env: CHEQD_MNEMONIC: "steak come surprise obvious remain black trouble measure design volume retreat float coach amused match album moment radio stuff crack orphan ranch dose endorse" - CHEQD_IMAGE_TAG: 3.1.5 + CHEQD_IMAGE_TAG: 4.0.2 CHEQD_NETWORK: "testnet" steps: - uses: actions/checkout@v2 @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest env: CHEQD_MNEMONIC: "steak come surprise obvious remain black trouble measure design volume retreat float coach amused match album moment radio stuff crack orphan ranch dose endorse" - CHEQD_IMAGE_TAG: 3.1.5 + CHEQD_IMAGE_TAG: 4.0.2 CHEQD_NETWORK: "mainnet" steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/cheqd-modules-tests.yml b/.github/workflows/cheqd-modules-tests.yml index 459268436..c5acafc9e 100644 --- a/.github/workflows/cheqd-modules-tests.yml +++ b/.github/workflows/cheqd-modules-tests.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest env: CHEQD_MNEMONIC: "steak come surprise obvious remain black trouble measure design volume retreat float coach amused match album moment radio stuff crack orphan ranch dose endorse" - CHEQD_IMAGE_TAG: 3.1.5 + CHEQD_IMAGE_TAG: 4.0.2 CHEQD_NETWORK: "testnet" steps: - uses: actions/checkout@v2 @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest env: CHEQD_MNEMONIC: "steak come surprise obvious remain black trouble measure design volume retreat float coach amused match album moment radio stuff crack orphan ranch dose endorse" - CHEQD_IMAGE_TAG: 3.1.5 + CHEQD_IMAGE_TAG: 4.0.2 CHEQD_NETWORK: "mainnet" steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index e315adf56..136493d82 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -6,7 +6,7 @@ jobs: runs-on: ubuntu-latest env: CHEQD_MNEMONIC: "steak come surprise obvious remain black trouble measure design volume retreat float coach amused match album moment radio stuff crack orphan ranch dose endorse" - CHEQD_IMAGE_TAG: 3.1.5 + CHEQD_IMAGE_TAG: 4.0.2 CHEQD_NETWORK: "testnet" steps: - uses: actions/checkout@v2 @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest env: CHEQD_MNEMONIC: "steak come surprise obvious remain black trouble measure design volume retreat float coach amused match album moment radio stuff crack orphan ranch dose endorse" - CHEQD_IMAGE_TAG: 3.1.5 + CHEQD_IMAGE_TAG: 4.0.2 CHEQD_NETWORK: "mainnet" steps: - uses: actions/checkout@v2 diff --git a/packages/cheqd-blockchain-api/src/api/gas.js b/packages/cheqd-blockchain-api/src/api/gas.js deleted file mode 100644 index a67c0eb90..000000000 --- a/packages/cheqd-blockchain-api/src/api/gas.js +++ /dev/null @@ -1,125 +0,0 @@ -import { maxBigInt } from '@docknetwork/credential-sdk/utils'; - -/** - * Takes gas amount calculated using addition of gas amounts of every transaction included in batch and returns adjusted gas amount that corresponds to the real-world gas usage plus 20% margin. - * - * For referece, simple DID doc creating with 1 Ed25519 key consumes the following gas amounts: - * - * Pre-calculated: - * - 1 item: 155,000 - * - 10 items: 1,550,000 - * - 25 items: 3,875,000 - * - 50 items: 7,750,000 - * - 100 items: 15,500,000 - * - * Used: - * - 1 item: 127,011 - * - 10 items: 508,369 - * - 25 items: 1,133,562 - * - 50 items: 2,201,925 - * - 100 items: 4,318,315 - * - * @param {bigint|number} calculatedAmount - The calculated amount to convert - * @param {bigint|number} itemCount - Number of items this amount is for - * @returns {bigint} - The estimated used amount plus 20% margin - */ -/* eslint-disable sonarjs/cognitive-complexity */ -export const gasAmountForBatch = (estimatedGas, itemsPerBatch) => { - // Convert inputs to BigInt - const estGas = BigInt(estimatedGas); - const itemCount = BigInt(itemsPerBatch); - - let predictedGas; - let multiplier; - - // Different prediction logic based on item count - if (itemCount === 1n) { - // For single items - multiplier = 100n; // 1 - } else if (itemCount <= 10n) { - // For 2-10 items - if (estGas > 10000000n) { - multiplier = 60n; // 0.5 - } else if (estGas > 4000000n) { - multiplier = 65n; // 0.65 - } else { - multiplier = 70n; // 0.7 - } - } else if (itemCount <= 25n) { - // For 11-25 items - if (estGas > 20000000n) { - multiplier = 15n; // 0.15 - } else if (estGas > 10000000n) { - multiplier = 20n; // 0.2 - } else if (estGas > 4000000n) { - multiplier = 38n; // 0.38 - } else { - multiplier = 35n; // 0.35 - } - } else if (itemCount <= 50n) { - // For 26-50 items - if (estGas > 20000000n) { - multiplier = 27n; // 0.27 - } else if (estGas > 10000000n) { - multiplier = 30n; // 0.3 - } else { - multiplier = 37n; // 0.37 - } - } else { - // For 51+ items - multiplier = estGas > 20000000n ? 55n : 40n; // 0.55 or 0.4 - } - - // Calculate prediction using BigInt division - // Since we're using integers for multiplier, we need to divide by 100 after multiplication - predictedGas = (estGas * multiplier) / 100n; - - // Add 20% buffer (multiply by 120 and divide by 100) - predictedGas = (predictedGas * 120n) / 100n; - - return predictedGas; -}; - -const BASE_CREATE_DID_DOC_GAS_AMOUNT = 135000n; -const DID_DOC_OFFCHAIN_KEY_GAS_AMOUNT = 25000n; -const DID_DOC_ONCHAIN_KEY_GAS_AMOUNT = 5000n; -const DEACTIVATE_DID_DOC_GAS_AMOUNT = 100000n; -const MIN_CREATE_RESOURCE_GAS_AMOUNT = 150000n; -const RESOURCE_BYTE_GAS_AMOUNT = 500n; - -/** - * Calculates gas required to create or update a DID document - * @param {object} tx - Transaction JSON containing payload with assertion and verification methods - * @returns {bigint} - Total gas amount calculated as sum of base gas plus: - * - 25,000 per new assertion method not present in verification methods - * - 5,000 per verification method - */ -export const createOrUpdateDIDDocGas = ({ - payload: { assertionMethod, verificationMethod }, -}) => { - const offchainKeysCost = BigInt( - assertionMethod.filter( - (id) => !verificationMethod.some((item) => item.id === id), - ).length, - ) * DID_DOC_OFFCHAIN_KEY_GAS_AMOUNT; - - const onChainKeysCost = BigInt(verificationMethod.length) * DID_DOC_ONCHAIN_KEY_GAS_AMOUNT; - - return BASE_CREATE_DID_DOC_GAS_AMOUNT + offchainKeysCost + onChainKeysCost; -}; - -/** - * Returns fixed gas amount for deactivating a DID document - * @returns {bigint} - Fixed gas amount of 100,000 - */ -export const deactivateDIDDocGas = () => DEACTIVATE_DID_DOC_GAS_AMOUNT; - -/** - * Calculates gas required to create a resource - * @param {object} tx - Transaction JSON containing payload with data - * @returns {bigint} - Maximum between base gas (150,000) and scaled amount (250 per byte of data) - */ -export const createResourceGas = ({ payload: { data } }) => maxBigInt( - MIN_CREATE_RESOURCE_GAS_AMOUNT, - RESOURCE_BYTE_GAS_AMOUNT * BigInt(data.length), -); diff --git a/packages/cheqd-blockchain-api/src/api/index.js b/packages/cheqd-blockchain-api/src/api/index.js index f30f1b036..b02aa2f72 100644 --- a/packages/cheqd-blockchain-api/src/api/index.js +++ b/packages/cheqd-blockchain-api/src/api/index.js @@ -7,7 +7,6 @@ import { retry, minBigInt, } from '@docknetwork/credential-sdk/utils'; -import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx.js'; import { DIDModule, ResourceModule, @@ -55,13 +54,6 @@ import { import { TypedEnum } from '@docknetwork/credential-sdk/types/generic'; import pLimit from 'p-limit'; import { buildTypeUrlObject, fullTypeUrl, fullTypeUrls } from './type-url'; -import { - createOrUpdateDIDDocGas, - createResourceGas, - deactivateDIDDocGas, - gasAmountForBatch, -} from './gas'; -import { signedTxHash } from '../utils/tx'; export class CheqdAPI extends AbstractApiProvider { #sdk; @@ -90,13 +82,6 @@ export class CheqdAPI extends AbstractApiProvider { ResourceModule.fees.DefaultCreateResourceDefaultFee, ); - static BaseGasAmounts = buildTypeUrlObject( - createOrUpdateDIDDocGas, - createOrUpdateDIDDocGas, - deactivateDIDDocGas, - createResourceGas, - ); - static PayloadWrappers = buildTypeUrlObject( CheqdSetDidDocumentPayloadWithTypeUrlAndSignatures, CheqdSetDidDocumentPayloadWithTypeUrlAndSignatures, @@ -233,6 +218,7 @@ export class CheqdAPI extends AbstractApiProvider { const options = { modules: [DIDModule, ResourceModule, FeemarketModule], rpcUrl: url, + endpoint: url, wallet, network, }; @@ -241,6 +227,7 @@ export class CheqdAPI extends AbstractApiProvider { this.ensureNotInitialized(); this.#sdk = sdk; this.#spawn = pLimit(1); + this.#sdk.signer.endpoint = options.endpoint; // HACK: cheqd SDK doesnt pass this with createCheqdSDK yet return this; } @@ -270,18 +257,10 @@ export class CheqdAPI extends AbstractApiProvider { * @param {object|Array} txOrTxs * @returns {Promise} */ - async estimateGas(txOrTxs) { - const { BaseGasAmounts, BlockLimits } = this.constructor; - + async estimateGas(txOrTxs, from) { const txs = this.constructor.txToJSON(txOrTxs); - const limit = BlockLimits[this.network()]; - - const estimated = txs.reduce( - (total, { typeUrl, value }) => minBigInt(total + BigInt(BaseGasAmounts[typeUrl](value)), limit), - 0n, - ); - - return String(gasAmountForBatch(estimated, txs.length)); + const simulatedGas = await this.sdk.signer.simulate(from, txs); + return String(simulatedGas); } /** @@ -320,15 +299,13 @@ export class CheqdAPI extends AbstractApiProvider { * * @param {string} sender * @param {Array} txJSON - * @param {object} payment + * @param {DidStdFee | 'auto' | number} payment * @param {?string} memo * @returns {Promise} */ - async signAndBroadcast(sender, txJSON, { ...payment }, memo) { + async signAndBroadcast(sender, txJSON, payment, memo) { const { BlockLimits } = this.constructor; - let signedTx; - let connectionClosed = false; const onError = async (err, continueSym) => { const strErr = String(err); @@ -337,45 +314,29 @@ export class CheqdAPI extends AbstractApiProvider { || strErr.includes('Bad status') || strErr.includes('other side closed') ) { - connectionClosed = true; console.error(err); await this.reconnect(); return continueSym; - } else if ( - strErr.includes('exists') - && connectionClosed - && signedTx != null - ) { - const hash = signedTxHash(signedTx); - - const res = await this.txResult(hash); - if (res == null) { - throw new Error( - `Transaction with hash ${hash} not found, but entity already exists: ${JSON.stringify( - txJSON, - )}`, - ); - } - - return res; } else if (strErr.includes('out of gas in location')) { - const gasAmount = BigInt(payment.gas); - const limit = BlockLimits[this.network()]; - if (gasAmount >= limit) { - throw new Error( - `Can't process transaction because it exceeds block gas limit: ${JSON.stringify( - txJSON, - )}`, - ); + if (payment.gas) { + const gasAmount = BigInt(payment.gas); + const limit = BlockLimits[this.network()]; + if (gasAmount >= limit) { + throw new Error( + `Can't process transaction because it exceeds block gas limit: ${JSON.stringify( + txJSON, + )}`, + ); + } + + // eslint-disable-next-line no-param-reassign + payment.gas = String(minBigInt(gasAmount * 2n, limit)); + } else { + throw err; } - - // eslint-disable-next-line no-param-reassign - payment.gas = String(minBigInt(gasAmount * 2n, limit)); - signedTx = void 0; return continueSym; } else if (strErr.includes('account sequence mismatch')) { - signedTx = void 0; return continueSym; } @@ -384,11 +345,7 @@ export class CheqdAPI extends AbstractApiProvider { return await this.#spawn(() => retry( async () => { - signedTx ??= TxRaw.encode( - await this.sdk.signer.sign(sender, txJSON, payment, memo ?? ''), - ).finish(); - const res = await this.sdk.signer.broadcastTx(signedTx); - + const res = await this.sdk.signer.signAndBroadcast(sender, txJSON, payment, memo ?? ''); if (res.code) { console.error(res); @@ -417,18 +374,18 @@ export class CheqdAPI extends AbstractApiProvider { * @returns {Promise<*>} */ async signAndSend(tx, { - from, fee, memo, gas, + from, fee, memo, gas, payment, } = {}) { const sender = from ?? (await this.address()); const txJSON = this.constructor.txToJSON(tx); - const payment = { + const paymentObj = payment || { amount: [].concat(fee ?? this.calculateFee(tx)), - gas: gas ?? (await this.estimateGas(tx)), + gas: gas ?? (await this.estimateGas(tx, sender)), payer: sender, }; - return await this.signAndBroadcast(sender, txJSON, payment, memo); + return await this.signAndBroadcast(sender, txJSON, paymentObj, memo); } async reconnect() {