diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab631a922..3b107324b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,6 @@ jobs: - name: Run Ganache with Barge working-directory: ${{ github.workspace }}/barge run: | - export CONTRACTS_VERSION=v2.3.0 bash -x start_ocean.sh --with-typesense 2>&1 > start_ocean.log & - name: Install deps & build run: npm ci && npm run build:metadata @@ -118,23 +117,11 @@ jobs: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} - - name: Delete default runner images - run: | - docker image rm -f node:20 - docker image rm -f node:20-alpine - docker image rm -f node:18 - docker image rm -f node:18-alpine - docker image rm -f debian:10 - docker image rm -f debian:11 - docker image rm -f ubuntu:22.04 - docker image rm -f ubuntu:20.04 - docker image rm -f moby/buildkit:latest - rm -rf /usr/share/swift/ - - name: Run Barge working-directory: ${{ github.workspace }}/barge run: | - bash -x start_ocean.sh --with-typesense 2>&1 > start-node.log & + bash -x start_ocean.sh --with-typesense 2>&1 > start-node.log & + - name: Install deps & build run: npm ci && npm run build:metadata diff --git a/package-lock.json b/package-lock.json index 4045b7243..6c0b1a79f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@oasisprotocol/sapphire-paratime": "^1.3.2", - "@oceanprotocol/contracts": "^2.4.0", + "@oceanprotocol/contracts": "^2.4.1", "@oceanprotocol/ddo-js": "^0.1.4", "@rdfjs/dataset": "^2.0.2", "@rdfjs/formats-common": "^3.1.0", @@ -3988,9 +3988,9 @@ } }, "node_modules/@oceanprotocol/contracts": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-2.4.0.tgz", - "integrity": "sha512-OqDUBqQXPT68geLgGJDyH9Wsg1lE1V4ZS7aCJSfGdDWlMOBUAh/KxuaY46ga6WwWz8XVGTB+HZLGLQTYROo5tw==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-2.4.1.tgz", + "integrity": "sha512-mOad2bCyOqYTp5oSiivH23shU/AyfEfp/beBl9zkSEhz7LagvFvDAIicELfHd0NaSVSwvTbM5NbMuOC0Wd5NSA==", "license": "Apache-2.0" }, "node_modules/@oceanprotocol/ddo-js": { @@ -25280,9 +25280,9 @@ } }, "@oceanprotocol/contracts": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-2.4.0.tgz", - "integrity": "sha512-OqDUBqQXPT68geLgGJDyH9Wsg1lE1V4ZS7aCJSfGdDWlMOBUAh/KxuaY46ga6WwWz8XVGTB+HZLGLQTYROo5tw==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-2.4.1.tgz", + "integrity": "sha512-mOad2bCyOqYTp5oSiivH23shU/AyfEfp/beBl9zkSEhz7LagvFvDAIicELfHd0NaSVSwvTbM5NbMuOC0Wd5NSA==" }, "@oceanprotocol/ddo-js": { "version": "0.1.4", diff --git a/package.json b/package.json index ae6292453..d09e294f4 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ }, "dependencies": { "@oasisprotocol/sapphire-paratime": "^1.3.2", - "@oceanprotocol/contracts": "^2.4.0", + "@oceanprotocol/contracts": "^2.4.1", "@oceanprotocol/ddo-js": "^0.1.4", "@rdfjs/dataset": "^2.0.2", "@rdfjs/formats-common": "^3.1.0", diff --git a/src/config/Config.ts b/src/config/Config.ts index 684c873a4..73d8f81a8 100644 --- a/src/config/Config.ts +++ b/src/config/Config.ts @@ -82,6 +82,12 @@ export class Config { */ public opfCommunityFeeCollector?: string + /** + * OPFCommunityFeeCollector + * @type {string} + */ + public EnterpriseFeeCollector?: string + /** * SideStaking address * @type {string} diff --git a/src/config/ConfigHelper.ts b/src/config/ConfigHelper.ts index 2b17dc7aa..e51814c1f 100644 --- a/src/config/ConfigHelper.ts +++ b/src/config/ConfigHelper.ts @@ -216,7 +216,8 @@ export class ConfigHelper { veFeeEstimate, Router, AccessListFactory, - Escrow + Escrow, + EnterpriseFeeCollector } = customAddresses[network] configAddresses = { nftFactoryAddress: ERC721Factory, @@ -237,6 +238,7 @@ export class ConfigHelper { veFeeEstimate, accessListFactory: AccessListFactory, escrow: Escrow, + EnterpriseFeeCollector, ...getUris() } } else if ((DefaultContractsAddresses as { [key: string]: any })[network]) { @@ -258,7 +260,8 @@ export class ConfigHelper { veFeeEstimate, Router, AccessListFactory, - Escrow + Escrow, + EnterpriseFeeCollector } = (DefaultContractsAddresses as { [key: string]: any })[network] configAddresses = { nftFactoryAddress: ERC721Factory, @@ -279,6 +282,7 @@ export class ConfigHelper { veFeeEstimate, accessListFactory: AccessListFactory, escrow: Escrow, + EnterpriseFeeCollector, ...getUris() } } diff --git a/src/contracts/EnterpriseFeeCollector.ts b/src/contracts/EnterpriseFeeCollector.ts new file mode 100644 index 000000000..df299178b --- /dev/null +++ b/src/contracts/EnterpriseFeeCollector.ts @@ -0,0 +1,60 @@ +import { Signer } from 'ethers' +import ContractABI from '@oceanprotocol/contracts/artifacts/contracts/communityFee/EnterpriseFeeCollector.sol/EnterpriseFeeCollector.json' +import { AbiItem } from '../@types' +import { Config } from '../config' +import { SmartContractWithAddress } from './SmartContractWithAddress' + +export class EnterpriseFeeCollectorContract extends SmartContractWithAddress { + public abi: AbiItem[] + + getDefaultAbi() { + return ContractABI.abi as AbiItem[] + } + + /** + * Instantiate EnterpriseFeeCollectorContract class + * @param {string} address The contract address. + * @param {Signer} signer The signer object. + * @param {string | number} [network] Network id or name + * @param {Config} [config] The configuration object. + * @param {AbiItem[]} [abi] ABI array of the smart contract + */ + constructor( + address: string, + signer: Signer, + network?: string | number, + config?: Config, + abi?: AbiItem[] + ) { + super(address, signer, network, config, abi) + this.abi = abi || this.getDefaultAbi() + } + + /** + * Check if token is allowed + * @return {Promise} Boolean indicating if token is allowed + */ + public async isTokenAllowed(token: string): Promise { + return await this.contract.isTokenAllowed(token) + } + + /** + * Get Token details + * @return {Promise} Token details + */ + public async getToken(token: string): Promise { + return await this.contract.getToken(token) + } + + /** + * Calculate fee + * @param {string} token Token address + * @param {string} amount Amount + * @return {Promise} Fee amount + */ + public async calculateFee(token: string, amount: number): Promise { + const weiAmount = this.amountToUnits(token, amount.toString()) + const amountWithFee = await this.contract.calculateFee(token, weiAmount) + return this.unitsToAmount(token, amountWithFee) + } +} diff --git a/src/services/Provider.ts b/src/services/Provider.ts index c7e2e1a89..5610e4b0a 100644 --- a/src/services/Provider.ts +++ b/src/services/Provider.ts @@ -149,8 +149,13 @@ export class Provider { // const isMetaMask = web3 && web3.currentProvider && (web3.currentProvider as any).isMetaMask // if (isMetaMask) return await web3.eth.personal.sign(consumerMessage, accountId, password) // await web3.eth.sign(consumerMessage, await signer.getAddress()) - const consumerMessage = ethers.keccak256(toUtf8Bytes(message)) - const messageHashBytes = ethers.getBytes(consumerMessage) + // const consumerMessage = ethers.keccak256(toUtf8Bytes(message)) + // const messageHashBytes = ethers.getBytes(consumerMessage) + const consumerMessage = ethers.solidityPackedKeccak256( + ['bytes'], + [ethers.hexlify(ethers.toUtf8Bytes(message))] + ) + const messageHashBytes = ethers.toBeArray(consumerMessage) try { return await signer.signMessage(messageHashBytes) } catch (error) { @@ -817,10 +822,7 @@ export class Provider { serviceEndpoints )) + 1 ).toString() - - let signatureMessage = consumerAddress - signatureMessage += datasets[0]?.documentId - signatureMessage += nonce + const signatureMessage = String(consumerAddress + datasets[0]?.documentId + nonce) const signature = await this.getSignature(signerOrAuthToken, signatureMessage) const payload = Object() payload.consumerAddress = consumerAddress diff --git a/test/integration/ComputeFlow.test.ts b/test/integration/ComputeFlow.test.ts index 77e2ce887..d630016d0 100644 --- a/test/integration/ComputeFlow.test.ts +++ b/test/integration/ComputeFlow.test.ts @@ -10,7 +10,8 @@ import { sendTx, amountToUnits, isDefined, - unitsToAmount + unitsToAmount, + sleep } from '../../src/index.js' import { ComputeJob, @@ -545,7 +546,9 @@ describe('Compute flow tests', async () => { } catch (e) { assert( e.message === - `ComputeJob cannot be initialized: Error: Not enough cpu resources. Requested 5, but max is 4.` + `ComputeJob cannot be initialized: Error: Not enough cpu resources. Requested ${ + computeEnv.resources[0].max + 1 + }, but max is ${computeEnv.resources[0].max}.` ) } }) @@ -708,7 +711,7 @@ describe('Compute flow tests', async () => { paidComputeJobId = computeJobs[0].jobId }) - delay(100000) + delay(5000) it('Check compute status', async () => { const jobStatus = (await ProviderInstance.computeStatus( @@ -720,6 +723,8 @@ describe('Compute flow tests', async () => { assert(jobStatus, 'Cannot retrieve compute status!') }) + delay(2000) + it('should restart a computeJob on paid resources, by paying only escrow lock for max job duration, because orders for assets are valid and providerFees are still valid', async () => { const { chainId } = await consumerAccount.provider.getNetwork() // we choose the paid env @@ -806,8 +811,11 @@ describe('Compute flow tests', async () => { }) // move to reuse Orders - - delay(180000) + const delayTimeout = Math.max( + resolvedDdoWith2mTimeout.services[0].timeout * 1000 + 1000, + resolvedAlgoDdoWith2mTimeout.services[0].timeout * 1000 + 1000 + ) + delay(delayTimeout) it('should start a computeJob using the paid resources, by paying the assets providerFees (reuseOrder) and paying escrow lock for max job duration', async () => { const { chainId } = await consumerAccount.provider.getNetwork() diff --git a/test/unit/EnterpriseFeeCollector.test.ts b/test/unit/EnterpriseFeeCollector.test.ts new file mode 100644 index 000000000..53f70863d --- /dev/null +++ b/test/unit/EnterpriseFeeCollector.test.ts @@ -0,0 +1,38 @@ +import { assert } from 'chai' +import { provider, getAddresses } from '../config' +import { Signer } from 'ethers' + +import { Datatoken, amountToUnits, unitsToAmount } from '../../src/' +import { EnterpriseFeeCollectorContract } from '../../src/contracts/EnterpriseFeeCollector' +import BigNumber from 'bignumber.js' + +describe('EnterpriseFeeCollector payments flow', () => { + let user1: Signer + let user2: Signer + let EnterpriseFeeCollector: EnterpriseFeeCollectorContract + let addresses + let OCEAN + + before(async () => { + user1 = (await provider.getSigner(3)) as Signer + user2 = (await provider.getSigner(4)) as Signer + + addresses = await getAddresses() + OCEAN = addresses.Ocean + }) + + it('should initialize EnterpriseFeeCollectorContract class', async () => { + const { chainId } = await user2.provider.getNetwork() + EnterpriseFeeCollector = new EnterpriseFeeCollectorContract( + addresses.EnterpriseFeeCollector, + user2, + Number(chainId) + ) + assert(EnterpriseFeeCollector !== null) + }) + + it('Get token', async () => { + const tx = await EnterpriseFeeCollector.getToken(OCEAN) + assert(tx, 'failed to get token') + }) +})