From 7358ec68915ec272c2574fc6383e448d1046bc81 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 11:09:22 +0100 Subject: [PATCH 01/12] Make sure params are correct in provider mock response --- demos/provider-mock/src/api.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/demos/provider-mock/src/api.ts b/demos/provider-mock/src/api.ts index 8cb775bcfe..56043069b1 100644 --- a/demos/provider-mock/src/api.ts +++ b/demos/provider-mock/src/api.ts @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import { ApiPaths, VerifySolutionBody } from '@prosopo/types' +import { ApiParams, ApiPaths, ImageVerificationResponse, VerifySolutionBody } from '@prosopo/types' import { ProsopoApiError } from '@prosopo/common' import { VerifySolutionBodyType } from '@prosopo/types' import express, { Router } from 'express' @@ -55,16 +55,18 @@ export function prosopoRouter(): Router { ) { approved = true statusMessage = 'API.USER_VERIFIED' - return res.json({ - status: req.t(statusMessage), - verified: approved, - commitmentId: testCommitmentId, - }) + const response: ImageVerificationResponse = { + [ApiParams.status]: req.t(statusMessage), + [ApiParams.verified]: approved, + [ApiParams.commitmentId]: testCommitmentId, + [ApiParams.blockNumber]: parsed.blockNumber, + } + return res.json(response) } return res.json({ - status: req.t(statusMessage), - verified: false, + [ApiParams.status]: req.t(statusMessage), + [ApiParams.verified]: false, }) } catch (err) { return next(new ProsopoApiError('API.UNKNOWN', { context: { error: err, errorCode: 500 } })) From 290ede659b72a8a0722f2a8a6ff68698a725b2ea Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 11:09:43 +0100 Subject: [PATCH 02/12] Allow gas increase factor to be passed to contract interface --- packages/contract/src/contract/helpers.ts | 5 ++-- packages/contract/src/contract/interface.ts | 29 +++++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/contract/src/contract/helpers.ts b/packages/contract/src/contract/helpers.ts index f1e13c7036..d4226feb81 100644 --- a/packages/contract/src/contract/helpers.ts +++ b/packages/contract/src/contract/helpers.ts @@ -161,9 +161,10 @@ export function getOptions( value?: number | BN, gasLimit?: WeightV2, storageDeposit?: StorageDeposit, - increaseGas?: boolean + increaseGas?: boolean, + gasIncreaseFactor?: number ): ContractOptions { - const gasIncreaseFactor = increaseGas ? GAS_INCREASE_FACTOR : 1 + gasIncreaseFactor = increaseGas ? (gasIncreaseFactor ? gasIncreaseFactor : GAS_INCREASE_FACTOR) : 1 const _gasLimit: WeightV2 | undefined = gasLimit ? api.registry.createType('WeightV2', { refTime: gasLimit.refTime.toBn().muln(gasIncreaseFactor), diff --git a/packages/contract/src/contract/interface.ts b/packages/contract/src/contract/interface.ts index ae102f4de1..7cb0b76367 100644 --- a/packages/contract/src/contract/interface.ts +++ b/packages/contract/src/contract/interface.ts @@ -208,7 +208,8 @@ export class ProsopoCaptchaContract extends Contract implements IProsopoCaptchaC async dryRunContractMethod( contractMethodName: string, args: T[], - value?: number | BN | undefined + value?: number | BN | undefined, + gasIncreaseFactor?: number ): Promise { const message = this.getContractMethod(contractMethodName) if (!this.nativeContract.query[message.method]) { @@ -224,7 +225,7 @@ export class ProsopoCaptchaContract extends Contract implements IProsopoCaptchaC ) // Increase the gas required by a factor of 1.1 to make sure we don't hit contracts.StorageDepositLimitExhausted - const options = getOptions(this.api, true, value, gasRequired, storageDeposit, true) + const options = getOptions(this.api, true, value, gasRequired, storageDeposit, true, gasIncreaseFactor) const method = get(this.nativeContract.query, message.method) const extrinsic = method(this.pair.address, options, ...args) const secondResult = await extrinsic @@ -257,7 +258,9 @@ export class ProsopoCaptchaContract extends Contract implements IProsopoCaptchaC async getExtrinsicAndGasEstimates( contractMethodName: string, args: T[], - value?: number | BN | undefined + value?: number | BN | undefined, + increaseGas = false, + gasIncreaseFactor?: number ): Promise<{ extrinsic: SubmittableExtrinsic; options: ContractOptions; storageDeposit: StorageDeposit }> { // Always query first as errors are passed back from a dry run but not from a transaction const message = this.abi.findMessage(contractMethodName) @@ -281,7 +284,15 @@ export class ProsopoCaptchaContract extends Contract implements IProsopoCaptchaC const response = (await extrinsic) as unknown as ContractCallOutcome if (response.result.isOk) { - let options = getOptions(this.api, message.isMutating, value, response.gasRequired, response.storageDeposit) + let options = getOptions( + this.api, + message.isMutating, + value, + response.gasRequired, + response.storageDeposit, + increaseGas, + gasIncreaseFactor + ) let method = this.contract.tx[contractMethodName] if (method === undefined) { throw new RangeError(`Method ${contractMethodName} does not exist on contract ${this.contractName}`) @@ -291,7 +302,15 @@ export class ProsopoCaptchaContract extends Contract implements IProsopoCaptchaC const paymentInfo = await extrinsicTx.paymentInfo(this.pair.address) this.logger.debug('Payment info: ', paymentInfo.partialFee.toHuman()) // increase the gas limit to make sure the tx succeeds - options = getOptions(this.api, message.isMutating, value, paymentInfo.weight, response.storageDeposit, true) + options = getOptions( + this.api, + message.isMutating, + value, + paymentInfo.weight, + response.storageDeposit, + true, + gasIncreaseFactor + ) // Will throw an error if the contract reverted this.getQueryResult(message, response, args) From 5a6bb9dbf6298fe45c91e0cc6c607e88abddb7fa Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 11:10:01 +0100 Subject: [PATCH 03/12] Add command for running provider GUI and use SVG in top bar --- package.json | 1 + provider-gui/components/Topbar.tsx | 2 +- provider-gui/public/prosopo-logo-white.png | Bin 26530 -> 0 bytes provider-gui/public/prosopo-logo-white.svg | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 provider-gui/public/prosopo-logo-white.png create mode 100644 provider-gui/public/prosopo-logo-white.svg diff --git a/package.json b/package.json index 1c1adc1ed9..52b855b5b4 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "setup": "npm -w @prosopo/scripts run setup", "setup:all": "npm run deploy_protocol && npm run setup", "start:provider": "npm -w @prosopo/cli run start", + "start:provider-gui": "npm -w @prosopo/provider-gui run start", "start:server": "npm -w @prosopo/client-example-server run start", "start:demo": "npm -w @prosopo/client-example run start", "start:demo:pow": "npm -w @prosopo/client-pow-example run start", diff --git a/provider-gui/components/Topbar.tsx b/provider-gui/components/Topbar.tsx index c5fbb362ae..1a8beecd75 100644 --- a/provider-gui/components/Topbar.tsx +++ b/provider-gui/components/Topbar.tsx @@ -29,7 +29,7 @@ const TopBar = () => { - Prosopo Logo + Prosopo Logo diff --git a/provider-gui/public/prosopo-logo-white.png b/provider-gui/public/prosopo-logo-white.png deleted file mode 100644 index 8e5c929b0f44dc6fa2694a0afe035878e356f273..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26530 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 Ca2+WC diff --git a/provider-gui/public/prosopo-logo-white.svg b/provider-gui/public/prosopo-logo-white.svg new file mode 100644 index 0000000000..a660ebcae9 --- /dev/null +++ b/provider-gui/public/prosopo-logo-white.svg @@ -0,0 +1 @@ +Prosopo Logo White \ No newline at end of file From ee301eb3d5cf3410d4746cc21cec0fd879861f05 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 11:19:11 +0100 Subject: [PATCH 04/12] make blockNumber and commitmentId optional params on ImageVerificationResponse --- demos/provider-mock/src/api.ts | 14 ++++++++++---- packages/procaptcha/src/modules/Manager.ts | 2 +- packages/provider/src/api/captcha.ts | 15 ++++++--------- packages/server/src/server.ts | 6 +++++- packages/types/src/provider/api.ts | 4 ++-- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/demos/provider-mock/src/api.ts b/demos/provider-mock/src/api.ts index 56043069b1..44136a62b5 100644 --- a/demos/provider-mock/src/api.ts +++ b/demos/provider-mock/src/api.ts @@ -11,7 +11,13 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import { ApiParams, ApiPaths, ImageVerificationResponse, VerifySolutionBody } from '@prosopo/types' +import { + ApiParams, + ApiPaths, + ImageVerificationResponse, + VerificationResponse, + VerifySolutionBody, +} from '@prosopo/types' import { ProsopoApiError } from '@prosopo/common' import { VerifySolutionBodyType } from '@prosopo/types' import express, { Router } from 'express' @@ -63,11 +69,11 @@ export function prosopoRouter(): Router { } return res.json(response) } - - return res.json({ + const verificationResponse: VerificationResponse = { [ApiParams.status]: req.t(statusMessage), [ApiParams.verified]: false, - }) + } + return res.json(verificationResponse) } catch (err) { return next(new ProsopoApiError('API.UNKNOWN', { context: { error: err, errorCode: 500 } })) } diff --git a/packages/procaptcha/src/modules/Manager.ts b/packages/procaptcha/src/modules/Manager.ts index 8160c92125..f127a14be0 100644 --- a/packages/procaptcha/src/modules/Manager.ts +++ b/packages/procaptcha/src/modules/Manager.ts @@ -194,7 +194,7 @@ export function Manager( undefined, configOptional.challengeValidLength ) - if (verifyDappUserResponse.verified) { + if (verifyDappUserResponse.verified && verifyDappUserResponse.commitmentId) { updateState({ isHuman: true, loading: false }) const output: ProcaptchaOutput = { [ApiParams.providerUrl]: procaptchaStorage.providerUrl, diff --git a/packages/provider/src/api/captcha.ts b/packages/provider/src/api/captcha.ts index e01206c180..84ce8003b2 100644 --- a/packages/provider/src/api/captcha.ts +++ b/packages/provider/src/api/captcha.ts @@ -142,27 +142,24 @@ export function prosopoRouter(env: ProviderEnvironment): Router { return next(new ProsopoApiError('CAPTCHA.PARSE_ERROR', { context: { errorCode: 400, error: err } })) } try { + const failedVerificationResponse: VerificationResponse = { + [ApiParams.status]: req.t('API.USER_NOT_VERIFIED'), + [ApiParams.verified]: false, + } const solution = await (parsed.commitmentId ? tasks.getDappUserCommitmentById(parsed.commitmentId) : tasks.getDappUserCommitmentByAccount(parsed.user)) if (!solution) { - return res.json({ - [ApiParams.status]: req.t('API.USER_NOT_VERIFIED'), - [ApiParams.verified]: false, - }) + return res.json(failedVerificationResponse) } if (parsed.maxVerifiedTime) { const currentBlockNumber = await getCurrentBlockNumber(tasks.contract.api) const blockTimeMs = getBlockTimeMs(tasks.contract.api) const timeSinceCompletion = (currentBlockNumber - solution.completedAt) * blockTimeMs - const verificationResponse: VerificationResponse = { - [ApiParams.status]: req.t('API.USER_NOT_VERIFIED'), - [ApiParams.verified]: false, - } if (timeSinceCompletion > parsed.maxVerifiedTime) { - return res.json(verificationResponse) + return res.json(failedVerificationResponse) } } diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 13a653ce59..b37efbbded 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -199,6 +199,7 @@ export class ProsopoServer { commitmentId?: string, maxVerifiedTime = DEFAULT_MAX_VERIFIED_TIME_CACHED ) { + let verifyRecency = false this.logger.info('Verifying with provider.') const providerApi = await this.getProviderApi(providerUrl) if (challenge) { @@ -207,7 +208,10 @@ export class ProsopoServer { return result.verified } const result = await providerApi.verifyDappUser(dapp, user, blockNumber, commitmentId, maxVerifiedTime) - const verifyRecency = await this.verifyRecency(result.blockNumber, maxVerifiedTime) + + if (result.blockNumber) { + verifyRecency = await this.verifyRecency(result.blockNumber, maxVerifiedTime) + } return result.verified && verifyRecency } diff --git a/packages/types/src/provider/api.ts b/packages/types/src/provider/api.ts index 6e91f433d7..6d0c191e70 100644 --- a/packages/types/src/provider/api.ts +++ b/packages/types/src/provider/api.ts @@ -128,9 +128,9 @@ export interface VerificationResponse { } export interface ImageVerificationResponse extends VerificationResponse { - [ApiParams.commitmentId]: Hash + [ApiParams.commitmentId]?: Hash // The block at which the captcha was requested - [ApiParams.blockNumber]: number + [ApiParams.blockNumber]?: number } export interface GetPowCaptchaResponse { From f94febdec15a386a389e1ca9e33a9737231f3cdd Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 11:35:29 +0100 Subject: [PATCH 05/12] Add build CICD for provider mock --- .github/workflows/provider_image.yml | 31 ++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/.github/workflows/provider_image.yml b/.github/workflows/provider_image.yml index fb4e8b73fa..c154d2468d 100644 --- a/.github/workflows/provider_image.yml +++ b/.github/workflows/provider_image.yml @@ -117,7 +117,7 @@ jobs: # create the provider image for AMD64 - name: Build the Provider Container id: build_docker_provider_amd64 - continue-on-error: true + continue-on-error: false uses: docker/build-push-action@v5 with: context: ${{github.workspace}} @@ -130,7 +130,7 @@ jobs: # create the provider image for ARM64 - name: Build the Provider Container id: build_docker_provider_arm64 - continue-on-error: true + continue-on-error: false uses: docker/build-push-action@v5 with: context: ${{github.workspace}} @@ -157,3 +157,30 @@ jobs: docker logs $CONTAINER >& provider.log echo $(cat provider.log) grep -oE "Version: \".*\"" provider.log || (echo $(cat provider.log) && exit 1) + + # create the provider mock image for AMD64 + - name: Build the Provider Mock Container + id: build_docker_provider_mock_amd64 + continue-on-error: false + uses: docker/build-push-action@v5 + with: + context: ${{github.workspace}} + file: ${{github.workspace}}/docker/images/provider.mock.dockerfile + platforms: linux/amd64 + push: false + tags: prosopo/provider-mock:dev + outputs: type=docker,dest=provider-mock-amd64.tar + + # load the provider-mock AMD64 image + - name: Load the Provider Container + run: docker load -i provider-mock-amd64.tar + + # Check that the version command works when running the bundle in the provider image + - name: Check provider mock container runs + run: | + CONTAINER=$(docker run -p 9229:9229 prosopo/provider-mock:dev') + sleep 10s + docker logs $CONTAINER >& provider-mock.log + echo $(cat provider.log) + RESPONSE=$(curl --location 'http://localhost:9229/v1/prosopo/provider/verify' --header 'Content-Type: application/json' --data '{"user": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY","dapp": "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM","blockNumber": 1}'| jq --raw-output '.status') + if [ "$RESPONSE" = "User verified" ]; then exit 0; else exit 1; fi From f0e43b361640d809eabf14f9e3cc25317f4dc867 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 11:37:26 +0100 Subject: [PATCH 06/12] build and push the provider mock image --- .github/workflows/publish.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d1db88e761..ed7e015ee1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -578,6 +578,18 @@ jobs: push: true tags: prosopo/provider:${{ steps.next_version.outputs.version }},prosopo/provider:latest + - name: Build and push the Provider Mock Container + if: steps.check_version_docker_js_server.outputs.bump == 'true' + id: publish_docker_provider_mock + continue-on-error: true + uses: docker/build-push-action@v5 + with: + context: ${{github.workspace}} + file: ${{github.workspace}}/docker/images/provider.mock.dockerfile + platforms: linux/amd64 + push: true + tags: prosopo/provider:${{ steps.next_version.outputs.version }},prosopo/provider-mock:latest + - name: Docker provider release notification if: always() run: | From 2c0261cf4aaf977bcb49abbe9584e278bc458387 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 11:47:07 +0100 Subject: [PATCH 07/12] Add build step for provider-mock package --- .github/workflows/publish.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ed7e015ee1..78b327506d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -578,6 +578,12 @@ jobs: push: true tags: prosopo/provider:${{ steps.next_version.outputs.version }},prosopo/provider:latest + - name: Build the provider-mock package + id: build_provider_mock_package + run: | + echo "Building the provider-mock package..." + npm run -w @prosopo/provider-mock build + - name: Build and push the Provider Mock Container if: steps.check_version_docker_js_server.outputs.bump == 'true' id: publish_docker_provider_mock From aef4d53b8f8dc8da7b57374e27571baf8d9761d5 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 11:52:29 +0100 Subject: [PATCH 08/12] Add build step for provider-mock package --- .github/workflows/provider_image.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/provider_image.yml b/.github/workflows/provider_image.yml index c154d2468d..59c50dc7f7 100644 --- a/.github/workflows/provider_image.yml +++ b/.github/workflows/provider_image.yml @@ -158,6 +158,12 @@ jobs: echo $(cat provider.log) grep -oE "Version: \".*\"" provider.log || (echo $(cat provider.log) && exit 1) + - name: Build the provider-mock package + id: build_provider_mock_package + run: | + echo "Building the provider-mock package..." + npm run -w @prosopo/provider-mock build + # create the provider mock image for AMD64 - name: Build the Provider Mock Container id: build_docker_provider_mock_amd64 From fb38a188ac8959e14418c573a16c58cf5b2e6399 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 12:02:04 +0100 Subject: [PATCH 09/12] prettier --- .github/workflows/provider_image.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/provider_image.yml b/.github/workflows/provider_image.yml index 59c50dc7f7..2f293307c6 100644 --- a/.github/workflows/provider_image.yml +++ b/.github/workflows/provider_image.yml @@ -161,8 +161,8 @@ jobs: - name: Build the provider-mock package id: build_provider_mock_package run: | - echo "Building the provider-mock package..." - npm run -w @prosopo/provider-mock build + echo "Building the provider-mock package..." + npm run -w @prosopo/provider-mock build # create the provider mock image for AMD64 - name: Build the Provider Mock Container @@ -181,12 +181,12 @@ jobs: - name: Load the Provider Container run: docker load -i provider-mock-amd64.tar - # Check that the version command works when running the bundle in the provider image + # Check that the version command works when running the bundle in the provider image - name: Check provider mock container runs run: | - CONTAINER=$(docker run -p 9229:9229 prosopo/provider-mock:dev') - sleep 10s - docker logs $CONTAINER >& provider-mock.log - echo $(cat provider.log) - RESPONSE=$(curl --location 'http://localhost:9229/v1/prosopo/provider/verify' --header 'Content-Type: application/json' --data '{"user": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY","dapp": "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM","blockNumber": 1}'| jq --raw-output '.status') - if [ "$RESPONSE" = "User verified" ]; then exit 0; else exit 1; fi + CONTAINER=$(docker run -p 9229:9229 prosopo/provider-mock:dev') + sleep 10s + docker logs $CONTAINER >& provider-mock.log + echo $(cat provider.log) + RESPONSE=$(curl --location 'http://localhost:9229/v1/prosopo/provider/verify' --header 'Content-Type: application/json' --data '{"user": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY","dapp": "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM","blockNumber": 1}'| jq --raw-output '.status') + if [ "$RESPONSE" = "User verified" ]; then exit 0; else exit 1; fi From 47a4977c21a0b5b0e80854c07cadbd89ed4a22bb Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 12:02:59 +0100 Subject: [PATCH 10/12] remove trailing quote --- .github/workflows/provider_image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/provider_image.yml b/.github/workflows/provider_image.yml index 2f293307c6..164e727157 100644 --- a/.github/workflows/provider_image.yml +++ b/.github/workflows/provider_image.yml @@ -184,7 +184,7 @@ jobs: # Check that the version command works when running the bundle in the provider image - name: Check provider mock container runs run: | - CONTAINER=$(docker run -p 9229:9229 prosopo/provider-mock:dev') + CONTAINER=$(docker run -p 9229:9229 prosopo/provider-mock:dev) sleep 10s docker logs $CONTAINER >& provider-mock.log echo $(cat provider.log) From 82c3d7e583edd5759249aa6b71ac87e5cbd18332 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Fri, 26 Apr 2024 12:10:09 +0100 Subject: [PATCH 11/12] run the container in the background --- .github/workflows/provider_image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/provider_image.yml b/.github/workflows/provider_image.yml index 164e727157..71d0efe4d2 100644 --- a/.github/workflows/provider_image.yml +++ b/.github/workflows/provider_image.yml @@ -184,7 +184,7 @@ jobs: # Check that the version command works when running the bundle in the provider image - name: Check provider mock container runs run: | - CONTAINER=$(docker run -p 9229:9229 prosopo/provider-mock:dev) + CONTAINER=$(docker run -d -p 9229:9229 prosopo/provider-mock:dev) sleep 10s docker logs $CONTAINER >& provider-mock.log echo $(cat provider.log) From fad7f000a560459e9a5ce5c788c35f724898a5d4 Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Mon, 6 May 2024 16:45:20 +0100 Subject: [PATCH 12/12] Simplify --- packages/contract/src/contract/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contract/src/contract/helpers.ts b/packages/contract/src/contract/helpers.ts index d4226feb81..68a569b7ed 100644 --- a/packages/contract/src/contract/helpers.ts +++ b/packages/contract/src/contract/helpers.ts @@ -164,7 +164,7 @@ export function getOptions( increaseGas?: boolean, gasIncreaseFactor?: number ): ContractOptions { - gasIncreaseFactor = increaseGas ? (gasIncreaseFactor ? gasIncreaseFactor : GAS_INCREASE_FACTOR) : 1 + gasIncreaseFactor = increaseGas ? gasIncreaseFactor || GAS_INCREASE_FACTOR : 1 const _gasLimit: WeightV2 | undefined = gasLimit ? api.registry.createType('WeightV2', { refTime: gasLimit.refTime.toBn().muln(gasIncreaseFactor),