diff --git a/.github/workflows/ci-test-python-sdk.yaml b/.github/workflows/ci-test-python-sdk.yaml index ad3a051ce1..a04ef88e64 100644 --- a/.github/workflows/ci-test-python-sdk.yaml +++ b/.github/workflows/ci-test-python-sdk.yaml @@ -23,12 +23,6 @@ jobs: run: pip install pipenv - name: Python test working-directory: ./packages/sdk/python/human-protocol-sdk - env: - ESCROW_AWS_ACCESS_KEY_ID: ${{ secrets.ESCROW_AWS_ACCESS_KEY_ID }} - ESCROW_AWS_SECRET_ACCESS_KEY: ${{ secrets.ESCROW_AWS_SECRET_ACCESS_KEY }} - ESCROW_RESULTS_AWS_S3_ACCESS_KEY_ID: ${{ secrets.ESCROW_RESULTS_AWS_S3_ACCESS_KEY_ID }} - ESCROW_RESULTS_AWS_S3_SECRET_ACCESS_KEY: ${{ secrets.ESCROW_RESULTS_AWS_S3_SECRET_ACCESS_KEY }} - ESCROW_ENDPOINT_URL: https://storage.googleapis.com run: | pipenv install --dev make run-test diff --git a/CONTRACTS_LIST.md b/CONTRACTS_LIST.md index ab80afb809..527c3744a0 100644 --- a/CONTRACTS_LIST.md +++ b/CONTRACTS_LIST.md @@ -91,6 +91,14 @@ |2022/10/24 | EthKVStore | 0xd232c1426CF0653cE8a71DC98bCfDf10c471c114 | N/A | | | Reputation | | | +|🟢 SKALE Human Protocol Chain (Mainnet) | Contract | Address | Proxy | +|----------------------------|----------------|--------------------------------------------|--------------------------------------------| +|2023/01/18 | HMToken | 0xa91B2C7d9704aeE8918460fc4375866e2c415A67 | N/A | +|2023/02/28 | EscrowFactory | 0x6FE287F707cfAd44a135d6d392F5098Dbe09e5FF | 0x27B423cE73d1dBdB48d2dd351398b5Ce8223117c | +|2023/02/28 | Staking | 0x1D9f5Dc3078e9F734E4749f98176C2370516447f | 0xcc98Ad1C0915e271650e43714B20272AC947Ba9A | +|2023/02/28 | RewardPool | 0xca8745D76588C2aa8bA13E8ef14E986b8A74EE3E | 0xEc321ec45dDfBE9049550d461818f4E7759bBE89 | +|2023/02/28 | EthKVStore | 0xb251C9F9276d9EB0B2F4C6a7703AE094e0999BB6 | N/A | +| | Reputation | N/A | N/A | diff --git a/README.md b/README.md index 2c12467e08..81b1f1ab04 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,15 @@ -[![Lint Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-lint.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-lint.yaml) +

+

-[![Protocol Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-core.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-core.yaml) +| | | | | +| --- | --- | --- | --- | +| [![Lint Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-lint.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-lint.yaml) | [![Protocol Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-core.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-core.yaml) | [![Python SDK Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-python-sdk.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-python-sdk.yaml) | [![Python Base Models Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-python-basemodels.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-python-basemodels.yaml) | +| [![Node.js SDK Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-node-sdk.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-node-sdk.yaml) | [![Node.js Base Models Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-node-basemodels.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-node-basemodels.yaml) | [![Subgraph Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-subgraph.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-subgraph.yaml) | [![Fortune Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-fortune.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-fortune.yaml) | +| [![Escrow Dashboard Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-escrow-dashboard.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-escrow-dashboard.yaml) | | | | +| [![Core NPM Publish](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-core.yaml/badge.svg?event=release)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-core.yaml) | [![Python SDK Publish](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-python-sdk.yaml/badge.svg?event=release)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-python-sdk.yaml) | [![Python Base Models Publish](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-python-basemodels.yaml/badge.svg?event=release)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-python-basemodels.yaml) | [![Node.js SDK Publish](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-node-sdk.yaml/badge.svg?event=release)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-node-sdk.yaml) | +| [![Node.js Base Models Publish](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-node-basemodels.yaml/badge.svg?event=release)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-node-basemodels.yaml) | [![Subgraph Deploy](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-subgraph.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-subgraph.yaml) | | | -[![Python SDK Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-python-sdk.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-python-sdk.yaml) - -[![Python Base Models Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-python-basemodels.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-python-basemodels.yaml) - -[![Node.js SDK Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-node-sdk.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-node-sdk.yaml) - -[![Node.js Base Models Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-node-basemodels.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-node-basemodels.yaml) - -[![Subgraph Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-subgraph.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-subgraph.yaml) - -[![Fortune Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-fortune.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-fortune.yaml) - -[![Escrow Dashboard Check](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-escrow-dashboard.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/ci-test-escrow-dashboard.yaml) - -[![Core NPM Publish](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-core.yaml/badge.svg?event=release)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-core.yaml) - -[![Python SDK Publish](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-python-sdk.yaml/badge.svg?event=release)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-python-sdk.yaml) - -[![Python Base Models Publish](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-python-basemodels.yaml/badge.svg?event=release)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-python-basemodels.yaml) - -[![Node.js SDK Publish](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-node-sdk.yaml/badge.svg?event=release)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-node-sdk.yaml) - -[![Node.js Base Models Publish](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-node-basemodels.yaml/badge.svg?event=release)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-node-basemodels.yaml) - -[![Subgraph Deploy](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-subgraph.yaml/badge.svg?branch=main)](https://github.com/humanprotocol/human-protocol/actions/workflows/cd-subgraph.yaml) - -![HUMAN-LOGO](https://user-images.githubusercontent.com/104898604/201488028-2b0f29cb-c620-484f-991f-4a8b16efd7cc.png) - - -

All work on-chain

+## All work on-chain Tokenized, verified, rewarded. @@ -41,60 +19,61 @@ Join us on [Discord](http://hmt.ai/discord) HUMAN is a permissionless protocol to facilitate the exchange of HUMAN work, knowledge, and contribution. Using HUMAN, individuals, organizations or businesses can either create or complete tasks. These are tasks that cannot typically be automated or completed by a machine. The types of work that are currently being completed using the HUMAN Protocol are: - - * [Data labeling](https://app.humanprotocol.org/) - HUMAN is currently being used to label raw image data which can subsequently be used to train Machine Learning algorithms. Last month over 20 Million images were labeled by HUMAN workers: [HUMAN Escrow Scanner](https://dashboard.humanprotocol.org/) * [IMOO](https://www.humanprotocol.org/imoo) - An on-chain oracle for decentralized prediction markets * [POH](https://www.humanprotocol.org/proof-of-humanity) - A system that brings bot-blocking applications on-chain * … -**Documentation** +### Documentation For a more detailed description of the HUMAN Protocol architecture and vision see [here](https://github.com/humanprotocol/.github/wiki) -**Description** +### Description As part of our efforts to increase open source contributions we have consolidated all our codebase into a single monorepo. This monorepo provides an easy and reliable way to build applications that interact with the HUMAN Protocol. It has been designed so that it can be extended to meet the requirements of a wide variety of blockchain application use-cases involving human work or contribution. We have also included various example applications and reference implementations for the core infrastructure components that make up the HUMAN Protocol. - -**Contributing to this repository** +### Contributing to this repository The contribution guidelines are as per the CONTRIBUTING.MD file. -**Project Structure** +### Project Structure -``` -~~ Main ~~ +```raw ├── packages │ ├── apps - ├── escrow-dashboard: A UI that queries The Graph for escrow data -│ ├── core: EVM compatible smart contracts for HUMAN +│ │ ├── escrow-dashboard # A UI that queries The Graph for escrow data +│ ├── core # EVM compatible smart contracts for HUMAN │ ├── examples - ├── cvat: An open source annotation tool for labeling video and images - ├── eth-kvstore: An on-chain key value store for publishing and rotating public keys - ├── fortune: An example application that combines all the core - components of HUMAN - ├── launcher: A reference implementation of a Job Launcher - ├── oracles: Reference implementations for Recording and Reputation - Oracles -│ ├── sdk: Python and Typescript libraries for building applications on - HUMAN +│ │ ├── cvat # An open source annotation tool for labeling video and images +│ │ ├── eth-kvstore-gui # An on-chain key value store for publishing and rotating public keys +│ │ ├── fortune # An example application that combines all the core +│ │ │ components of HUMAN +│ │ ├── launcher # A reference implementation of a Job Launcher +│ │ ├── oracles # Reference implementations for Recording and Reputation oracles +│ ├── sdk +│ │ ├── json-schema # JSON schema for basemodels, +│ │ │ which is auto-generated from pydantic models +│ │ ├── python +│ │ │ ├── human-protocol-basemodels # Python library for basemodels +│ │ │ ├── human-protocol-sdk # Python SDK to interact with Human Protocol +│ │ ├── typescript +│ │ │ ├── human-protocol-basemodels # Node.js library for basemodels +│ │ │ ├── human-protocol-sdk # Node.js SDK to interact with Human Protocol +│ │ │ ├── subgraph # Human Protocol Subgraph ``` - -**How To Use This Repo** +### How To Use This Repo If you would like to join the HUMAN network as an operator please see the [examples](https://github.com/humanprotocol/human-protocol/tree/main/packages/examples) folder. Users may participate as any of the following roles: -[Job Launcher Operator](https://github.com/humanprotocol/human-protocol/tree/main/packages/examples/launcher) \ -[Recording Oracle Operator](https://github.com/humanprotocol/human-protocol/tree/main/packages/examples/oracles/recording) - -Exchange Oracle Operator +* [Job Launcher Operator](https://github.com/humanprotocol/human-protocol/tree/main/packages/examples/launcher) +* [Recording Oracle Operator](https://github.com/humanprotocol/human-protocol/tree/main/packages/examples/oracles/recording) +* Exchange Oracle Operator -**Building New Applications for HUMAN** +#### Building New Applications for HUMAN If you are a developer and would like to build on top of HUMAN please see [examples](https://github.com/humanprotocol/human-protocol/tree/main/packages/examples) and [sdk](https://github.com/humanprotocol/human-protocol/tree/main/packages/sdk) folders. -**Usage and Installation** +#### Usage and Installation Navigate to the folder that you would like to install and follow the instructions in the README file diff --git a/packages/apps/escrow-dashboard/src/constants/index.ts b/packages/apps/escrow-dashboard/src/constants/index.ts index fa9c25f77e..b7aff58669 100644 --- a/packages/apps/escrow-dashboard/src/constants/index.ts +++ b/packages/apps/escrow-dashboard/src/constants/index.ts @@ -11,6 +11,27 @@ import { avalanche, } from 'wagmi/chains'; +const wagmiSkaleHP = { + id: 1273227453, + name: 'Skale Human Protocol chain', + network: 'skale', + nativeCurrency: { + decimals: 18, + name: 'Skale FUEL', + symbol: 'sFUEL', + }, + rpcUrls: { + public: { http: ['https://mainnet.skalenodes.com/v1/wan-red-ain'] }, + default: { http: ['https://mainnet.skalenodes.com/v1/wan-red-ain'] }, + }, + blockExplorers: { + default: { + name: 'Skale Explorer', + url: 'https://mainnet.skalenodes.com/v1/wan-red-ain', + }, + }, +} as Chain; + export enum ChainId { ALL = -1, MAINNET = 1, @@ -24,12 +45,14 @@ export enum ChainId { MOONBASE_ALPHA = 1287, AVALANCHE_TESTNET = 43113, AVALANCHE = 43114, + SKALE = 1273227453, } export const HMT_ADDRESSES: { [chainId in ChainId]?: string } = { [ChainId.MAINNET]: '0xd1ba9BAC957322D6e8c07a160a3A8dA11A0d2867', [ChainId.POLYGON]: '0xc748b2a084f8efc47e086ccddd9b7e67aeb571bf', [ChainId.AVALANCHE]: '0x12365293cb6477d4fc2686e46BB97E3Fb64f1550', + [ChainId.SKALE]: '0xa91B2C7d9704aeE8918460fc4375866e2c415A67', }; export interface IEscrowNetwork { @@ -58,6 +81,7 @@ export const SUPPORTED_CHAIN_IDS = [ ChainId.MOONBASE_ALPHA, ChainId.AVALANCHE_TESTNET, ChainId.AVALANCHE, + ChainId.SKALE, ]; export const TESTNET_CHAIN_IDS = [ @@ -216,6 +240,21 @@ export const ESCROW_NETWORKS: { kvstoreAddress: '0x4B79eaD28F52eD5686bf0e379717e85fc7aD10Df', wagmiChain: avalanche, }, + [ChainId.SKALE]: { + chainId: ChainId.SKALE, + title: 'SKALE Human Protocol Chain', + scanUrl: 'https://wan-red-ain.explorer.mainnet.skalenodes.com/', + rpcUrl: 'https://mainnet.skalenodes.com/v1/wan-red-ain', + // Subgraph hasn't been implemented yet + subgraphUrl: 'https://api.thegraph.com/', + factoryAddress: '0x27B423cE73d1dBdB48d2dd351398b5Ce8223117c', + hmtAddress: '0xa91B2C7d9704aeE8918460fc4375866e2c415A67', + oldSubgraphUrl: '', + oldFactoryAddress: '0x1cE8d1820D60fF792bd6E59EbFf3C9b1089808c0', + kvstoreAddress: '0xb251C9F9276d9EB0B2F4C6a7703AE094e0999BB6', + // Add custom wagmi + wagmiChain: wagmiSkaleHP, + }, }; export const FAST_INTERVAL = 10_000; diff --git a/packages/core/hardhat.config.ts b/packages/core/hardhat.config.ts index 56d0098670..fa01efe60a 100644 --- a/packages/core/hardhat.config.ts +++ b/packages/core/hardhat.config.ts @@ -123,6 +123,13 @@ const config: HardhatUserConfig = { accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], }, + + skale: { + chainId: 1273227453, + url: process.env.ETH_SKALE_URL || '', + accounts: + process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], + }, }, gasReporter: { enabled: process.env.REPORT_GAS !== undefined, @@ -156,7 +163,18 @@ const config: HardhatUserConfig = { bscTestnet: process.env.BSC_API_KEY || '', moonbeam: process.env.MOONSCAN_API_KEY || '', moonbaseAlpha: process.env.MOONSCAN_API_KEY || '', + skale: process.env.SKALE_API_KEY || '', }, + customChains: [ + { + network: 'skale', + chainId: 1273227453, + urls: { + apiURL: process.env.SKALE_BROWSER_API_URL || '', + browserURL: process.env.SKALE_BROWSER_URL || '', + }, + }, + ], }, mocha: { timeout: 200000, diff --git a/packages/examples/fortune/launcher/client/package.json b/packages/examples/fortune/launcher/client/package.json index 3101d65d18..a25473ecb6 100644 --- a/packages/examples/fortune/launcher/client/package.json +++ b/packages/examples/fortune/launcher/client/package.json @@ -7,6 +7,8 @@ "@emotion/styled": "^11.10.5", "@human-protocol/core": "workspace:*", "@mui/material": "^5.11.7", + "@stripe/react-stripe-js": "^1.16.4", + "@stripe/stripe-js": "^1.46.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/packages/examples/fortune/launcher/client/src/App.tsx b/packages/examples/fortune/launcher/client/src/App.tsx index ca800ae7ec..e7ef22ac32 100644 --- a/packages/examples/fortune/launcher/client/src/App.tsx +++ b/packages/examples/fortune/launcher/client/src/App.tsx @@ -7,6 +7,7 @@ import { FortuneStages, FortuneFundingMethod, FortuneJobRequest, + FortuneFiatJobRequest, FortuneLaunch, FortuneLaunchSuccess, FortuneLaunchFail, @@ -32,6 +33,7 @@ function App() { escrowAddress: '', exchangeUrl: '', }); + const [errorMessage, setErrorMessage] = useState(''); const handleChangeFundingMethod = (method: FundingMethodType) => { setFundingMethod(method); @@ -39,8 +41,7 @@ function App() { }; const handleBack = () => { - setFundingMethod('crypto'); - setStatus(FortuneStageStatus.JOB_REQUEST); + setStatus(status > 0 ? status - 1 : 0); }; const handleOnSuccess = (data: JobLaunchResponse) => { @@ -53,6 +54,11 @@ function App() { setStatus(FortuneStageStatus.FUNDING_METHOD); }; + const handleOnError = (message: string) => { + setErrorMessage(message); + setStatus(FortuneStageStatus.LAUNCH_FAIL); + }; + const fetchLastEscrow = async (factoryAddress: string | undefined) => { if (factoryAddress && signer) { const contract = new ethers.Contract( @@ -127,15 +133,26 @@ function App() { {status === FortuneStageStatus.FUNDING_METHOD && ( )} - {status === FortuneStageStatus.JOB_REQUEST && ( - setStatus(FortuneStageStatus.LAUNCH)} - onSuccess={handleOnSuccess} - onFail={() => setStatus(FortuneStageStatus.LAUNCH_FAIL)} - /> - )} + {status === FortuneStageStatus.JOB_REQUEST && + fundingMethod === 'crypto' && ( + setStatus(FortuneStageStatus.LAUNCH)} + onSuccess={handleOnSuccess} + onFail={handleOnError} + /> + )} + {status === FortuneStageStatus.JOB_REQUEST && + fundingMethod === 'fiat' && ( + setStatus(FortuneStageStatus.LAUNCH)} + onSuccess={handleOnSuccess} + onFail={handleOnError} + /> + )} {status === FortuneStageStatus.LAUNCH && } {status === FortuneStageStatus.LAUNCH_SUCCESS && ( setStatus(FortuneStageStatus.JOB_REQUEST)} /> )} - - Last Escrow: {lastEscrowAddress} - + {lastEscrowAddress && ( + + Last Escrow: {lastEscrowAddress} + + )} diff --git a/packages/examples/fortune/launcher/client/src/components/FiatJobRequest.tsx b/packages/examples/fortune/launcher/client/src/components/FiatJobRequest.tsx new file mode 100644 index 0000000000..67c8493f5d --- /dev/null +++ b/packages/examples/fortune/launcher/client/src/components/FiatJobRequest.tsx @@ -0,0 +1,377 @@ +import HMTokenABI from '@human-protocol/core/abis/HMToken.json'; +import { + Box, + Button, + CircularProgress, + FormControl, + Grid, + InputLabel, + MenuItem, + Select, + TextField, + Typography, +} from '@mui/material'; +import { CardElement, useElements, useStripe } from '@stripe/react-stripe-js'; +import axios from 'axios'; +import { ethers } from 'ethers'; +import { useEffect, useState } from 'react'; +import { + ChainId, + Currencies, + ESCROW_NETWORKS, + HM_TOKEN_DECIMALS, + SUPPORTED_CHAIN_IDS, +} from 'src/constants'; +import { RoundedBox } from './RoundedBox'; +import { + CreatePaymentType, + FortuneJobRequestType, + FundingMethodType, + JobLaunchResponse, +} from './types'; + +type JobRequestProps = { + fundingMethod: FundingMethodType; + onBack: () => void; + onLaunch: () => void; + onSuccess: (response: JobLaunchResponse) => void; + onFail: (message: string) => void; +}; + +export const JobRequest = ({ + fundingMethod, + onBack, + onLaunch, + onSuccess, + onFail, +}: JobRequestProps) => { + const stripe = useStripe(); + const elements = useElements(); + const [jobRequest, setJobRequest] = useState({ + chainId: SUPPORTED_CHAIN_IDS.includes(ChainId.LOCALHOST) + ? ChainId.LOCALHOST + : SUPPORTED_CHAIN_IDS.includes(ChainId.POLYGON_MUMBAI) + ? ChainId.POLYGON_MUMBAI + : SUPPORTED_CHAIN_IDS[0], + title: '', + description: '', + fortunesRequired: '', + token: '', + fundAmount: '', + jobRequester: '', + }); + const [provider, setProvider] = useState(); + const [paymentData, setPaymentData] = useState({ + amount: '', + currency: 'USD', + name: '', + }); + const [isLoading, setIsLoading] = useState(false); + const [tokenAmount, setTokenAmount] = useState(0); + + const handleJobRequestFormFieldChange = ( + fieldName: string, + fieldValue: any + ) => { + const regex = /^[0-9\b]+$/; + if (fieldName !== 'fortunesRequired') { + setJobRequest({ ...jobRequest, [fieldName]: fieldValue }); + } else if (regex.test(fieldValue) || fieldValue === '') { + setJobRequest({ ...jobRequest, [fieldName]: fieldValue }); + } + }; + + const handlePaymentDataFormFieldChange = ( + fieldName: string, + fieldValue: any + ) => { + setPaymentData({ ...paymentData, [fieldName]: fieldValue }); + }; + + useEffect(() => { + const getHMTPrice = async () => { + const currentPrice = ( + await axios.get( + `https://api.coingecko.com/api/v3/simple/price?ids=human-protocol&vs_currencies=${paymentData.currency.toLowerCase()}` + ) + ).data['human-protocol'][paymentData.currency.toLowerCase()]; + setTokenAmount(Number(paymentData.amount) / currentPrice); + }; + getHMTPrice(); + }, [paymentData.amount, paymentData.currency]); + + useEffect(() => { + setProvider( + new ethers.providers.JsonRpcProvider( + ESCROW_NETWORKS[jobRequest.chainId as ChainId]?.rpcUrl + ) + ); + }, [jobRequest.chainId]); + + const handleLaunch = async () => { + if (!stripe || !elements) { + // Stripe.js has not yet loaded. + // Make sure to disable form submission until Stripe.js has loaded. + alert('Stripe.js has not yet loaded.'); + return; + } + setIsLoading(true); + const data: FortuneJobRequestType = { + ...jobRequest, + fundAmount: tokenAmount.toString(), + token: ESCROW_NETWORKS[jobRequest.chainId as ChainId]?.hmtAddress!, + fiat: true, + }; + try { + const contract = new ethers.Contract(data.token, HMTokenABI, provider); + const jobLauncherAddress = process.env.REACT_APP_JOB_LAUNCHER_ADDRESS; + if (!jobLauncherAddress) { + alert('Job Launcher address is missing'); + setIsLoading(false); + return; + } + const balance = await contract.balanceOf(jobLauncherAddress); + + const fundAmount = ethers.utils.parseUnits( + data.fundAmount, + HM_TOKEN_DECIMALS + ); + if (balance.lt(fundAmount)) { + throw new Error('Balance not enough for funding the escrow'); + } + const baseUrl = process.env.REACT_APP_JOB_LAUNCHER_SERVER_URL; + await axios.post(`${baseUrl}/check-escrow`, data); + + const clientSecret = ( + await axios.post(`${baseUrl}/create-payment-intent`, { + currency: paymentData.currency.toLowerCase(), + amount: (Number(paymentData.amount) * 100).toString(), + }) + )?.data?.clientSecret; + + const card = elements.getElement(CardElement); + if (!card) return; + const { error: stripeError, paymentIntent } = + await stripe.confirmCardPayment(clientSecret, { + payment_method: { + card: card, + billing_details: { + name: paymentData.name, + }, + }, + }); + if (stripeError) throw new Error(stripeError.message); + + onLaunch(); + data.paymentId = paymentIntent?.id; + const result = await axios.post(`${baseUrl}/escrow`, data); + onSuccess(result.data); + } catch (err: any) { + console.error(err); + if (err.name === 'AxiosError') onFail(err.response.data); + else onFail(err.message); + } + + setIsLoading(false); + }; + + return ( + + + Job Details + + + + + Network + + + + + + + handleJobRequestFormFieldChange('title', e.target.value) + } + /> + + + + + + handleJobRequestFormFieldChange( + 'fortunesRequired', + e.target.value + ) + } + /> + + + + + + handleJobRequestFormFieldChange('description', e.target.value) + } + /> + + + + handleJobRequestFormFieldChange('jobRequester', e.target.value) + } + /> + + + + + Funds + + + + Card + + + + + + + + Name on Card + + + + handlePaymentDataFormFieldChange('name', e.target.value) + } + /> + + + + + + + Amount + + + + handlePaymentDataFormFieldChange('amount', e.target.value) + } + /> + + + + + + + Currency + + + + + + + + + + {tokenAmount !== 0 && ( + + Escrow will be funded with {tokenAmount}HMT aprox. + + )} + {tokenAmount === 0 &&

} + + + + +
+ ); +}; diff --git a/packages/examples/fortune/launcher/client/src/components/FundingMethod.tsx b/packages/examples/fortune/launcher/client/src/components/FundingMethod.tsx index 7b00b0ed89..5afa1efadb 100644 --- a/packages/examples/fortune/launcher/client/src/components/FundingMethod.tsx +++ b/packages/examples/fortune/launcher/client/src/components/FundingMethod.tsx @@ -76,9 +76,8 @@ export const FundingMethod = ({ onChange }: FundingMethodProps) => { variant="outlined" sx={{ mt: 2.5, minWidth: '200px' }} onClick={() => onChange('fiat')} - disabled > - Fiat (Coming soon) + Fiat diff --git a/packages/examples/fortune/launcher/client/src/components/JobRequest.tsx b/packages/examples/fortune/launcher/client/src/components/JobRequest.tsx index 1d33dc29ce..24c04306d1 100644 --- a/packages/examples/fortune/launcher/client/src/components/JobRequest.tsx +++ b/packages/examples/fortune/launcher/client/src/components/JobRequest.tsx @@ -13,14 +13,14 @@ import { } from '@mui/material'; import axios from 'axios'; import { ethers } from 'ethers'; +import { useState } from 'react'; +import { useAccount, useChainId, useSigner, useSwitchNetwork } from 'wagmi'; import { - SUPPORTED_CHAIN_IDS, - ESCROW_NETWORKS, ChainId, + ESCROW_NETWORKS, HM_TOKEN_DECIMALS, + SUPPORTED_CHAIN_IDS, } from '../constants'; -import React, { useEffect, useState } from 'react'; -import { useAccount, useChainId, useSigner, useSwitchNetwork } from 'wagmi'; import { RoundedBox } from './RoundedBox'; import { FortuneJobRequestType, @@ -33,7 +33,7 @@ type JobRequestProps = { onBack: () => void; onLaunch: () => void; onSuccess: (response: JobLaunchResponse) => void; - onFail: () => void; + onFail: (message: string) => void; }; export const JobRequest = ({ @@ -96,28 +96,33 @@ export const JobRequest = ({ setIsLoading(false); return; } + const balance = await contract.balanceOf(address); + const fundAmount = ethers.utils.parseUnits( + data.fundAmount, + HM_TOKEN_DECIMALS + ); + if (balance.lt(fundAmount)) { + throw new Error('Balance not enough for funding the escrow'); + } + + const baseUrl = process.env.REACT_APP_JOB_LAUNCHER_SERVER_URL; + await axios.post(`${baseUrl}/check-escrow`, data); + const allowance = await contract.allowance(address, jobLauncherAddress); - if ( - allowance.lt( - ethers.utils.parseUnits(data.fundAmount, HM_TOKEN_DECIMALS) - ) - ) { - const tx = await contract.approve( - jobLauncherAddress, - ethers.utils.parseUnits(data.fundAmount, HM_TOKEN_DECIMALS) - ); + if (allowance.lt(fundAmount)) { + const tx = await contract.approve(jobLauncherAddress, fundAmount); const receipt = await tx.wait(); console.log(receipt); } - const baseUrl = process.env.REACT_APP_JOB_LAUNCHER_SERVER_URL; onLaunch(); const result = await axios.post(`${baseUrl}/escrow`, data); onSuccess(result.data); - } catch (err) { + } catch (err: any) { console.log(err); - onFail(); + if (err.name === 'AxiosError') onFail(err.response.data); + else onFail(err.message); } setIsLoading(false); diff --git a/packages/examples/fortune/launcher/client/src/components/LaunchFail.tsx b/packages/examples/fortune/launcher/client/src/components/LaunchFail.tsx index 046166e1d9..24615f71d5 100644 --- a/packages/examples/fortune/launcher/client/src/components/LaunchFail.tsx +++ b/packages/examples/fortune/launcher/client/src/components/LaunchFail.tsx @@ -3,17 +3,18 @@ import React from 'react'; import { RoundedBox } from './RoundedBox'; type LaunchFailProps = { + message: string; onBack: () => void; }; -export const LaunchFail = ({ onBack }: LaunchFailProps) => { +export const LaunchFail = ({ message, onBack }: LaunchFailProps) => { return ( Fail! - Fail message here. + {message}