diff --git a/submissions/CorGit/.DS_Store b/submissions/CorGit/.DS_Store new file mode 100644 index 0000000..99e9b29 Binary files /dev/null and b/submissions/CorGit/.DS_Store differ diff --git a/submissions/CorGit/About.md b/submissions/CorGit/About.md new file mode 100644 index 0000000..c704535 --- /dev/null +++ b/submissions/CorGit/About.md @@ -0,0 +1,4 @@ +# Team + +1. Antonio (Discord ID: t0n1x#7103) +2. Federico (Discord ID: Frenk_eth#7663) diff --git a/submissions/CorGit/README.md b/submissions/CorGit/README.md new file mode 100644 index 0000000..c363a24 --- /dev/null +++ b/submissions/CorGit/README.md @@ -0,0 +1,53 @@ +# CorGit dApp + +## Overview +Open Source projects have always problems in managing their funds, especially when it comes to proportionally distribute them among previous and future holders. CorGit is an OpenSource Project Tokenization solution that aims to fix this problem. + +Each project can create its own token, and distribute to anyone included within a project. The token can be immediately, or later, collateralized. In this way projects are able to proportionally reward past, present and future contributors, by transparently manage funds collected. + +## See our Demo + +Video has been made connecting to Goerli, but the exact same functionalities are available on ZkSync (See contract addresses and instructions on how to run below) + +[Video](https://youtu.be/kUXQLmH7JBY) + +[Slides](https://drive.google.com/file/d/1QUoel0iwXg0FsOXTrtr1nlGrha1dh2_l/view) + +## Smart Contracts + +Currently our smart contracts have been deployed on ZkSync 2.0 +* GithubAddressRegister - 0xa5B07286eA9a9f7deC44104Cb621f1cf55AA9634 +* ContractTokenFactory - 0x84360Fc81b1be9860A37859Be3b48e505D494F38 + +**GithubAddressRegister** works as an Oracle and creates the match between a Github ID and a wallet address. This is required to claim rewards based on Github pull requests. Only admin can write to that contract, so if a new Github ID needs to be added, please open an issue with githubID + wallet address to associate + +**ContractTokenFactory** is the contract called to deploy a custom Corgit contract. Each project will have its own token. + + +## How to deploy smart contracts + +This step is optional as SCs are already deployed + +Folder `/contracts-corgit-zksync` contains the smart contracts. If you want to deploy them, to create a second deployment, you need to create a file `.secrets.json` (See the `.secrets.example.json`). This file contains the RPC endpoints and the private keys of the wallets. + +Then you need to run + +``` +yarn install +yarn hardhat compile +yarn hardhat deploy-zksync +``` + +Finally, to enable the match between githubID and a wallet address, run `ts-node scripts/associateGithubToWallet.ts` (After having correctly set the variables `GITHUB_ID` and `WALLET_ADDRESS_TO_ASSOCIATE` on top of the same file) + +## How to run dApp + +Enter in folder `/webapp`. To quick run the app just use the following commands + +``` +yarn install +yarn run start +``` + +You will find in the CorGit homepage. If you have deployed a new set of contracts, make sure to update their address in `src/utils/constants.ts` file + diff --git a/submissions/CorGit/code/.gitignore b/submissions/CorGit/code/.gitignore new file mode 100644 index 0000000..ad15f41 --- /dev/null +++ b/submissions/CorGit/code/.gitignore @@ -0,0 +1,4 @@ +node_modules +.idea +yarn.lock +build diff --git a/submissions/CorGit/code/README.md b/submissions/CorGit/code/README.md new file mode 100644 index 0000000..5c1d20e --- /dev/null +++ b/submissions/CorGit/code/README.md @@ -0,0 +1,17 @@ +# CorGit + +Tokenize your OpenSource project, to proportionally reward past, present and future contributors + + + + +# How it works + +By connecting a dedicated CorGit token to a project, contributors of any kind can be rewarded with the project native token. + +cg Tokens (i.e., tokens created with CorGit) have an intrinsic mechanism to preserve value over time, and to incentivize token holdings. + +Whenever funds reaches the project, through grants or other channels, they are used partly to give value +to the tokens owned by current token holders, and partly to mint new tokens. + +Token holders can exchange their tokens for a guaranteed value, directly on our platform, at smart contract level. diff --git a/submissions/CorGit/code/contracts-corgit-zksync/.gitignore b/submissions/CorGit/code/contracts-corgit-zksync/.gitignore new file mode 100644 index 0000000..19839aa --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/.gitignore @@ -0,0 +1,29 @@ +node_modules +.env +coverage +coverage.json +typechain +typechain-types +.idea +yarn.lock + +#Hardhat files +cache +cache-zk +artifacts +artifacts-zk/ + + +node_modules +.env +coverage +coverage.json +typechain +typechain-types + +#Hardhat files +cache +artifacts + +# Deployment secrets +.secrets.json diff --git a/submissions/CorGit/code/contracts-corgit-zksync/.secrets.example.json b/submissions/CorGit/code/contracts-corgit-zksync/.secrets.example.json new file mode 100644 index 0000000..6f77438 --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/.secrets.example.json @@ -0,0 +1,17 @@ +{ + "nodeUrls": { + "goerli": "https://-----------------", + "zksync": "https://zksync2-testnet.zksync.dev" + }, + "privateKeys": { + "hardhat": { + "deployer": "-----------" + }, + "goerli": { + "deployer": "-------------" + }, + "zksync": { + "deployer": "--------------" + } + } +} diff --git a/submissions/CorGit/code/contracts-corgit-zksync/contracts/GithubAddressRegister.sol b/submissions/CorGit/code/contracts-corgit-zksync/contracts/GithubAddressRegister.sol new file mode 100644 index 0000000..efd9699 --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/contracts/GithubAddressRegister.sol @@ -0,0 +1,60 @@ +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts/access/Ownable.sol"; + +contract GithubAddressRegister is Ownable { + + // mappings + /// @dev stores the match between github ID and chain address. One github ID can have more than one chain address + mapping(uint256 => address[]) public githubIDToAddress; + /// @dev stores teh match between an address and a github ID. One address can have just one github ID connected + mapping(address => uint256) public addressToGithubID; + + constructor() { + + } + + /** + * @notice Adds a new address to the list of github addresses. + * @dev Currently performed by the backend after a succesfull github login. + Should be implemented with call by the user, that publishes a sign (approval) sent by our authorized App. + In addition, it can be implemented via ChainLink oracle + * @param _githubID - id of github account + * @param _wallet - wallet address + **/ + function addAddress( + uint256 _githubID, + address _wallet + ) external onlyOwner { + require(addressToGithubID[_wallet] == 0, "Wallet already assigned to GithubID"); + githubIDToAddress[_githubID].push(_wallet); + addressToGithubID[_wallet] = _githubID; + } + + /** + * @notice Removes an address from the list of authorized addresses. + * @dev Only the wallet owner can remove it, calling from one of the wallets registered under that githubID + * @param _githubID - id of github account + * @param _addressToRemove - address to be removed + **/ + function removeAddress(uint256 _githubID, address _addressToRemove) public { + bool validCaller = false; + bool validAddress = false; + uint indexToRemove = 0; + address[] memory addressList = githubIDToAddress[_githubID]; + for(uint i=0; i Payment) public payments; + /// @dev list of pending payments, connected to given githubID - (paymentID =>(githubID => SinglePayment)) + mapping(uint256 => mapping(uint256 => SinglePayment)) public paymentAmounts; + /// @dev list of all the payments a user has ever received + mapping(uint256 => uint256[]) public userPayments; + + // bytes32 + /// @dev role necessary to pay (create rewards) + bytes32 public constant PAYER_ROLE = keccak256("PAYER"); + + // address + GithubAddressRegister githubAddressRegister; + + event NewGroupPayment(uint indexed _id, uint256 _numOfUsers); + event PaymentPending(uint256 indexed _githubID, uint256 _amount); + event PaymentClaimed(uint256 indexed _githubID, uint256 indexed _paymentId, uint256 _amount); + + constructor( + string memory _name, + string memory _symbol, + uint _initialSupply, + uint16 _percFundingDistribute, + address _githubAddressRegister, + address creator + ) ERC20(_name, _symbol) { + require(_percFundingDistribute >= MIN_PERC_FUNDING_DISTRIBUTED, "Invalid _percFundingDistribute"); + percFundingDistributed = _percFundingDistribute; + percFundingForNewTokens = 100 - _percFundingDistribute; + githubAddressRegister = GithubAddressRegister(_githubAddressRegister); + _mint(address(this), _initialSupply); + _setupRole(DEFAULT_ADMIN_ROLE, creator); + _setupRole(PAYER_ROLE, creator); + } + + /** + * Allow the payment to the given githubID for the specified amount + **/ + function pay(uint256[] calldata _githubID, uint256[] calldata _amount, string calldata _name) public onlyRole(PAYER_ROLE) { + require(_githubID.length == _amount.length, "Arrays must have equal length"); + + uint totalAmount = 0; + uint tokensAvailable = balanceOf(address(this)) - lockedTokensForPayments; + + // add the payment details + for (uint i=0; i<_githubID.length; ++i){ + // record the amount paid, and make sure the amount is available + totalAmount += _amount[i]; + require(totalAmount <= tokensAvailable, "Not enough tokens for payment"); + // add SinglePayment detalils + paymentAmounts[nextPaymentId][_githubID[i]] = SinglePayment({ + amount: _amount[i], + paid: false + }); + // add the id of the payment to the list + userPayments[ _githubID[i] ].push(nextPaymentId); + // emit the event for the specific user + emit PaymentPending(_githubID[i], _amount[i]); + } + + // save the payment details + payments[nextPaymentId] = Payment({ + creation: block.timestamp, + totalTokenAmount: totalAmount, + totalTokenClaimed: 0, + name: _name, + numOfUsers: _githubID.length, + claimCompleted: false + }); + + emit NewGroupPayment(nextPaymentId, _githubID.length); + + // update variables for next payments + lockedTokensForPayments += totalAmount; + nextPaymentId++; + } + + /** + * Collect the payments for a user + **/ + function collectPayment(address _to, uint _paymentId) public { + uint githubId = githubAddressRegister.addressToGithubID(_to); + require(githubId != 0, "Unregistered address"); + + uint amount = paymentAmounts[_paymentId][githubId].amount; + require(!paymentAmounts[_paymentId][githubId].paid + && amount > 0, "No amount or claimed"); + _transfer(address(this), _to, amount); + paymentAmounts[_paymentId][githubId].paid = true; + payments[_paymentId].totalTokenClaimed += amount; + if (payments[_paymentId].totalTokenClaimed == payments[_paymentId].totalTokenAmount) + payments[_paymentId].claimCompleted = true; + + lockedTokensForPayments -= amount; + emit PaymentClaimed(githubId, _paymentId, amount); + } + + /** + * A cg Token holder can collect its underlying assets, by burning _amount of tokens. + * Payment is returned in proportion in WETH and DAI, and is sent to the + * selected wallet among the allowed one from GithubAddressRegister SC + **/ + function convert(uint256 _amount, address _to) public { + + } + + /** + * Call this function with the amount of weth and dai to transfer to this contract. + * Make sure to have the right allowance + **/ + function contribute() public payable { + require(msg.value>0, "No contribution provided"); + require(percFundingDistributed>0, "Invalud percFundingDistributed"); + uint amountReceived = msg.value; + uint amountToRedistribute = amountReceived * percFundingDistributed / 100; + uint amountToNewMint = amountReceived - amountToRedistribute; + uint balanceBeforeReceive = address(this).balance - msg.value; + uint newTokens = (amountToNewMint * totalSupply()) / ( balanceBeforeReceive + amountToRedistribute ); + _mint(address(this), newTokens); + } + + + +} diff --git a/submissions/CorGit/code/contracts-corgit-zksync/deploy/deploy_zk.ts b/submissions/CorGit/code/contracts-corgit-zksync/deploy/deploy_zk.ts new file mode 100644 index 0000000..edcda32 --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/deploy/deploy_zk.ts @@ -0,0 +1,37 @@ +import {ethers} from "hardhat"; +import {CgFactory, GithubAddressRegister} from "../typechain-types"; +import {deployGithubAddressRegister, zkDeployGithubAddressRegister} from "../scripts/Deployer/SingleContracts/GithubAddressRegister"; +import {deployCgFactory, zkDeployCgFactory} from "../scripts/Deployer/SingleContracts/cgFactory"; +import {Provider, Wallet} from "zksync-web3"; +import secrets from "../.secrets.json"; +import {HardhatRuntimeEnvironment} from "hardhat/types"; +import {Deployer} from "@matterlabs/hardhat-zksync-deploy"; + +/** + * Function to deploy all the contracts on a new chain. Must call this file with command `yarn hardhat deploy-zksync` + * + * @param hre {HardhatRuntimeEnvironment} - the hardhat runtime enviroment wrapped + */ +const deploy = async ( + hre: HardhatRuntimeEnvironment +): Promise<{ + cgFactory: CgFactory, + githubAddressRegister: GithubAddressRegister +}> => { + + const provider = new Provider(hre.userConfig.zkSyncDeploy?.zkSyncNetwork); + const wallet = new Wallet(secrets.privateKeys.zksync.deployer); + const deployer = new Deployer(hre, wallet); + + + // Deploy all the smart contracts + const githubAddressRegister = await zkDeployGithubAddressRegister(deployer); + console.log("githubAddressRegister deployed - " + githubAddressRegister.address); + + const cgFactory = await zkDeployCgFactory(deployer, githubAddressRegister.address); + console.log("cgFactory deployed - " + cgFactory.address); + + return { githubAddressRegister: githubAddressRegister, cgFactory } +} + +export default deploy; diff --git a/submissions/CorGit/code/contracts-corgit-zksync/hardhat.config.ts b/submissions/CorGit/code/contracts-corgit-zksync/hardhat.config.ts new file mode 100644 index 0000000..5dbb917 --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/hardhat.config.ts @@ -0,0 +1,44 @@ +import { HardhatUserConfig } from "hardhat/config"; +import "@nomicfoundation/hardhat-toolbox"; +import "@matterlabs/hardhat-zksync-deploy"; +import "@matterlabs/hardhat-zksync-solc"; + +import secrets from './.secrets.json'; + +/** + * @type import('hardhat/config').HardhatUserConfig + */ +const config: HardhatUserConfig = { + zksolc: { + version: "1.2.0", + compilerSource: "binary", + settings: { + optimizer: { + enabled: true, + }, + experimental: { + dockerImage: "matterlabs/zksolc", + tag: "v1.2.0", + }, + }, + }, + zkSyncDeploy: { + zkSyncNetwork: "https://zksync2-testnet.zksync.dev", + ethNetwork: secrets.nodeUrls.goerli + }, + solidity: { + version: "0.8.17", + settings: { + viaIR: false, + } + }, + networks: { + hardhat: { + allowUnlimitedContractSize: true, + chainId: 1337, + zksync: true + } + } +}; + +export default config; diff --git a/submissions/CorGit/code/contracts-corgit-zksync/package.json b/submissions/CorGit/code/contracts-corgit-zksync/package.json new file mode 100644 index 0000000..1392d88 --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/package.json @@ -0,0 +1,28 @@ +{ + "name": "contracts-corgit-zksync", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "devDependencies": { + "@matterlabs/hardhat-zksync-deploy": "^0.5.2", + "@matterlabs/hardhat-zksync-solc": "^0.3.10", + "@nomicfoundation/hardhat-chai-matchers": "^1.0.4", + "@nomicfoundation/hardhat-toolbox": "^2.0.0", + "@nomiclabs/hardhat-ethers": "^2.2.1", + "@nomiclabs/hardhat-etherscan": "^3.1.2", + "@typechain/ethers-v5": "^10.1.0", + "@typechain/hardhat": "^6.1.3", + "chai": "^4.3.6", + "ethers": "^5.7.2", + "hardhat": "^2.12.1", + "hardhat-gas-reporter": "^1.0.9", + "solidity-coverage": "^0.8.2", + "ts-node": "^10.9.1", + "typechain": "^8.1.0", + "typescript": "^4.8.4", + "zksync-web3": "^0.11.1" + }, + "dependencies": { + "@openzeppelin/contracts": "^4.7.3" + } +} diff --git a/submissions/CorGit/code/contracts-corgit-zksync/scripts/Deployer/SingleContracts/GithubAddressRegister.ts b/submissions/CorGit/code/contracts-corgit-zksync/scripts/Deployer/SingleContracts/GithubAddressRegister.ts new file mode 100644 index 0000000..ef68bc8 --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/scripts/Deployer/SingleContracts/GithubAddressRegister.ts @@ -0,0 +1,33 @@ +import {SignerWithAddress} from "@nomiclabs/hardhat-ethers/signers"; +import {GithubAddressRegister} from "../../../typechain-types"; +import {ethers} from "hardhat"; +import {Deployer} from "@matterlabs/hardhat-zksync-deploy"; + +/** + * Deploy an instance of GithubAddressRegister + * @param signer - who's going to sign the transaction + * @param [nonce] - if we want to pass a nonce, rather than having the code to evaluate it + */ +export async function deployGithubAddressRegister( + signer: SignerWithAddress, + nonce: number = -1 +): Promise { + let next_nonce = nonce >= 0 ? nonce : await signer.getTransactionCount(); + const contractFactory = await ethers.getContractFactory("GithubAddressRegister", signer); + return await contractFactory.deploy( + { nonce: next_nonce } + ) as GithubAddressRegister; +} + +/** + * Deploy an instance of GithubAddressRegister on zkSync + * @param deployer - entity able to deploy on zkSync + * @param [nonce] - if we want to pass a nonce, rather than having the code to evaluate it + */ +export async function zkDeployGithubAddressRegister( + deployer: Deployer +): Promise { + const artifact = await deployer.loadArtifact("GithubAddressRegister"); + const contract = await deployer.deploy(artifact, []); + return contract as GithubAddressRegister; +} diff --git a/submissions/CorGit/code/contracts-corgit-zksync/scripts/Deployer/SingleContracts/cgFactory.ts b/submissions/CorGit/code/contracts-corgit-zksync/scripts/Deployer/SingleContracts/cgFactory.ts new file mode 100644 index 0000000..5ea3e04 --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/scripts/Deployer/SingleContracts/cgFactory.ts @@ -0,0 +1,40 @@ +import {SignerWithAddress} from "@nomiclabs/hardhat-ethers/signers"; +import {CgFactory, GithubAddressRegister} from "../../../typechain-types"; +import {ethers} from "hardhat"; +import {Deployer} from "@matterlabs/hardhat-zksync-deploy"; + +/** + * Deploy an instance of cgFactory + * @param signer - who's going to sign the transaction + * @param {string} githubAddressRegisterAddress - address of Github Address Register + * @param [nonce] - if we want to pass a nonce, rather than having the code to evaluate it + */ +export async function deployCgFactory( + signer: SignerWithAddress, + githubAddressRegisterAddress: string, + nonce: number = -1 +): Promise { + let next_nonce = nonce >= 0 ? nonce : await signer.getTransactionCount(); + const contractFactory = await ethers.getContractFactory("cgFactory", signer); + return await contractFactory.deploy( + githubAddressRegisterAddress, + { nonce: next_nonce } + ) as CgFactory; + +} + + +/** + * Deploy an instance of cgFactory on zkSync + * @param deployer - entity able to deploy on zkSync + * @param {string} githubAddressRegisterAddress - address of Github Address Register + * @param [nonce] - if we want to pass a nonce, rather than having the code to evaluate it + */ +export async function zkDeployCgFactory( + deployer: Deployer, + githubAddressRegisterAddress: string +): Promise { + const artifact = await deployer.loadArtifact("cgFactory"); + const contract = await deployer.deploy(artifact, [githubAddressRegisterAddress]); + return contract as CgFactory; +} diff --git a/submissions/CorGit/code/contracts-corgit-zksync/scripts/Deployer/SingleContracts/cgToken.ts b/submissions/CorGit/code/contracts-corgit-zksync/scripts/Deployer/SingleContracts/cgToken.ts new file mode 100644 index 0000000..fb4d858 --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/scripts/Deployer/SingleContracts/cgToken.ts @@ -0,0 +1,26 @@ +import {SignerWithAddress} from "@nomiclabs/hardhat-ethers/signers"; +import {ethers} from "hardhat"; + +/** + * Deploy an instance of cgToken + * @param signer - who's going to sign the transaction + * @param cgTokenAddress - address of the cgToken deployed + * @param newPayerAddress - address to be added as a payer + * @param [nonce] - if we want to pass a nonce, rather than having the code to evaluate it + */ +export async function cgToken_setPayerRole( + signer: SignerWithAddress, + cgTokenAddress: string, + newPayerAddress: string, + nonce: number = -1 +): Promise { + let next_nonce = nonce >= 0 ? nonce : await signer.getTransactionCount(); + const contractFactory = await ethers.getContractFactory("cgToken", signer); + return await contractFactory + .attach(cgTokenAddress) + .grantRole( + ethers.utils.keccak256(ethers.utils.toUtf8Bytes("PAYER")), + newPayerAddress, + { nonce: next_nonce } + ); +} diff --git a/submissions/CorGit/code/contracts-corgit-zksync/scripts/ProjectConstants.ts b/submissions/CorGit/code/contracts-corgit-zksync/scripts/ProjectConstants.ts new file mode 100644 index 0000000..0200a1f --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/scripts/ProjectConstants.ts @@ -0,0 +1,16 @@ +import {ethers} from "ethers"; + +export const CHAIN_CONSTANTS = { + // Goerli Ethereum + 5: { + + }, + // Polygon Mainnet + 137: { + + }, + // Hardhat testnet + 1337: { + + } +} diff --git a/submissions/CorGit/code/contracts-corgit-zksync/scripts/associateGithubToWallet.ts b/submissions/CorGit/code/contracts-corgit-zksync/scripts/associateGithubToWallet.ts new file mode 100644 index 0000000..525e7c5 --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/scripts/associateGithubToWallet.ts @@ -0,0 +1,48 @@ +import {Contract, Provider, Wallet} from "zksync-web3"; +import secrets from "../.secrets.json"; +import githubAddressRegister from "../artifacts-zk/contracts/GithubAddressRegister.sol/GithubAddressRegister.json"; + +const GITHUB_REGISTER_ADDRESS = "0xa5B07286eA9a9f7deC44104Cb621f1cf55AA9634"; + +// const GITHUB_ID = 12898752; +// const WALLET_ADDRESS_TO_ASSOCIATE = "0x349F4A96a44fcd83338b90DC37Fb7F5FeEc8AdE1"; +const GITHUB_ID = 31770652; +const WALLET_ADDRESS_TO_ASSOCIATE = "0x6cA960968E33F9350a3B4522a673f5d8438c9aAf"; + +export const associateGithubToWallet = async ( + githubAddressRegisterContract: string, + githubId: number, + walletAddress: string +): Promise<{ + +}> => { + const provider = new Provider('https://zksync2-testnet.zksync.dev'); + const signer = new Wallet(secrets.privateKeys.zksync.deployer, provider); + + const githubContract = new Contract( + githubAddressRegisterContract, + githubAddressRegister.abi, + signer + ); + + const transaction = await githubContract.connect(signer).addAddress(githubId, walletAddress); + console.log(transaction); + + return {}; +} + +if (typeof require !== 'undefined' && require.main === module) { + associateGithubToWallet( + GITHUB_REGISTER_ADDRESS, + GITHUB_ID, + WALLET_ADDRESS_TO_ASSOCIATE + ) + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); +} + + + diff --git a/submissions/CorGit/code/contracts-corgit-zksync/tsconfig.json b/submissions/CorGit/code/contracts-corgit-zksync/tsconfig.json new file mode 100644 index 0000000..8f16a28 --- /dev/null +++ b/submissions/CorGit/code/contracts-corgit-zksync/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "strict": true, + "skipLibCheck": true + } +} diff --git a/submissions/CorGit/code/webapp/.env.dev b/submissions/CorGit/code/webapp/.env.dev new file mode 100644 index 0000000..cdbe1f0 --- /dev/null +++ b/submissions/CorGit/code/webapp/.env.dev @@ -0,0 +1 @@ +REACT_APP_API_URL=https://google.clom diff --git a/submissions/CorGit/code/webapp/.env.production b/submissions/CorGit/code/webapp/.env.production new file mode 100644 index 0000000..cdbe1f0 --- /dev/null +++ b/submissions/CorGit/code/webapp/.env.production @@ -0,0 +1 @@ +REACT_APP_API_URL=https://google.clom diff --git a/submissions/CorGit/code/webapp/.gitignore b/submissions/CorGit/code/webapp/.gitignore new file mode 100644 index 0000000..ad15f41 --- /dev/null +++ b/submissions/CorGit/code/webapp/.gitignore @@ -0,0 +1,4 @@ +node_modules +.idea +yarn.lock +build diff --git a/submissions/CorGit/code/webapp/README.md b/submissions/CorGit/code/webapp/README.md new file mode 100644 index 0000000..120477a --- /dev/null +++ b/submissions/CorGit/code/webapp/README.md @@ -0,0 +1,21 @@ +# CorGit + +Tokenize your OpenSource project, to proportionally reward past, present and future contributors + +## Project creation + +In the project directory, you can run: + +### `yarn start` + +Runs the app in the development mode.\ +Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +### `yarn test` + +Launches the test runner in the interactive watch mode. + +### `yarn build` + +Builds the app for production to the `build` folder.\ +It correctly bundles React in production mode and optimizes the build for the best performance. diff --git a/submissions/CorGit/code/webapp/craco.config.js b/submissions/CorGit/code/webapp/craco.config.js new file mode 100644 index 0000000..ba3d522 --- /dev/null +++ b/submissions/CorGit/code/webapp/craco.config.js @@ -0,0 +1,25 @@ +// const webpack = require('webpack'); + +module.exports = { + // webpack: { + // configure: (webpackConfig, { env, paths }) => { + // // eslint-disable-next-line no-param-reassign + // webpackConfig.resolve.fallback = { + // crypto: require.resolve('crypto-browserify'), + // http: require.resolve('stream-http'), + // https: require.resolve('https-browserify'), + // os: require.resolve('os-browserify/browser'), + // stream: require.resolve('stream-browserify'), + // }; + // // Issue: https://github.com/webpack/changelog-v5/issues/10 + // webpackConfig.plugins.push( + // new webpack.ProvidePlugin({ + // process: "process/browser.js", + // Buffer: ["buffer", "Buffer"], + // }) + // ); + // return webpackConfig; + // }, + // }, + +} diff --git a/submissions/CorGit/code/webapp/package.json b/submissions/CorGit/code/webapp/package.json new file mode 100644 index 0000000..e6653d4 --- /dev/null +++ b/submissions/CorGit/code/webapp/package.json @@ -0,0 +1,87 @@ +{ + "name": "tproof-web-app", + "version": "0.3.0-beta", + "private": true, + "dependencies": { + "@emotion/react": "^11.9.3", + "@emotion/styled": "^11.9.3", + "@ethersproject/bignumber": "^5.7.0", + "@mui/icons-material": "^5.10.9", + "@mui/lab": "^5.0.0-alpha.105", + "@mui/material": "^5.10.11", + "@mui/styles": "^5.10.10", + "@octokit/rest": "^19.0.5", + "@reduxjs/toolkit": "^1.8.3", + "@sentry/react": "^7.6.0", + "@sentry/tracing": "^7.6.0", + "@testing-library/jest-dom": "^5.16.4", + "@testing-library/react": "^11.2.7", + "@testing-library/user-event": "^12.8.3", + "axios": "^1.1.3", + "date-fns": "^2.28.0", + "ellipsize": "^0.5.1", + "ethers": "^5.7.2", + "is-github-url": "^1.2.2", + "localforage": "^1.10.0", + "parse-github-url": "^1.0.2", + "pretty-bytes": "^6.0.0", + "query-string": "^7.1.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-markdown": "^8.0.3", + "react-redux": "^8.0.2", + "react-router": "^6.3.0", + "react-router-dom": "^6.3.0", + "react-scripts": "^5.0.1", + "redux": "^4.2.0", + "redux-logger": "^3.0.6", + "redux-thunk": "^2.4.1", + "styled-components": "^5.3.5", + "use-debounce": "^8.0.2", + "use-query-params": "^1.2.3", + "uuid": "^8.3.2", + "wagmi": "^0.7.8", + "web-vitals": "^2.1.4", + "web3": "^1.7.4", + "zksync-web3": "^0.11.1" + }, + "scripts": { + "start": "env-cmd -f .env.dev craco start", + "build": "env-cmd -f .env.production craco build", + "test": "react-scripts test", + "eject": "react-scripts eject" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@types/jest": "^28.1.5", + "@types/node": "^18.0.4", + "@types/react": "^18.0.15", + "@types/react-dom": "^18.0.6", + "@types/react-redux": "^7.1.24", + "@types/react-router": "^5.1.18", + "@types/react-router-dom": "^5.3.3", + "@types/redux-logger": "^3.0.9", + "@types/styled-components": "^5.1.25", + "craco": "^0.0.3", + "dotenv": "^10.0.0", + "env-cmd": "^10.1.0", + "typescript": "^4.4.4" + } +} diff --git a/submissions/CorGit/code/webapp/public/favicon.ico b/submissions/CorGit/code/webapp/public/favicon.ico new file mode 100644 index 0000000..0470b43 Binary files /dev/null and b/submissions/CorGit/code/webapp/public/favicon.ico differ diff --git a/submissions/CorGit/code/webapp/public/img/CorGitHomeImage.png b/submissions/CorGit/code/webapp/public/img/CorGitHomeImage.png new file mode 100644 index 0000000..633ca61 Binary files /dev/null and b/submissions/CorGit/code/webapp/public/img/CorGitHomeImage.png differ diff --git a/submissions/CorGit/code/webapp/public/index.html b/submissions/CorGit/code/webapp/public/index.html new file mode 100644 index 0000000..9a14b84 --- /dev/null +++ b/submissions/CorGit/code/webapp/public/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + CorGit | Tokenize Open Source Projects + + + + + + + + + + +
+ + + diff --git a/submissions/CorGit/code/webapp/public/logo192.png b/submissions/CorGit/code/webapp/public/logo192.png new file mode 100644 index 0000000..a862b14 Binary files /dev/null and b/submissions/CorGit/code/webapp/public/logo192.png differ diff --git a/submissions/CorGit/code/webapp/public/logo512.png b/submissions/CorGit/code/webapp/public/logo512.png new file mode 100644 index 0000000..7b726d7 Binary files /dev/null and b/submissions/CorGit/code/webapp/public/logo512.png differ diff --git a/submissions/CorGit/code/webapp/public/manifest.json b/submissions/CorGit/code/webapp/public/manifest.json new file mode 100644 index 0000000..5310930 --- /dev/null +++ b/submissions/CorGit/code/webapp/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "tProof.io", + "name": "Ethereum-based certification for digital content existing prior to a certain point in time. Certify your files on the most used blockchain", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/submissions/CorGit/code/webapp/src/App.Routes.tsx b/submissions/CorGit/code/webapp/src/App.Routes.tsx new file mode 100644 index 0000000..a8534d4 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/App.Routes.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import Home from "./ui/pages/Home/Home"; +import Create from "./ui/pages/Create/Create"; +import ProjectPage from "./ui/pages/ProjectPage/ProjectPage"; +import Reward from "./ui/pages/Reward/Reward"; + +export enum RouteKey { + Home = "/", + Create = "/create", + ProjectPage = "/project/:tokenAddress", + Reward = "/project/:tokenAddress/reward", +} +// list of all the routes of the App +export const routes = [ { + key: RouteKey.Home, + protected: false, + path: RouteKey.Home, + component: , +}, { + key: RouteKey.Create, + protected: false, + path: RouteKey.Create, + component: , +}, { + key: RouteKey.ProjectPage, + protected: false, + path: RouteKey.ProjectPage, + component: , +}, { + key: RouteKey.Reward, + protected: false, + path: RouteKey.Reward, + component: , +}] diff --git a/submissions/CorGit/code/webapp/src/App.tsx b/submissions/CorGit/code/webapp/src/App.tsx new file mode 100644 index 0000000..8bf7cfe --- /dev/null +++ b/submissions/CorGit/code/webapp/src/App.tsx @@ -0,0 +1,72 @@ +import React from 'react'; +import {BrowserRouter} from "react-router-dom"; +import {Route, Routes} from "react-router"; +import {routes} from "./App.Routes"; +import {configureChains, createClient, defaultChains, WagmiConfig} from "wagmi"; +import {publicProvider} from 'wagmi/providers/public'; +import {InjectedConnector} from "wagmi/connectors/injected"; +import {WalletConnectConnector} from 'wagmi/connectors/walletConnect'; + +const { chains, provider } = configureChains(defaultChains, [publicProvider()]) + +// const client = createClient({ +// autoConnect: true, +// provider: getDefaultProvider(), +// }) + +// export const goarliCustomTestnet = { +// id: 5, +// name: "Goerli", +// network: "goerli", +// nativeCurrency: { +// decimals: 18, +// name: "GoerliETH", +// symbol: "GoerliETH", +// }, +// rpcUrls: { +// default: "---", +// }, +// blockExplorers: { +// default: { +// name: "Goerli explorer", +// url: "https://blockscout.chiadochain.net", +// }, +// }, +// testnet: true, +// }; + +const client = createClient({ + autoConnect: true, + connectors: [ + new InjectedConnector({ chains }), + new WalletConnectConnector({ + chains, + options: { + qrcode: true, + }, + }), + ], + provider, +}) + +function App(): JSX.Element { + + return ( + + + + { + routes.map(r => { + if(r.protected) + return + else return + }) + } + + + + ); + +} + +export default App; diff --git a/submissions/CorGit/code/webapp/src/GlobalStyles.ts b/submissions/CorGit/code/webapp/src/GlobalStyles.ts new file mode 100644 index 0000000..7f850b4 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/GlobalStyles.ts @@ -0,0 +1,98 @@ +// @ts-ignore +// @ts-ignore + +import {createTheme, Theme} from '@mui/material/styles'; + +export const theme = createTheme({ + typography: { + fontFamily: [ + 'Roboto', + '"Helvetica Neue"', + 'Arial', + 'sans-serif', + '"Apple Color Emoji"', + '"Segoe UI Emoji"', + '"Segoe UI Symbol"', + ].join(','), + h1: { + fontSize: 30, + fontWeight: 600, + fontFamily: "Roboto" + }, + h2: { + fontSize: 24, + fontWeight: 600, + fontFamily: "Roboto" + }, + h3: { + fontSize: 22, + fontFamily: "Roboto", + fontWeight: 600 + }, + h4: { + fontSize: 18, + fontFamily: "Roboto", + fontWeight: 600 + }, + h5: { + fontSize: 16, + fontFamily: "Roboto" + }, + h6: { + fontSize: 14, + fontFamily: "Roboto" + }, + body1: { + fontSize: 18, + fontFamily: "Roboto" + }, + body2: { + fontSize: 14, + fontFamily: "Roboto" + }, + subtitle1: { + fontSize: 16, + fontFamily: "Roboto" + }, + button: { + fontWeight: 600, + fontFamily: "Roboto" + } + }, + palette: { + primary: { + main: '#158fd6', + light: '#63bfff', + dark: '#0062a4' + }, + secondary: { + main: '#ed9020', + light: '#ffc154', + dark: '#b56200' + }, + text: { + primary: '#4A4A4A', + secondary: '#737373', + disabled: '#B8B8B8', + }, + error: { main: '#F44336' }, + success: { main: '#80D283' }, + action: { + selected: '#E6E6E6' + } + }, + components: { + MuiCssBaseline: { + styleOverrides: ` + a { + color: #ed9020 + } + ` + } + } +}); + +// @ts-ignore +declare module '@mui/styles/defaultTheme' { + interface DefaultTheme extends Theme {} +} diff --git a/submissions/CorGit/code/webapp/src/hooks/fileListHook.ts b/submissions/CorGit/code/webapp/src/hooks/fileListHook.ts new file mode 100644 index 0000000..26909e8 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/hooks/fileListHook.ts @@ -0,0 +1,74 @@ +import {v4 as uuidv4} from 'uuid'; + +/** + * Manages a list of files as cache. Needed as in the redux store we cannot save a File object + */ + + +/** + * Defines an item in the FileListCache object (used within its class) + * + * @param {string} id - random if for the given file + * @param {File} file - the File object representing that item + */ +export type FileListCacheItem = { + id: string, + file: File +} + +/** + * Manages basic operation around a list of {@link FileListCacheItem[]} + */ +class FileListCacheClass { + listCache: FileListCacheItem[]; + + constructor() { + this.listCache = []; + } + + /** + * Adds a list of files to the cache, returning their IDs in the same order of FileList elements + * @param {FileList} fileList + * @return {string[]} the associated IDs with the files + */ + appendToFileListCache = (fileList: FileList): string[] => { + let ids: string[] = []; + for (let fi=0; fi < fileList.length; fi++) { + let uuid = uuidv4(); + ids.push(uuid); + this.listCache.push({ + id: uuid, + file: fileList.item(fi) + }) + } + return ids; + } + + /** + * Removes one file from the list cache + * @param {string} id - id of the file to remove + */ + removeFileFromListCache = (id: string) => { + this.listCache = this.listCache.filter(f => f.id !== id); + } + + /** + * Return the asked File (required by id) + * @return {File} + */ + getFileItem = (id: string): File => { + for (let p of this.listCache) { + if (p.id === id) return p.file; + } + throw new Error("Request a fileId that is not present in the list"); + } +} + +let fileListCache: FileListCacheClass = new FileListCacheClass(); + +/** + * Returns the reference to the fileListCache class object + */ +export const useFileListCache = (): FileListCacheClass => { + return fileListCache; +} diff --git a/submissions/CorGit/code/webapp/src/hooks/reduxHooks.ts b/submissions/CorGit/code/webapp/src/hooks/reduxHooks.ts new file mode 100644 index 0000000..ae50f09 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/hooks/reduxHooks.ts @@ -0,0 +1,5 @@ +import {TypedUseSelectorHook, useDispatch, useSelector} from "react-redux"; +import {AppDispatch, RootState} from "../store"; + +export const useAppDispatch = () => useDispatch() +export const useAppSelector: TypedUseSelectorHook = useSelector \ No newline at end of file diff --git a/submissions/CorGit/code/webapp/src/hooks/useAddCollateral.ts b/submissions/CorGit/code/webapp/src/hooks/useAddCollateral.ts new file mode 100644 index 0000000..7ae33ab --- /dev/null +++ b/submissions/CorGit/code/webapp/src/hooks/useAddCollateral.ts @@ -0,0 +1,48 @@ +import {useState} from "react"; +import {useAppDispatch} from "./reduxHooks"; +import {CONTRACTS_DETAILS} from "../utils/constants"; +import {ethers, Signer} from "ethers"; +import {useContract} from "wagmi"; +import {Contract, Web3Provider} from "zksync-web3"; + +export const useAddCollateral = (params: {cgTokenAddress: string}) => { + const [status, setStatus] = useState<{ + completed: boolean, + transactionHash: string, + error: string + }>({completed: false, transactionHash: "", error: ""}); + const dispatch = useAppDispatch(); + + + // const contract = useContract({ + // address: params.cgTokenAddress, + // abi: CONTRACTS_DETAILS[5].CG_PROJECT_ABI + // }); + + let contract = new Contract( + params.cgTokenAddress, + CONTRACTS_DETAILS[280].CG_PROJECT_ABI + ); + + const checkNow = (params: {amountETH: number, signer: Signer}) => { + setStatus({completed: false, transactionHash: "", error: ""}); + let signer = (new Web3Provider(window.ethereum)).getSigner(); + contract.connect(signer).contribute({ + value: ethers.utils.parseEther(params.amountETH.toString()) + }) + .then(tx => { + console.log(tx); + setStatus({completed: false, transactionHash: tx.hash, error: ""}); + return tx.wait(); + }) + .then(rc => { + setStatus({completed: true, transactionHash: "", error: ""}); + }) + .catch(error => { + setStatus({completed: true, error: "Transaction error", transactionHash: ""}); + }); + } + return { + ...status, checkNow + }; +} diff --git a/submissions/CorGit/code/webapp/src/hooks/useClaimRewards.ts b/submissions/CorGit/code/webapp/src/hooks/useClaimRewards.ts new file mode 100644 index 0000000..612dcd5 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/hooks/useClaimRewards.ts @@ -0,0 +1,43 @@ +import {useState} from "react"; +import {useAppDispatch} from "./reduxHooks"; +import {useContract} from "wagmi"; +import {CONTRACTS_DETAILS} from "../utils/constants"; +import {Signer} from "ethers"; +import {Contract, Web3Provider} from "zksync-web3"; + +export const useClaimRewards = (params: {cgTokenAddress: string}) => { + const [status, setStatus] = useState<{ + completed: boolean, + transactionHash: string, + error: string + }>({completed: false, transactionHash: "", error: ""}); + const dispatch = useAppDispatch(); + // const contract = useContract({ + // address: params.cgTokenAddress, + // abi: CONTRACTS_DETAILS[5].CG_PROJECT_ABI + // }); + let contract = new Contract( + params.cgTokenAddress, + CONTRACTS_DETAILS[280].CG_PROJECT_ABI + ); + const checkNow = (params: {toAddress: string, paymentId: number, signer: Signer}) => { + setStatus({completed: false, transactionHash: "", error: ""}); + + let signer = (new Web3Provider(window.ethereum)).getSigner(); + contract.connect(signer).collectPayment(params.toAddress, params.paymentId) + .then(tx => { + console.log(tx); + setStatus({completed: false, transactionHash: tx.hash, error: ""}); + return tx.wait(); + }) + .then(rc => { + setStatus({completed: true, transactionHash: "", error: ""}); + }) + .catch(error => { + setStatus({completed: true, error: "Transaction error", transactionHash: ""}); + }); + } + return { + ...status, checkNow + }; +} diff --git a/submissions/CorGit/code/webapp/src/hooks/useCreateCgProject.ts b/submissions/CorGit/code/webapp/src/hooks/useCreateCgProject.ts new file mode 100644 index 0000000..4587774 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/hooks/useCreateCgProject.ts @@ -0,0 +1,57 @@ +import {useState} from "react"; +import {useAppDispatch} from "./reduxHooks"; +import {Signer} from "ethers"; +import {useContract, useProvider} from "wagmi"; +import {CONTRACTS_DETAILS} from "../utils/constants"; +import { Contract, Web3Provider } from "zksync-web3"; + + +export interface CreateCgProjectInterface { + fromAddress: string, + tokenName: string, + tokenSymbol: string, + prevContrRewards: number, + signer: Signer +} + +export const useCreateCgProject = () => { + const [status, setStatus] = useState<{ + transactionHash: string, + error: string, + tokenAddress: string + }>({transactionHash: "", error: "", tokenAddress: ""}); + const dispatch = useAppDispatch(); + + // const contract = useContract({ + // address: CONTRACTS_DETAILS[280].CG_FACTORY, + // abi: CONTRACTS_DETAILS[280].CG_FACTORY_ABI + // }); + + let contract = new Contract( + CONTRACTS_DETAILS[280].CG_FACTORY, + CONTRACTS_DETAILS[280].CG_FACTORY_ABI + ); + + const checkNow = (params: CreateCgProjectInterface) => { + setStatus({transactionHash: "", error: "", tokenAddress: ""}); + let signer = (new Web3Provider(window.ethereum)).getSigner(); + contract.connect(signer).generate( + params.tokenName, + params.tokenSymbol, + params.prevContrRewards + ).then(result => { + return result.wait() + .then(rc => { + console.log('RC'); + console.log(rc); + const event = rc?.events?.find(event => event.event === 'NewCgTokenCreated'); + console.log("event read", event); + const [_addr, _name, _symbol, _percFundingDistribute] = event?.args; + setStatus({transactionHash: "", error: "", tokenAddress: _addr}); + }) + }); + } + return { + ...status, checkNow + } +} diff --git a/submissions/CorGit/code/webapp/src/hooks/useCreateRewardContributions.ts b/submissions/CorGit/code/webapp/src/hooks/useCreateRewardContributions.ts new file mode 100644 index 0000000..48842b2 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/hooks/useCreateRewardContributions.ts @@ -0,0 +1,53 @@ +import {useState} from "react"; +import {useAppDispatch} from "./reduxHooks"; +import {useContract} from "wagmi"; +import {CONTRACTS_DETAILS} from "../utils/constants"; +import {Signer} from "ethers"; +import {BigNumber} from "@ethersproject/bignumber"; +import {Contract, Web3Provider} from "zksync-web3"; + +export interface CreateRewardContributionsInterface { + githubIds: number[], + amountList: BigNumber[], + name: string, + signer: Signer +} + +export const useCreateRewardContributions = (params: {cgTokenAddress: string}) => { + const [status, setStatus] = useState<{ + completed: boolean, + transactionHash: string, + error: string + }>({completed: false, transactionHash: "", error: ""}); + const dispatch = useAppDispatch(); + + // const contract = useContract({ + // address: params.cgTokenAddress, + // abi: CONTRACTS_DETAILS[5].CG_PROJECT_ABI + // }); + let contract = new Contract( + params.cgTokenAddress, + CONTRACTS_DETAILS[280].CG_PROJECT_ABI + ); + + const checkNow = (params: CreateRewardContributionsInterface) => { + setStatus({transactionHash: "", error: "", completed: false}); + // call the contract function to create rewards + let signer = (new Web3Provider(window.ethereum)).getSigner(); + contract.connect(signer).pay(params.githubIds, params.amountList, params.name) + .then(tx => { + console.log(tx); + setStatus({completed: false, transactionHash: tx.hash, error: ""}); + return tx.wait(); + }) + .then(rc => { + setStatus({completed: true, transactionHash: "", error: ""}); + }) + .catch(error => { + setStatus({completed: true, error: "Transaction error", transactionHash: ""}); + }); + }; + return { + ...status, checkNow + }; +} diff --git a/submissions/CorGit/code/webapp/src/hooks/useGetPullRequestDetails.ts b/submissions/CorGit/code/webapp/src/hooks/useGetPullRequestDetails.ts new file mode 100644 index 0000000..9a318d1 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/hooks/useGetPullRequestDetails.ts @@ -0,0 +1,68 @@ +import {useState} from "react"; +import {useAppDispatch} from "./reduxHooks"; +import parseGithubUrl from "parse-github-url"; +import {Octokit} from "@octokit/rest"; +import {PullRequest, PullRequestContributor, PullRequestState} from "../utils/ProjectTypes/Project.types"; +import {githubReducerActions} from "../store/reducers/github"; + +const loadPullRequestInformation = async ( + params: {repoOwner: string, repoName: string, pullRequestNumber: number}): Promise => { + // run the GET calling the github sdk + const octokit = new Octokit({}); + const pullRequestInformation = await octokit.rest.pulls.get({ + owner: params.repoOwner, repo: params.repoName, pull_number: params.pullRequestNumber}); + const commitsInformation = await octokit.rest.pulls.listCommits({ + owner: params.repoOwner, repo: params.repoName, pull_number: params.pullRequestNumber}); + const commits: {authorId: number, message: string, sha: string, url: string}[] = + commitsInformation.data.map(commit => { + return {authorId: commit.author.id, sha: commit.sha, message: commit.commit.message, url: commit.url};}); + // get unique contributors from commits + const uniqueContributors: number[] = Array.from(new Set(commits.map(commit => {return commit.authorId; } )).values()); + const contributors: PullRequestContributor[] = []; + uniqueContributors.forEach(contributorId => { + let contributorInformation = commitsInformation.data.filter(contributor => { return contributor.author.id })[0]; + contributors.push({ + id: contributorId, + username: contributorInformation.author.login as string, + avatarUrl: contributorInformation.author.avatar_url as string, + profileUrl: contributorInformation.author.url as string, + commits: commits.filter(commit => { return commit.authorId === contributorId; }).map(commit => { + return { message: commit.message, sha: commit.sha, url: commit.url }; + }) + }); + }); + return { + id: pullRequestInformation.data.id, + title: pullRequestInformation.data.title, + closedAt: Math.round(new Date(pullRequestInformation.data.closed_at).getTime() / 1000), + state: pullRequestInformation.data.state as PullRequestState, + contributors: contributors + } as PullRequest; +} + +export const useGetPullRequestDetails = () => { + const [status, setStatus] = useState<{ + loading: boolean, + error: string, + pullRequestUrl: string, + }>({loading: false, error: "", pullRequestUrl: ""}); + const dispatch = useAppDispatch(); + const checkNow = (pullRequestUrl: string) => { + setStatus({loading: true, error: "", pullRequestUrl: ""}); + const pullRequestInformation = parseGithubUrl(pullRequestUrl); + console.log(pullRequestInformation); + if (pullRequestInformation.branch !== null && pullRequestInformation.branch === "pull") { + loadPullRequestInformation({ + repoOwner: pullRequestInformation.owner, + repoName: pullRequestInformation.name, + pullRequestNumber: parseInt(pullRequestInformation.filepath) + }).then(pullRequest => { + dispatch(githubReducerActions.setPullRequest(pullRequest)); + }); + setStatus({loading: false, error: "", pullRequestUrl: pullRequestUrl}); + } else setStatus({loading: false, error: "Invalid pull request URL", pullRequestUrl: ""}); + } + return { + ...status, checkNow + } +} diff --git a/submissions/CorGit/code/webapp/src/hooks/useLoadCgProject.ts b/submissions/CorGit/code/webapp/src/hooks/useLoadCgProject.ts new file mode 100644 index 0000000..0224640 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/hooks/useLoadCgProject.ts @@ -0,0 +1,98 @@ +import {useState} from "react"; +import {useAppDispatch} from "./reduxHooks"; +import {CONTRACTS_DETAILS} from "../utils/constants"; +import {Contract, ethers, providers, Signer} from "ethers"; +import {BigNumber} from "@ethersproject/bignumber"; +import {cgProjectReducerActions} from "../store/reducers/cgProject"; +import {useContract} from "wagmi"; +import {Web3Provider} from "zksync-web3"; + +const getCgTokenInformation = async (contract: Contract, signer: Signer, provider: providers.Provider, userAddress: string): Promise<{ + tokenSymbol: string, + distributionReward: number, + name: string, + isPayer: boolean, + totalSupply: number, + unclaimedRewards: number, + treasuryBalance: number, + collectedRewards: number, + tokenValue: number +}> => { + + let roleHash = ethers.utils.keccak256(ethers.utils.toUtf8Bytes("PAYER")); + let promises = []; + promises.push(contract.connect(signer).symbol()); + promises.push(contract.connect(signer).percFundingDistributed()); + promises.push(contract.connect(signer).name()); + promises.push(contract.connect(signer).hasRole(roleHash, userAddress)); + promises.push(contract.connect(signer).totalSupply()); + promises.push(contract.connect(signer).lockedTokensForPayments()) + promises.push(contract.connect(signer).balanceOf(contract.address)); + // balance del contratto + promises.push(provider.getBalance(contract.address)); + + let responses = await Promise.all(promises); + const tokenSymbol = responses[0]; + const distributionReward = responses[1]; + const name = responses[2]; + const isPayer = responses[3]; + const totalSupplyBigNumber = (responses[4] as BigNumber); + const totalSupply = totalSupplyBigNumber.div(BigNumber.from(10).pow(18)).toNumber(); + const unclaimedRewards = (responses[5] as BigNumber).div(BigNumber.from(10).pow(18)).toNumber(); + const treasuryBalance = + (responses[6] as BigNumber).div(BigNumber.from(10).pow(18)).toNumber() - unclaimedRewards; + const collectedRewards = totalSupply - treasuryBalance - unclaimedRewards; + const balance = (responses[7] as BigNumber); + const balanceInEth = ethers.utils.formatEther(balance); + console.log("balance.toHexString", balance.toHexString()); + console.log("totalSupplyBigNumber", totalSupplyBigNumber.toHexString()); + const tokenValue = parseFloat(ethers.utils.formatEther(balance)) / parseFloat(ethers.utils.formatEther(totalSupplyBigNumber)); + + return { + tokenSymbol, distributionReward, name, isPayer, totalSupply, unclaimedRewards, treasuryBalance, collectedRewards, + tokenValue: tokenValue + }; +}; + +export const useLoadCgProject = (cgTokenAddress: string) => { + const [status, setStatus] = useState<{ + loading: boolean, + error: string + }>({loading: false, error: ""}); + const dispatch = useAppDispatch(); + // const contract = useContract({ + // address: cgTokenAddress, + // abi: CONTRACTS_DETAILS[5].CG_PROJECT_ABI + // }); + let contract = new Contract( + cgTokenAddress, + CONTRACTS_DETAILS[280].CG_PROJECT_ABI + ); + + const checkNow = (signer: Signer, provider: providers.Provider, userAddress: string,) => { + setStatus({loading: true, error: ""}); + let signerZk = (new Web3Provider(window.ethereum)).getSigner(); + if (!ethers.utils.isAddress(cgTokenAddress)) setStatus({loading: false, error: "Invalid Ethereum address"}); + else { + getCgTokenInformation(contract, signerZk, provider, userAddress).then(cgTokenInformation => { + dispatch(cgProjectReducerActions.setCgProjectInformation({ + tokenAddress: cgTokenAddress, + tokenSymbol: cgTokenInformation.tokenSymbol, + tokenTotalSupply: cgTokenInformation.totalSupply, + tokenName: cgTokenInformation.name, + tokenValue: cgTokenInformation.tokenValue, + isPayer: cgTokenInformation.isPayer, + treasuryBalance: cgTokenInformation.treasuryBalance, + unclaimedRewards: cgTokenInformation.unclaimedRewards, + distributionReward: cgTokenInformation.distributionReward, + collectedRewards: cgTokenInformation.collectedRewards + })) + setStatus({loading: false, error: ""}); + }) + } + }; + + return { + ...status, checkNow + }; +}; diff --git a/submissions/CorGit/code/webapp/src/hooks/useLoadProjectUserContributions.ts b/submissions/CorGit/code/webapp/src/hooks/useLoadProjectUserContributions.ts new file mode 100644 index 0000000..08cbf9b --- /dev/null +++ b/submissions/CorGit/code/webapp/src/hooks/useLoadProjectUserContributions.ts @@ -0,0 +1,141 @@ +import {useState} from "react"; +import {useAppDispatch} from "./reduxHooks"; +import {Contract, Signer} from "ethers"; +import {useContract} from "wagmi"; +import {CONTRACTS_DETAILS} from "../utils/constants"; +import {contributionsReducerActions} from "../store/reducers/contributions"; +import {BigNumber} from "@ethersproject/bignumber"; +import {Web3Provider} from "zksync-web3"; + +export interface LoadProjectUserContributionsInterface { + signer: Signer, + address: string +} + +export interface ProjectUserContributionInterface { + paymentId: number, + amount: number, + paid: boolean, + creation: number, + totalTokenAmount: number, + totalTokenClaimed: number, + name: string, + numOfUsers: number, + claimCompleted: boolean +} + +const loadProjectUserContributions = async (params: { + signer: Signer, address: string, githubContract: Contract, cgTokenContract: Contract}): + Promise<{githubId: number | undefined, contributions: ProjectUserContributionInterface[]}> => { + // get github id connect to the address parameter + let githubId; + try { + githubId = await params.githubContract.connect(params.signer).addressToGithubID(params.address); + } catch (e) { + return {githubId: undefined, contributions: []}; + } + if (githubId === undefined) return {githubId: undefined, contributions: []}; + // get the list id payment ids associated with the github id got from the blockchain + const paymentIds = []; + for (let i = 0; i < 10; i++) { + try { + const paymentIdTmp = await params.cgTokenContract.connect(params.signer).userPayments(githubId, i); + paymentIds.push(paymentIdTmp); + } catch (e) { + break; + } + } + const userPaymentsPromises = []; + const paymentsPromises = []; + paymentIds.forEach((paymentId: string) => { + userPaymentsPromises.push(params.cgTokenContract.connect(params.signer).paymentAmounts(paymentId, githubId)); + paymentsPromises.push(params.cgTokenContract.connect(params.signer).payments(paymentId)); + }); + const userPayments: {amount: number, paid: boolean}[] = [] ; + if (userPaymentsPromises.length !== 0) { + let responses = await Promise.all(userPaymentsPromises); + responses.forEach(response => { + userPayments.push({ + amount: response.amount.div(BigNumber.from(10).pow(18)).toNumber(), + paid: response.paid + }); + }); + } + const payments: { + creation: number, totalTokenAmount: number, totalTokenClaimed: number, name: string, + numOfUsers: number, claimCompleted: boolean}[] = []; + if (paymentsPromises.length !== 0) { + let responses = await Promise.all(paymentsPromises); + responses.forEach(response => { + payments.push({ + creation: response.creation.toNumber(), + totalTokenAmount: response.totalTokenAmount.div(BigNumber.from(10).pow(18)).toNumber(), + totalTokenClaimed: response.totalTokenClaimed.div(BigNumber.from(10).pow(18)).toNumber(), + name: response.name, + numOfUsers: response.numOfUsers.toNumber(), + claimCompleted: response.claimCompleted + }); + }) + } + const contributions: ProjectUserContributionInterface[] = [] as ProjectUserContributionInterface[]; + for (let position in paymentIds) { + contributions.push({ + paymentId: paymentIds[position].toNumber(), + creation: payments[position].creation, + totalTokenAmount: payments[position].totalTokenAmount, + totalTokenClaimed: payments[position].totalTokenClaimed, + name: payments[position].name, + numOfUsers: payments[position].numOfUsers, + claimCompleted: payments[position].claimCompleted, + amount: userPayments[position].amount, + paid: userPayments[position].paid + }); + } + return {githubId: githubId, contributions: contributions.reverse()}; +} + +export const useLoadProjectUserContributions = (cgTokenAddress: string) => { + const [status, setStatus] = useState<{ + loading: boolean, + error: string, + projectUserContributions: ProjectUserContributionInterface[] + }>({loading: false, error: "", projectUserContributions: [] as ProjectUserContributionInterface[]}); + const dispatch = useAppDispatch(); + // const contract = useContract({ + // address: CONTRACTS_DETAILS[5].GITHUB_ADDRESS_REGISTER, + // abi: CONTRACTS_DETAILS[5].GITHUB_ADDRESS_REGISTER_ABI + // }); + let contract = new Contract( + CONTRACTS_DETAILS[280].GITHUB_ADDRESS_REGISTER, + CONTRACTS_DETAILS[280].GITHUB_ADDRESS_REGISTER_ABI + ); + // const cgTokenContract = useContract({ + // address: cgTokenAddress, + // abi: CONTRACTS_DETAILS[5].CG_PROJECT_ABI + // }); + let cgTokenContract = new Contract( + cgTokenAddress, + CONTRACTS_DETAILS[280].CG_PROJECT_ABI + ); + + const checkNow = (params: LoadProjectUserContributionsInterface) => { + setStatus({loading: true, error: "", projectUserContributions: []}); + let signer = (new Web3Provider(window.ethereum)).getSigner(); + loadProjectUserContributions({ + signer: signer, + address: params.address, + cgTokenContract: cgTokenContract, + githubContract: contract}) + .then(userContributions => { + if (userContributions.githubId === undefined) { + setStatus({loading: false, error: "No githubId connected to the given address", projectUserContributions: []}); + } else { + dispatch(contributionsReducerActions.setUserContributions(userContributions.contributions)); + setStatus({loading: false, error: "", projectUserContributions: userContributions.contributions}); + } + }) + } + return { + ...status, checkNow + } +} diff --git a/submissions/CorGit/code/webapp/src/hooks/useSearchCgProject.ts b/submissions/CorGit/code/webapp/src/hooks/useSearchCgProject.ts new file mode 100644 index 0000000..5a315bb --- /dev/null +++ b/submissions/CorGit/code/webapp/src/hooks/useSearchCgProject.ts @@ -0,0 +1,54 @@ +import {useState} from "react"; +import {useAppDispatch} from "./reduxHooks"; +import isGithubUrl from "is-github-url"; +import parseGithubUrl from "parse-github-url"; +import axios, {AxiosResponse} from "axios"; +import {cgProjectReducerActions} from "../store/reducers/cgProject"; +import {ethers} from "ethers"; + +const getContractAddressFromGithubRepo = async (repoOwner: string, repoName: string): Promise => { + const githubRawResponse: AxiosResponse = await axios.get( + `https://raw.githubusercontent.com/${repoOwner}/${repoName}/master/.corgit.config`); + if (githubRawResponse.status === 200) { + const corgitConfig: {cgTokenAddress: string} = githubRawResponse.data; + return corgitConfig.cgTokenAddress; + } else return undefined; +} + +export const useSearchCgProject = () => { + const [status, setStatus] = useState<{ + loading: boolean, + error: string, + address: string, + }>({loading: false, error: "", address: ""}); + const dispatch = useAppDispatch(); + + const checkNow = (address: string) => { + setStatus({loading: true, error: "", address: ""}); + // understand if the address parameter is an ETH contract address or a GitHub repository + if (isGithubUrl(address, {repository: true, strict: false})) { + console.log(`Valid GitHub url -> ` + address); + const githubRepoInfo = parseGithubUrl(address); + // get token address from the master branch + getContractAddressFromGithubRepo(githubRepoInfo.owner, githubRepoInfo.name) + .then(tokenAddress => { + if (tokenAddress === undefined) { + setStatus({loading: false, error: ".corgit.config not found", address: ""}); + } else { + dispatch(cgProjectReducerActions.setTokenAddress(tokenAddress)); + setStatus({loading: false, error: "", address: tokenAddress}); + } + }); + } else { + if (!ethers.utils.isAddress(address)) { + setStatus({loading: false, error: "Invalid Ethereum address", address: ""}); + } else { + dispatch(cgProjectReducerActions.setTokenAddress(address)); + setStatus({loading: false, error: "", address: address}); + } + } + } + return { + ...status, checkNow + } +} diff --git a/submissions/CorGit/code/webapp/src/index.css b/submissions/CorGit/code/webapp/src/index.css new file mode 100644 index 0000000..70d349b --- /dev/null +++ b/submissions/CorGit/code/webapp/src/index.css @@ -0,0 +1,13 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Benne', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +code { + font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + monospace; +} diff --git a/submissions/CorGit/code/webapp/src/index.tsx b/submissions/CorGit/code/webapp/src/index.tsx new file mode 100644 index 0000000..4db2425 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/index.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import {createRoot} from 'react-dom/client'; +import './index.css'; +import App from './App'; +import reportWebVitals from './reportWebVitals'; +import {Provider} from "react-redux"; +import {store} from "./store"; +import {CssBaseline, StyledEngineProvider, ThemeProvider} from "@mui/material"; +import {theme} from "./GlobalStyles"; + + +const container = document.getElementById('root'); +const root = createRoot(container!); +root.render( + + + + + + + + + + +); + + +// If you want to start measuring performance in your app, pass a function +// to log results (for example: reportWebVitals(console.log)) +// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals +reportWebVitals(); diff --git a/submissions/CorGit/code/webapp/src/logo.svg b/submissions/CorGit/code/webapp/src/logo.svg new file mode 100644 index 0000000..9dfc1c0 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/submissions/CorGit/code/webapp/src/react-app-env.d.ts b/submissions/CorGit/code/webapp/src/react-app-env.d.ts new file mode 100644 index 0000000..6431bc5 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/submissions/CorGit/code/webapp/src/reportWebVitals.ts b/submissions/CorGit/code/webapp/src/reportWebVitals.ts new file mode 100644 index 0000000..16ffde3 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/reportWebVitals.ts @@ -0,0 +1,15 @@ +import {ReportHandler} from 'web-vitals'; + +const reportWebVitals = (onPerfEntry?: ReportHandler) => { + if (onPerfEntry && onPerfEntry instanceof Function) { + import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + getCLS(onPerfEntry); + getFID(onPerfEntry); + getFCP(onPerfEntry); + getLCP(onPerfEntry); + getTTFB(onPerfEntry); + }); + } +}; + +export default reportWebVitals; diff --git a/submissions/CorGit/code/webapp/src/setupTests.ts b/submissions/CorGit/code/webapp/src/setupTests.ts new file mode 100644 index 0000000..8f2609b --- /dev/null +++ b/submissions/CorGit/code/webapp/src/setupTests.ts @@ -0,0 +1,5 @@ +// jest-dom adds custom jest matchers for asserting on DOM nodes. +// allows you to do things like: +// expect(element).toHaveTextContent(/react/i) +// learn more: https://github.com/testing-library/jest-dom +import '@testing-library/jest-dom'; diff --git a/submissions/CorGit/code/webapp/src/store/actions/basicActions.ts b/submissions/CorGit/code/webapp/src/store/actions/basicActions.ts new file mode 100644 index 0000000..bd9346b --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/actions/basicActions.ts @@ -0,0 +1,17 @@ +import {CaseReducer, PayloadAction} from "@reduxjs/toolkit"; +import {BaseReducer} from "../reducers"; + + +/** -- ACTIONS */ + +/** + * To fully set a value + * @param {Draft} state + * @param {PayloadAction} action + */ +export const clearError: CaseReducer> = (state, action) => { + state.dispatchError = undefined +} + + + diff --git a/submissions/CorGit/code/webapp/src/store/actions/cgProjectActions.ts b/submissions/CorGit/code/webapp/src/store/actions/cgProjectActions.ts new file mode 100644 index 0000000..0b10dff --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/actions/cgProjectActions.ts @@ -0,0 +1,35 @@ +import {CaseReducer, PayloadAction} from "@reduxjs/toolkit"; +import {CgProjectReducer} from "../reducers/cgProject"; + + +export interface CgProjectInformationInterface { + tokenAddress: string, + tokenSymbol: string, + distributionReward: number, + tokenName: string, + isPayer: boolean, + tokenTotalSupply: number, + unclaimedRewards: number, + treasuryBalance: number, + collectedRewards: number, + tokenValue: number +} + +export const setCgProjectInformation: CaseReducer> = + (state, action) => { + state.tokenSymbol = action.payload.tokenSymbol; + state.tokenAddress = action.payload.tokenAddress; + state.collectedRewards = action.payload.collectedRewards; + state.distributionReward = action.payload.distributionReward; + state.unclaimedRewards = action.payload.unclaimedRewards; + state.tokenName = action.payload.tokenName; + state.tokenValue = action.payload.tokenValue; + state.treasuryBalance = action.payload.treasuryBalance; + state.isPayer = action.payload.isPayer; + state.tokenTotalSupply = action.payload.tokenTotalSupply; + } + +export const setTokenAddress: CaseReducer> = + (state, action) => { + state.tokenAddress = action.payload; + }; diff --git a/submissions/CorGit/code/webapp/src/store/actions/contributionsActions.ts b/submissions/CorGit/code/webapp/src/store/actions/contributionsActions.ts new file mode 100644 index 0000000..d24100a --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/actions/contributionsActions.ts @@ -0,0 +1,10 @@ +import {CaseReducer, PayloadAction} from "@reduxjs/toolkit"; +import {ContributionsReducer} from "../reducers/contributions"; +import {ProjectUserContributionInterface} from "../../hooks/useLoadProjectUserContributions"; + +export const setUserContributions: CaseReducer> = + (state, action) => { + state.userContributions = action.payload; + }; + + diff --git a/submissions/CorGit/code/webapp/src/store/actions/githubActions.ts b/submissions/CorGit/code/webapp/src/store/actions/githubActions.ts new file mode 100644 index 0000000..8c23c82 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/actions/githubActions.ts @@ -0,0 +1,8 @@ +import {CaseReducer, PayloadAction} from "@reduxjs/toolkit"; +import {PullRequest} from "../../utils/ProjectTypes/Project.types"; +import {GithubReducer} from "../reducers/github"; + +export const setPullRequest: CaseReducer> = + (state, action) => { + state.pullRequest = action.payload; + } diff --git a/submissions/CorGit/code/webapp/src/store/actions/uiActions.ts b/submissions/CorGit/code/webapp/src/store/actions/uiActions.ts new file mode 100644 index 0000000..a9f83ed --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/actions/uiActions.ts @@ -0,0 +1,43 @@ +import {CaseReducer, PayloadAction} from "@reduxjs/toolkit"; +import {AllowedUiReducerName, UiReducer, UiReducerElement} from "../reducers/ui"; + +/** + * Shape of the action for the set() command + * @param {AllowedUiReducerName} name - name of the settings + * @param {UiReducerElement} payload - the actual content of that setting + */ +export interface UiReducerSetAction { + name: AllowedUiReducerName, + payload: UiReducerElement +} + +/** + * Shape of the action for the toggle() command + * @param {AllowedUiReducerName} name - name of the settings + * @param {boolean} state - the value to set (true / false) in the setting's state + */ +export interface UiReducerToggleAction { + name: AllowedUiReducerName, + state: boolean +} + +/** -- ACTIONS */ + +/** + * To fully set a value + * @param {Draft} state + * @param {PayloadAction>} action + */ +export const set: CaseReducer>> = (state, action) => { + // @ts-ignore + state[action.payload.name] = action.payload.payload; +} + +/** + * To set the state of a value + * @param {Draft} state + * @param {PayloadAction} action + */ +export const toggle: CaseReducer> = (state, action) => { + state[action.payload.name].state = action.payload.state +} diff --git a/submissions/CorGit/code/webapp/src/store/actions/userAccountActions.ts b/submissions/CorGit/code/webapp/src/store/actions/userAccountActions.ts new file mode 100644 index 0000000..e27bb66 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/actions/userAccountActions.ts @@ -0,0 +1,28 @@ +import {CaseReducer, PayloadAction} from "@reduxjs/toolkit"; +import {UserAccountReducer} from "../reducers/userAccount"; + + +/** -- ACTIONS */ + +/** + * Stores the connected account at browser level + * @param {Draft} state + * @param {PayloadAction} action + */ +export const setConnectedAccount: CaseReducer> = + (state, action) => { + state.connectedWalletAddress = action.payload + } + +/** + * Stores the id of the connected chain + * @param {Draft} state + * @param {PayloadAction} action - id of the chain + */ +export const setChainId: CaseReducer> = + (state, action) => { + state.chainId = action.payload + } + + + diff --git a/submissions/CorGit/code/webapp/src/store/index.ts b/submissions/CorGit/code/webapp/src/store/index.ts new file mode 100644 index 0000000..9cdc495 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/index.ts @@ -0,0 +1,18 @@ +import rootReducer from "./reducers"; +import {logger} from "redux-logger"; +import thunk from 'redux-thunk'; +import {configureStore} from "@reduxjs/toolkit"; + + +export const store = configureStore({ + reducer: rootReducer, + middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(thunk, logger), + devTools: process.env.NODE_ENV !== 'production' +}) + +export const getStoreRef = () => { + return store; +} + +export type RootState = ReturnType +export type AppDispatch = typeof store.dispatch diff --git a/submissions/CorGit/code/webapp/src/store/reducers/cgProject.ts b/submissions/CorGit/code/webapp/src/store/reducers/cgProject.ts new file mode 100644 index 0000000..2a51315 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/reducers/cgProject.ts @@ -0,0 +1,55 @@ +import {BaseReducer} from "./index"; +import {createSlice} from "@reduxjs/toolkit"; +import {clearError} from "../actions/basicActions"; +import {setCgProjectInformation, setTokenAddress} from "../actions/cgProjectActions"; + +export interface CgProjectReducer extends BaseReducer { + tokenName: string, + tokenSymbol: string, + tokenAddress: string, + tokenTotalSupply: number, + treasuryBalance: number, + unclaimedRewards: number, + collectedRewards: number, + distributionReward: number, + tokenValue: number, + isPayer: boolean +} + +/** -- INITIAL STATE */ + +const initialState: CgProjectReducer = { + tokenName: "", + tokenSymbol: "", + tokenAddress: "", + tokenTotalSupply: 0, + tokenValue: 0, + treasuryBalance: 0, + unclaimedRewards: 0, + collectedRewards: 0, + distributionReward: 0, + isPayer: false +} + +/** --- CREATE THE REDUCER */ + +export const cgProjectReducerSlice = createSlice({ + name: 'project', + initialState, + reducers: { + clearError, + setCgProjectInformation, + setTokenAddress + }, + extraReducers: + (builder) => { + + } +}); + +export const cgProjectReducerActions = { + setCgProjectInformation: cgProjectReducerSlice.actions.setCgProjectInformation, + setTokenAddress: cgProjectReducerSlice.actions.setTokenAddress +} + +export default cgProjectReducerSlice.reducer diff --git a/submissions/CorGit/code/webapp/src/store/reducers/contributions.ts b/submissions/CorGit/code/webapp/src/store/reducers/contributions.ts new file mode 100644 index 0000000..5d13e4c --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/reducers/contributions.ts @@ -0,0 +1,38 @@ +import {BaseReducer} from "./index"; +import {ProjectUserContributionInterface} from "../../hooks/useLoadProjectUserContributions"; +import {createSlice} from "@reduxjs/toolkit"; +import {clearError} from "../actions/basicActions"; +import {setUserContributions} from "../actions/contributionsActions"; + +export interface ContributionsReducer extends BaseReducer { + userContributions: ProjectUserContributionInterface[] +} + +/** -- INITIAL STATE */ + +const initialState: ContributionsReducer = { + dispatchError: undefined, + userContributions: [], +}; + +/** --- CREATE THE REDUCER */ + +export const contributionsReducerSlice = createSlice({ + name: 'contributions', + initialState, + reducers: { + clearError, + setUserContributions + }, + extraReducers: + (builder) => { + + } + } +); + +export const contributionsReducerActions = { + setUserContributions: contributionsReducerSlice.actions.setUserContributions +} + +export default contributionsReducerSlice.reducer diff --git a/submissions/CorGit/code/webapp/src/store/reducers/github.ts b/submissions/CorGit/code/webapp/src/store/reducers/github.ts new file mode 100644 index 0000000..9bde8d5 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/reducers/github.ts @@ -0,0 +1,38 @@ +import {BaseReducer} from "./index"; +import {PullRequest} from "../../utils/ProjectTypes/Project.types"; +import {createSlice} from "@reduxjs/toolkit"; +import {clearError} from "../actions/basicActions"; +import {setPullRequest} from "../actions/githubActions"; + +export interface GithubReducer extends BaseReducer { + pullRequest: PullRequest | undefined, +} + +/** -- INITIAL STATE */ + +const initialState: GithubReducer = { + dispatchError: undefined, + pullRequest: undefined, +}; + +/** --- CREATE THE REDUCER */ + +export const githubReducerSlice = createSlice({ + name: 'github', + initialState, + reducers: { + clearError, + setPullRequest + }, + extraReducers: + (builder) => { + + } + } +); + +export const githubReducerActions = { + setPullRequest: githubReducerSlice.actions.setPullRequest +} + +export default githubReducerSlice.reducer diff --git a/submissions/CorGit/code/webapp/src/store/reducers/index.ts b/submissions/CorGit/code/webapp/src/store/reducers/index.ts new file mode 100644 index 0000000..446f6ee --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/reducers/index.ts @@ -0,0 +1,52 @@ +import {combineReducers} from "redux"; +import {UiReducer, uiReducerSlice} from "./ui"; +import {UserAccountReducer, userAccountReducerSlice} from "./userAccount"; +import {ErrorsEnum} from "../../utils/ProjectTypes/Errors.enum"; +import {GithubReducer, githubReducerSlice} from "./github"; +import {CgProjectReducer, cgProjectReducerSlice} from "./cgProject"; +import {ContributionsReducer, contributionsReducerSlice} from "./contributions"; + +interface RootReducer { + ui: UiReducer, + userAccount: UserAccountReducer, + contributions: ContributionsReducer, + github: GithubReducer, + cgProject: CgProjectReducer +} + +const rootReducer = combineReducers({ + ui: uiReducerSlice.reducer, + contributions: contributionsReducerSlice.reducer, + userAccount: userAccountReducerSlice.reducer, + github: githubReducerSlice.reducer, + cgProject: cgProjectReducerSlice.reducer +}); + +export default rootReducer; + + + + +/** -- DEFINE THE BASE REDUCER -- */ + +/** + * Basic reducer interface, with members common to all reducers + */ +export interface BaseReducer { + dispatchError?: DispatchError | undefined +} + +/** + * Single error element, in response to a specific dispatch action + * + * @property {string} code - custom internal code + * @property {string} message - customer error message + * @property {string} action - the action that caused this error + */ +export interface DispatchError { + code?: ErrorsEnum | string, + message: string, + action: string +} + + diff --git a/submissions/CorGit/code/webapp/src/store/reducers/ui.ts b/submissions/CorGit/code/webapp/src/store/reducers/ui.ts new file mode 100644 index 0000000..427f6f3 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/reducers/ui.ts @@ -0,0 +1,71 @@ +import {createSlice} from "@reduxjs/toolkit"; +import {set, toggle} from "../actions/uiActions"; +import {BaseReducer} from "./index"; + +/** -- DEFINITIONS */ + +/** + * Define the shape of the reducer, by specifying the type of element accepted in each reducer elements + * + * @property {UiReducerElement} isBackdropActive - if the backdrop should be active or not + */ +export interface UiReducer extends BaseReducer { + showLoginDialog: UiReducerElement, + loginMethodSelected: UiReducerElement, +} + +/** + * The single value of an entry in this ui reducer + * + * @property {boolean} state - true or false to say it's enable / disabled + * @property {T} value - a custom value that can be associated + */ +export interface UiReducerElement { + state: boolean, + value: T +} + +/** + * Define the possible values of the UI Store + */ +export enum AllowedUiReducerName { + ShowLoginDialog = "showLoginDialog", // show / hide the login dialog in the /login page + LoginMethodSelected = "loginMethodSelected", // shows the path for the given login selected by the user +} + +/** + * Represent the constants to identify the type of logins available to the user + */ +export enum LoginMethods { + NOT_SELECTED = "NOT_SELECTED", // when nothis has been selected + WALLET= "WALLET", + EMAIL= "EMAIL" +} + +/** -- INITIAL STATE */ + +const initialState: UiReducer = { + showLoginDialog: { state: false, value: false }, + loginMethodSelected: { state: true, value: LoginMethods.NOT_SELECTED} +}; + +/** --- CREATE THE REDUCER */ + +export const uiReducerSlice = createSlice({ + name: 'ui', + initialState, + reducers: { + set, + toggle + } +}) + + +export const uiReducerActions = { + set: uiReducerSlice.actions.set, + toggle: uiReducerSlice.actions.toggle +} + + +export default uiReducerSlice.reducer + diff --git a/submissions/CorGit/code/webapp/src/store/reducers/userAccount.ts b/submissions/CorGit/code/webapp/src/store/reducers/userAccount.ts new file mode 100644 index 0000000..0033861 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/store/reducers/userAccount.ts @@ -0,0 +1,53 @@ +import {createSlice} from "@reduxjs/toolkit"; +import {setChainId, setConnectedAccount} from "../actions/userAccountActions"; +import {BaseReducer} from "./index"; +import {clearError} from "../actions/basicActions"; + +/** -- DEFINITIONS */ + + +/** + * Define the shape of the reducer, by specifying the type of element accepted in each reducer elements + * + * @param {string} connectedWalletAddress - address of the metamask wallet connected + * + */ +export interface UserAccountReducer extends BaseReducer { + connectedWalletAddress: string, + chainId: number +} + + +/** -- INITIAL STATE */ + +const initialState: UserAccountReducer = { + dispatchError: undefined, + connectedWalletAddress: "", + chainId: 0 +}; + + + +/** --- CREATE THE REDUCER */ + +export const userAccountReducerSlice = createSlice({ + name: 'userAccount', + initialState, + reducers: { + clearError, + setConnectedAccount, + setChainId + }, + extraReducers: + (builder) => { + + } +}) + +export const userAccountReducerActions = { + clearError: userAccountReducerSlice.actions.clearError, + setConnectedAccount: userAccountReducerSlice.actions.setConnectedAccount, + setChainId: userAccountReducerSlice.actions.setChainId +} + +export default userAccountReducerSlice.reducer diff --git a/submissions/CorGit/code/webapp/src/ui/atmos/Common.Backdrop/Common.Backdrop.tsx b/submissions/CorGit/code/webapp/src/ui/atmos/Common.Backdrop/Common.Backdrop.tsx new file mode 100644 index 0000000..2732c86 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/atmos/Common.Backdrop/Common.Backdrop.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import {Backdrop, CircularProgress} from "@mui/material"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const CommonBackdrop: React.FC = (props) => { + return ( +
+ theme.zIndex.drawer + 1 }} + open={props.show} + onClick={() => props.toggleClose} + > + + +
+ ); +}; + +export interface ICommonBackdrop { + show: boolean, + toggleClose: () => void +} + +export default CommonBackdrop; diff --git a/submissions/CorGit/code/webapp/src/ui/atmos/Project.SingleDetailCard/Project.SingleDetailCard.tsx b/submissions/CorGit/code/webapp/src/ui/atmos/Project.SingleDetailCard/Project.SingleDetailCard.tsx new file mode 100644 index 0000000..6fa6d12 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/atmos/Project.SingleDetailCard/Project.SingleDetailCard.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import {Paper, Typography} from "@mui/material"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const ProjectSingleDetailCard: React.FC = (props) => { + return ( + + {props.name} + {props.value} + + ); +}; + +export interface IProjectSingleDetailCard { + name: string, + value: string +} + +export default ProjectSingleDetailCard; diff --git a/submissions/CorGit/code/webapp/src/ui/organisms/Common.Header/Common.Header.tsx b/submissions/CorGit/code/webapp/src/ui/organisms/Common.Header/Common.Header.tsx new file mode 100644 index 0000000..0a4e12c --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/organisms/Common.Header/Common.Header.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import {Box, Typography} from "@mui/material"; +import {useNavigate} from "react-router-dom"; +import {RouteKey} from "../../../App.Routes"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const CommonHeader: React.FC = (props) => { + const navigate = useNavigate(); + return ( + + {navigate(RouteKey.Home)}} + color="primary"> + CorGit + + + ); +}; + +export interface ICommonHeader { + +} + +export default CommonHeader; diff --git a/submissions/CorGit/code/webapp/src/ui/organisms/Common.PageWrapper/Common.PageWrapper.tsx b/submissions/CorGit/code/webapp/src/ui/organisms/Common.PageWrapper/Common.PageWrapper.tsx new file mode 100644 index 0000000..ce53ab9 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/organisms/Common.PageWrapper/Common.PageWrapper.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import CommonHeader from "../Common.Header/Common.Header"; +import {Box} from "@mui/material"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const CommonPageWrapper: React.FC = (props) => { + return ( + + + + + {props.children} + + + + ); +}; + +export interface ICommonPageWrapper { + children?: JSX.Element | JSX.Element[]; + customWidth?: string | number +} + +export default CommonPageWrapper; diff --git a/submissions/CorGit/code/webapp/src/ui/organisms/Common.PageWrapper/SingleCreateElement.tsx b/submissions/CorGit/code/webapp/src/ui/organisms/Common.PageWrapper/SingleCreateElement.tsx new file mode 100644 index 0000000..e0aa772 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/organisms/Common.PageWrapper/SingleCreateElement.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import {Grid, TextField, Typography} from "@mui/material"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const SingleCreateElement: React.FC = (props) => { + return ( + + + {props.name} + {props.description} + + + + + + ); +}; + +export interface ISingleCreateElement { + name: string; + description: string; + value: string; + onChange: (event) => void; + isNumber?: boolean +} + +export default SingleCreateElement; diff --git a/submissions/CorGit/code/webapp/src/ui/organisms/Project.AddCollateralDialog/Project.AddCollateralDialog.tsx b/submissions/CorGit/code/webapp/src/ui/organisms/Project.AddCollateralDialog/Project.AddCollateralDialog.tsx new file mode 100644 index 0000000..6cef58d --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/organisms/Project.AddCollateralDialog/Project.AddCollateralDialog.tsx @@ -0,0 +1,143 @@ +import { + Box, + Button, + CircularProgress, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Grid, + Slide, + TextField, + Typography +} from '@mui/material'; +import React, {useEffect, useState} from 'react'; +import {TransitionProps} from "@mui/material/transitions"; +import {useAddCollateral} from "../../../hooks/useAddCollateral"; +import {useAccount, useSigner} from "wagmi"; +import {useParams} from "react-router"; +import {useLoadProjectUserContributions} from "../../../hooks/useLoadProjectUserContributions"; +import {useAppSelector} from "../../../hooks/reduxHooks"; + +const Transition = React.forwardRef(function Transition( + props: TransitionProps & { + children: React.ReactElement; + }, + ref: React.Ref, +) { + return ; +}); + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const ProjectAddCollateralDialog: React.FC = (props) => { + + const [valueEth, setValueEth] = useState("0"); + const tokenSymbol = useAppSelector(state => state.cgProject?.tokenSymbol); + + const { address, isConnected } = useAccount(); + let { tokenAddress } = useParams(); + const {data: signer} = useSigner({chainId: 5}); + const {completed, transactionHash, error, checkNow} = useAddCollateral({cgTokenAddress: tokenAddress}); + let { loading: loadingCgProjectContributions, + error: errorLoadCjProjectContributions, + projectUserContributions, checkNow: checkProjectContributions } = useLoadProjectUserContributions(tokenAddress); + + useEffect(() => { + if (completed) { + checkProjectContributions({ + signer: signer, + address: address + }); + props.close(); + } + }, [completed]); + + return ( + + + Add Collateral + + + + Fund your projects by adding collateral. Rewards will be used to reward previous contributors, + and to mint new tokens, according to your distribution reward value + + + {/* Enter the amount */} + + + {setValueEth(e.target.value)}} + InputProps={{ + sx: { + textAlign: "right" + } + }} + /> + + + ETH + + + Max 15,345 + + + + + + + After the operation completes + + + ${tokenSymbol} value + 0,00000 ETH + Amount Token minted + 12,456 + Increase token value + +120 % + + + + { + transactionHash ? + + : + + + + + } + + + + ); +}; + +export interface IProjectAddCollateralDialog { + show: boolean, + close: () => void +} + +export default ProjectAddCollateralDialog; diff --git a/submissions/CorGit/code/webapp/src/ui/organisms/Project.UserRewardsList/Project.UserRewardsList.tsx b/submissions/CorGit/code/webapp/src/ui/organisms/Project.UserRewardsList/Project.UserRewardsList.tsx new file mode 100644 index 0000000..97d2381 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/organisms/Project.UserRewardsList/Project.UserRewardsList.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import {Box} from "@mui/material"; +import RewardLine from "./RewardLine"; +import {ProjectUserContributionInterface} from "../../../hooks/useLoadProjectUserContributions"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const ProjectUserRewardsList: React.FC = (props) => { + return ( + + { + props.contributionList.length === 0 ? + "You don't have any paid contribution yet in this project" + : + props.contributionList.map((contribution) => + + ) + } + + ); +}; + +export interface IProjectUserRewardsList { + contributionList: ProjectUserContributionInterface[] + +} + +export default ProjectUserRewardsList; diff --git a/submissions/CorGit/code/webapp/src/ui/organisms/Project.UserRewardsList/RewardLine.tsx b/submissions/CorGit/code/webapp/src/ui/organisms/Project.UserRewardsList/RewardLine.tsx new file mode 100644 index 0000000..8d29697 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/organisms/Project.UserRewardsList/RewardLine.tsx @@ -0,0 +1,90 @@ +import React, {useEffect} from 'react'; +import {Box, Button, CircularProgress, Grid, Typography} from "@mui/material"; +import {Check} from "@mui/icons-material"; +import { + ProjectUserContributionInterface, + useLoadProjectUserContributions +} from "../../../hooks/useLoadProjectUserContributions"; +import {useClaimRewards} from "../../../hooks/useClaimRewards"; +import {useAccount, useProvider, useSigner} from 'wagmi'; +import {useParams} from "react-router"; +import {format} from 'date-fns'; +import {useAppSelector} from "../../../hooks/reduxHooks"; +import {useLoadCgProject} from "../../../hooks/useLoadCgProject"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const RewardLine: React.FC = (props) => { + + const { address, isConnected } = useAccount(); + let { tokenAddress } = useParams(); + const {data: signer} = useSigner({chainId: 5}); + const {completed, transactionHash, error, checkNow} = useClaimRewards({cgTokenAddress: tokenAddress}); + const provider = useProvider(); + let { loading: loadingCgProject, + error: errorLoadCjProject, + checkNow: loadProjectData } = useLoadCgProject(tokenAddress); + let { loading: loadingCgProjectContributions, + error: errorLoadCjProjectContributions, + projectUserContributions, checkNow: checkProjectContributions } = useLoadProjectUserContributions(tokenAddress); + + const tokenSymbol = useAppSelector(state => state.cgProject?.tokenSymbol); + + useEffect(() => { + if (completed) { + loadProjectData(signer, provider, address); + checkProjectContributions({ + signer: signer, + address: address + }); + } + + }, [completed]) + + return ( + + + {props.contribution.name} + { + format(new Date(props.contribution.creation * 1000), "d LLL yyyy @ h:mm aaa") + } + + + {props.contribution.amount} ${tokenSymbol} + + + { + props.contribution.paid ? + + Claimed + + + : + transactionHash ? + + : + + } + + + ); +}; + +export interface IRewardLine { + contribution: ProjectUserContributionInterface +} + +export default RewardLine; diff --git a/submissions/CorGit/code/webapp/src/ui/organisms/Reward.PullRequestViewer/Reward.PullRequestViewer.tsx b/submissions/CorGit/code/webapp/src/ui/organisms/Reward.PullRequestViewer/Reward.PullRequestViewer.tsx new file mode 100644 index 0000000..7992159 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/organisms/Reward.PullRequestViewer/Reward.PullRequestViewer.tsx @@ -0,0 +1,129 @@ +import React, {useEffect, useMemo, useState} from 'react'; +import {Box, Button, CircularProgress, Typography} from "@mui/material"; +import {PullRequest, PullRequestContributor} from "../../../utils/ProjectTypes/Project.types"; +import SingleContributorLine from "./SingleContributorLine"; +import {theme} from "../../../GlobalStyles"; +import {useParams} from "react-router"; +import {useCreateRewardContributions} from "../../../hooks/useCreateRewardContributions"; +import {useNavigate} from "react-router-dom"; +import {useSigner} from "wagmi"; +import {BigNumber} from "@ethersproject/bignumber"; +import {format} from "date-fns"; +import {useAppSelector} from "../../../hooks/reduxHooks"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const RewardPullRequestViewer: React.FC = (props) => { + + const [contributorRewards, setContributorRewards] = useState<{ c: PullRequestContributor, amount: string }[]>([]); + let { tokenAddress } = useParams(); + const navigate = useNavigate(); + const { data: signer, isError, isLoading } = useSigner(); + const tokenSymbol = useAppSelector(state => state.cgProject?.tokenSymbol); + + const {completed, transactionHash, error: createContributionError, checkNow: createContribution} + = useCreateRewardContributions({cgTokenAddress: tokenAddress}); + + useEffect(() => { + if (completed) + navigate(`/project/${tokenAddress}`); + }, [completed]); + + useEffect(() => { + let newContributorsRewards = props.pullRequest.contributors.map((c) => ({ + c, amount: "0" + })); + setContributorRewards(newContributorsRewards); + }, [props.pullRequest]); + + const editContributorReward = (pos: number, amount: string) => { + let cr = contributorRewards; + cr[pos].amount = amount; + setContributorRewards(JSON.parse(JSON.stringify(cr))); + } + + const totalAmount = useMemo(() => { + return contributorRewards.reduce((a,b) => a+parseInt(b.amount ? b.amount : "0"), 0); + }, [contributorRewards]); + + return ( + + {props.pullRequest.title} + Closed { + format(new Date(props.pullRequest.closedAt * 1000), "d LLL yyyy @ h:mm aaa") + } + + Total of {props.pullRequest.contributors.length} contributors + + { + contributorRewards.map((c, i) => + {editContributorReward(i, newAmount)}}/> + ) + } + + {/* TOTAL */} + { + props.pullRequest.contributors.length > 0 ? + + + TOTAL + + {totalAmount} + ${tokenSymbol} + + : + "" + } + + {/* REWARD BUTTON */} + { + props.pullRequest.contributors.length > 0 ? + transactionHash ? + + + + : + + + + + : + "" + } + + ); +}; + +export interface IRewardPullRequestViewer { + pullRequest: PullRequest +} + +export default RewardPullRequestViewer; diff --git a/submissions/CorGit/code/webapp/src/ui/organisms/Reward.PullRequestViewer/SingleContributorLine.tsx b/submissions/CorGit/code/webapp/src/ui/organisms/Reward.PullRequestViewer/SingleContributorLine.tsx new file mode 100644 index 0000000..f5969b9 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/organisms/Reward.PullRequestViewer/SingleContributorLine.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import {Avatar, Box, TextField, Typography} from "@mui/material"; +import {PullRequestContributor} from "../../../utils/ProjectTypes/Project.types"; +import {useAppSelector} from "../../../hooks/reduxHooks"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const SingleContributorLine: React.FC = (props) => { + + const tokenSymbol = useAppSelector(state => state.cgProject?.tokenSymbol); + + const handleChange = (e) => { + props.editReward(e.target.value); + } + + return ( + + + + {props.contributor.username} + {props.contributor.commits.length} commit{props.contributor.commits.length > 1 ? "s" : ""} + + + ${tokenSymbol} + + ); +}; + +export interface ISingleContributorLine { + contributor: PullRequestContributor, + contributorReward: string, + editReward: (string) => void +} + +export default SingleContributorLine; diff --git a/submissions/CorGit/code/webapp/src/ui/pages/Create/Create.tsx b/submissions/CorGit/code/webapp/src/ui/pages/Create/Create.tsx new file mode 100644 index 0000000..f2403e2 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/pages/Create/Create.tsx @@ -0,0 +1,84 @@ +import React, {useEffect, useState} from 'react'; +import {Box, Button, CircularProgress, Typography} from "@mui/material"; +import CommonPageWrapper from "../../organisms/Common.PageWrapper/Common.PageWrapper"; +import SingleCreateElement from "../../organisms/Common.PageWrapper/SingleCreateElement"; +import {RocketLaunch} from "@mui/icons-material"; +import {useCreateCgProject} from "../../../hooks/useCreateCgProject"; +import {useAccount, useSigner} from "wagmi"; +import {useNavigate} from "react-router-dom"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const Create: React.FC = (props) => { + + const [projectName, setProjectName, ] = useState(""); + const [projectSymbol, setProjectSymbol, ] = useState(""); + const [projectPrevContRew, setProjectPrevContRew, ] = useState(""); + const {data} = useSigner({chainId: 5}); + const {transactionHash, error, tokenAddress, checkNow} = useCreateCgProject(); + const { address, isConnected } = useAccount(); + const navigate = useNavigate(); + + useEffect(() => { + if (tokenAddress) + navigate(`/project/${tokenAddress}`); + }, [tokenAddress]); + + return ( + + + + Create a new cgToken + + + Enter the details of your cgToken, then add a .cgToken.json file on your repository main root. + + setProjectName(e.target.value)}/> + setProjectSymbol(e.target.value)}/> + setProjectPrevContRew(e.target.value)}/> + + { + transactionHash ? + + : + + } + + + + + + ); +}; + +export interface ICreate { + +} + +export default Create; diff --git a/submissions/CorGit/code/webapp/src/ui/pages/Home/Home.tsx b/submissions/CorGit/code/webapp/src/ui/pages/Home/Home.tsx new file mode 100644 index 0000000..0fc03a8 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/pages/Home/Home.tsx @@ -0,0 +1,96 @@ +import React, {useEffect, useState} from 'react'; +import {Box, Button, CircularProgress, TextField} from "@mui/material"; +import {theme} from "../../../GlobalStyles"; +import {RocketLaunch} from "@mui/icons-material"; +import {useSearchCgProject} from "../../../hooks/useSearchCgProject"; +import {useDebounce} from "use-debounce"; +import {RouteKey} from "../../../App.Routes"; +import {useNavigate} from 'react-router-dom'; +import {useAccount, useConnect} from "wagmi"; +import {InjectedConnector} from 'wagmi/connectors/injected'; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const Home: React.FC = (props) => { + const { loading, address, error, checkNow } = useSearchCgProject(); + const [tokenSearchValue, setTokenSearchValue] = useState(""); + const [searchCgProjectValue] = useDebounce(tokenSearchValue, 500); + const navigate = useNavigate(); + const account = useAccount(); + const { connect } = useConnect({ + connector: new InjectedConnector(), + chainId: 5 + }) + + useEffect(() => { + if (searchCgProjectValue) + checkNow(searchCgProjectValue); + }, [searchCgProjectValue]); + + // redirect on address found + useEffect(() => { + if (address) + navigate(`/project/${address}`); + }, [address]); + + const onInputChange = (e) => { + setTokenSearchValue(e.target.value); + } + + return ( + +

+ CorGit +

+
+ Open Source project tokenization +
+ + }: {} ) + }} + placeholder={"Search by Github Repo URL or Token Address"} /> + + + + + { + account.address ? + "" + : + + } + + + + +
+ ); +}; + +export interface IHome { + +} + +export default Home; diff --git a/submissions/CorGit/code/webapp/src/ui/pages/ProjectPage/ProjectPage.tsx b/submissions/CorGit/code/webapp/src/ui/pages/ProjectPage/ProjectPage.tsx new file mode 100644 index 0000000..d5b1312 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/pages/ProjectPage/ProjectPage.tsx @@ -0,0 +1,135 @@ +import React, {useEffect, useState} from 'react'; +import CommonPageWrapper from "../../organisms/Common.PageWrapper/Common.PageWrapper"; +import {Box, Button, Grid, Typography} from "@mui/material"; +import ProjectSingleDetailCard from "../../atmos/Project.SingleDetailCard/Project.SingleDetailCard"; +import ProjectUserRewardsList from "../../organisms/Project.UserRewardsList/Project.UserRewardsList"; +import {useNavigate} from "react-router-dom"; +import {useParams} from "react-router"; +import ProjectAddCollateralDialog from "../../organisms/Project.AddCollateralDialog/Project.AddCollateralDialog"; +import CommonBackdrop from "../../atmos/Common.Backdrop/Common.Backdrop"; +import {useLoadCgProject} from "../../../hooks/useLoadCgProject"; +import {useLoadProjectUserContributions} from "../../../hooks/useLoadProjectUserContributions"; +import {useAppSelector} from "../../../hooks/reduxHooks"; +import {useAccount, useProvider, useSigner} from "wagmi"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const ProjectPage: React.FC = (props) => { + + const [showAddCollateral, setShowAddCollateral] = useState(false); + const [showLoader, setShowLoader] = useState(false); + const navigate = useNavigate(); + let { tokenAddress } = useParams(); + const tokenSymbol = useAppSelector(state => state.cgProject?.tokenSymbol); + let { loading: loadingCgProject, + error: errorLoadCjProject, + checkNow: loadProjectData } = useLoadCgProject(tokenAddress); + let { loading: loadingCgProjectContributions, + error: errorLoadCjProjectContributions, + projectUserContributions, checkNow: checkProjectContributions } = useLoadProjectUserContributions(tokenAddress); + const { data: signer, error: errorSigner, isLoading: isLoadingSigner } = useSigner(); + + const provider = useProvider(); + const { address, isConnected } = useAccount(); + + const project = useAppSelector(state => state.cgProject); + const contributions = useAppSelector(state => state.contributions.userContributions); + + useEffect(() => { + // if (tokenAddress && isConnected && !isLoadingSigner && signer + // && provider.network.chainId === 280) { + if (tokenAddress && isConnected && !isLoadingSigner) { + loadProjectData(signer, provider, address); + checkProjectContributions({ + signer: signer, + address: address + }); + } + }, [tokenAddress, isConnected, isLoadingSigner, signer, provider]); + + useEffect(() => { + setShowLoader(loadingCgProject || loadingCgProjectContributions); + }, [loadingCgProject, loadingCgProjectContributions]); + + return ( + + + + {project.tokenName} + ${project.tokenSymbol} + { + tokenAddress.substring(0,6) + "..." + tokenAddress.substring(38) + } + + + + { + project.isPayer ? + + : + "" + } + + + + {/* Section of details */} + + + + + + + + + + + + + + + + + + + + + + {/* Section of unclaimed rewards*/} + + Your unclaimed rewards + + + + + + {/* Dialog to add collateral */} + {setShowAddCollateral(false)}}/> + + {/* Show a Backdrop loader */} + setShowLoader(false)}/> + + + ); +}; + +export interface IProjectPage { + +} + +export default ProjectPage; diff --git a/submissions/CorGit/code/webapp/src/ui/pages/Reward/Reward.tsx b/submissions/CorGit/code/webapp/src/ui/pages/Reward/Reward.tsx new file mode 100644 index 0000000..c8d0c18 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/ui/pages/Reward/Reward.tsx @@ -0,0 +1,65 @@ +import React, {useEffect, useState} from 'react'; +import CommonPageWrapper from "../../organisms/Common.PageWrapper/Common.PageWrapper"; +import {Box, CircularProgress, TextField, Typography} from "@mui/material"; +import {useGetPullRequestDetails} from "../../../hooks/useGetPullRequestDetails"; +import {useDebounce} from "use-debounce"; +import {useAppSelector} from "../../../hooks/reduxHooks"; +import {PullRequest} from "../../../utils/ProjectTypes/Project.types"; +import RewardPullRequestViewer from "../../organisms/Reward.PullRequestViewer/Reward.PullRequestViewer"; + +/** + * + * @param {React.PropsWithChildren} props + * @return {JSX.Element} + * @constructor + */ +const Reward: React.FC = (props) => { + + const [urlSearchValue, setUrlSearchValue] = useState(""); + const tokenName = useAppSelector(state => state.cgProject?.tokenName); + const [searchCgProjectValue] = useDebounce(urlSearchValue, 500); + const {loading, error, pullRequestUrl, checkNow} = useGetPullRequestDetails(); + + const pullRequest: PullRequest = useAppSelector(state => state.github.pullRequest); + + useEffect(() => { + if (searchCgProjectValue) + checkNow(searchCgProjectValue); + }, [searchCgProjectValue]); + + return ( + + + {tokenName} + - Reward Page - + + + {setUrlSearchValue(e.target.value)}} + InputProps={{ + ...( loading ? {endAdornment: }: {} ) + }} + placeholder={"Enter the Github Pull Request URL"} /> + + + { + pullRequest ? + + + + : + "" + } + + + + ); +}; + +export interface IReward { + +} + +export default Reward; diff --git a/submissions/CorGit/code/webapp/src/utils/ProjectTypes/Errors.enum.ts b/submissions/CorGit/code/webapp/src/utils/ProjectTypes/Errors.enum.ts new file mode 100644 index 0000000..85d9c51 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/utils/ProjectTypes/Errors.enum.ts @@ -0,0 +1,8 @@ +export enum ErrorsEnum { + PROOF_0001 = "Cannot correctly load all the proofs", + PROOF_0002 = "Cannot eval hashes of selected files", + PROOF_0003 = "Error uploading files to temporary S3", + PROOF_0004 = "Error minting the transaction", + PROOF_0005 = "Failed reading the prices", + PROOF_0006 = "Failed updating the title", +} diff --git a/submissions/CorGit/code/webapp/src/utils/ProjectTypes/Project.enum.ts b/submissions/CorGit/code/webapp/src/utils/ProjectTypes/Project.enum.ts new file mode 100644 index 0000000..977c4b3 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/utils/ProjectTypes/Project.enum.ts @@ -0,0 +1,20 @@ +export enum ProofVerificationStatus { + NotVerified, + Pending, + Verified, + Failed +} + +export enum StorageType { + ArweaveV1 +} + +export enum Chain { + Goerli=5, + PolygonMainnet=137 +} + +export enum FileMIMEType { + "-"= "-", + "text/html"= "text/html" +} diff --git a/submissions/CorGit/code/webapp/src/utils/ProjectTypes/Project.types.ts b/submissions/CorGit/code/webapp/src/utils/ProjectTypes/Project.types.ts new file mode 100644 index 0000000..d91ff71 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/utils/ProjectTypes/Project.types.ts @@ -0,0 +1,143 @@ +import {Chain, FileMIMEType, ProofVerificationStatus, StorageType} from "./Project.enum"; +import Web3 from "web3"; +import {AbiItem} from "web3-utils"; + +export type address = string; + +export type GithubCommit = { + sha: string, + url: string, + message: string +}; + +export enum PullRequestState { + CLOSED = "closed", + OPEN = "open" +}; + +export type PullRequest = { + id: number, + title: string, + state: PullRequestState, + closedAt: number, + contributors: PullRequestContributor[] +}; + +export type PullRequestContributor = { + id: number, + avatarUrl: string, + username: string, + profileUrl: string, + commits: GithubCommit[] +}; + +/** + * Object of a proof already minted + * + * @param {string} id - the full tokenId represented by a string + * @param {Chain} chain + * @param {number} nftNum - the number of NFT on that chain + * @param {string} title + * @param {string} description + * @param {string} hash + * @param {ProofVerificationStatus} verificationStatus + * @param {number} createdAt + * @param {StorageType?} storageType + * @param {string?} fileUrl + * @param {FileMIMEType?} MIMEType + */ +export type Proof = { + id: string, + chain: Chain, + nftNum: number, + title: string, + description: string, + hash: string, + verificationStatus: ProofVerificationStatus, + createdAt: number, + storageType?: StorageType, + fileUrl?: string, + MIMEType?: FileMIMEType, + verificationFailed: boolean +}; + +/** + * Object of a proof the user is creating and will be minted + * + * @param {string} id - the id of the file + * @param {string} title - the title of Proof (empty by default) + * @param {string} fileName - name of the file on the machine host + * @param {number} size - size in byte of the file + * @param {string} hash - hash of the file + * @param {boolean} toBeVerified - true if the file has to be verified, false otherwise + * @param {number} uploadPerc - in case of upload, the perc of upload + */ +export type ProofToMint = { + id: string, + title: string, + fileName: string, + size: number, + hash: string, + toBeVerified: boolean, + uploadPerc: number +}; + +/** + * Params for the redux action to generate the params (aka, make the mint transaction) + * + * @param {address} address - who's making the transaction (and will receive the NFT too) + * @param {Web3} web3 - web3 instance + * @param {ProofToMint[]} proofs - list of the proofs to mint + * @param {AbiItem} routerAbi - ABI of the router + * @param {address} routerAddress - address of the router + * @param {AbiItem} nftAbi - ABI of the NFT Factory + * @param {address} nftAddress - address of the NFT factory + */ +export type GenerateProofActionParams = { + address: address, + web3: Web3, + proofs: ProofToMint[], + delegatorAddress: address, + routerAbi: AbiItem, + routerAddress: address, + nftAbi: AbiItem, + nftAddress: address, + price: Prices +}; + +/** + * Represents the prices of the service in wei + * + * @param {number} mint - price for just minting (in ETH or equivalent) + * @param {number} verification - price to publish and certify the file (in ETH or equivalent) + */ +export type Prices = { + mint: number, + verification: number +}; + +/** + * Represent the core details for a given deployment, and all its contracts we have to interact + */ +export type DeploymentContractsDetails = { + CG_FACTORY: address, + GITHUB_ADDRESS_REGISTER: address, + CG_FACTORY_ABI: any, + GITHUB_ADDRESS_REGISTER_ABI: any, + CG_PROJECT_ABI: any +}; + +/** + * Core details of each chain we're on + * + * @param {number} ID - the ID of the chain + * @param {string} EXPLORER_URL - the initial part of the explorer url (ex. "https://etherscan.io") + * @param {string} OPENSEA_CHAIN_NAME - the name of the chain in the URL of opensea + * @param {boolean} IS_TESTNET - true if it's a testnet, false otherwise + */ +export type ChainDetails = { + ID: number, + EXPLORER_URL: string, + OPENSEA_CHAIN_NAME: string, + IS_TESTNET: boolean +}; diff --git a/submissions/CorGit/code/webapp/src/utils/Tools/FileManagement.ts b/submissions/CorGit/code/webapp/src/utils/Tools/FileManagement.ts new file mode 100644 index 0000000..f0d5ca0 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/utils/Tools/FileManagement.ts @@ -0,0 +1,46 @@ +/** + * Generates the sha-256 hash of a given File + * + * @param {File} file - the file you want the hash + */ +export const fileToHash = async (file: File): Promise => { + // get byte array of file + let buffer = await file.arrayBuffer(); + // hash the message + const hashBuffer = await crypto.subtle.digest('SHA-256', buffer); + // convert ArrayBuffer to Array + const hashArray = Array.from(new Uint8Array(hashBuffer)); + // convert bytes to hex string + return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); +} + +/** + * Merges two FileList objects and returns a new FileList object + * @param {FileList} fileListA The first FileList object + * @param {FileList} fileListB The second FileList object + */ +export const mergeFileLists = (fileListA: FileList, fileListB: FileList): FileList => { + const dataTransfer = new DataTransfer(); + for (let i = 0; i < fileListA.length; i++) { + dataTransfer.items.add(fileListA[i]); + } + for (let i = 0; i < fileListB.length; i++) { + dataTransfer.items.add(fileListB[i]); + } + return dataTransfer.files; +} + + +/** + * Merges two FileList objects and returns a new FileList object + * @param {FileList} fileList The FileList object + * @param {number} idItem The id of the item to remove (position) + */ +export const removeFromFileList = (fileList: FileList, idItem: number): FileList => { + const dataTransfer = new DataTransfer(); + for (let i = 0; i < fileList.length; i++) { + if (i !== idItem) dataTransfer.items.add(fileList[i]); + } + return dataTransfer.files; +} + diff --git a/submissions/CorGit/code/webapp/src/utils/Tools/Web3Management.ts b/submissions/CorGit/code/webapp/src/utils/Tools/Web3Management.ts new file mode 100644 index 0000000..96170ee --- /dev/null +++ b/submissions/CorGit/code/webapp/src/utils/Tools/Web3Management.ts @@ -0,0 +1,18 @@ +import {Chain} from "../ProjectTypes/Project.enum"; +import {BigNumber} from "@ethersproject/bignumber"; + + +/** + * Transforms a full tokenId of an NFT, into it's ID number, and the chain where it is on. + * @param {string} tokenId - the token id as it's written on the chain + * @return {chain: Chain, nftNum: number} - the combination of chain number and the number of NFT on that chain + */ +export const fromTokenIdToChain = (tokenId: string): { chain: Chain, nftNum: number } => { + let tkId = BigNumber.from(tokenId); + let chainNum = tkId.div(BigNumber.from(10).pow(50)).toNumber(); + let tokenIdNum = tkId.sub(BigNumber.from(10).pow(50).mul(chainNum)).toNumber(); + return { + chain: chainNum, + nftNum: tokenIdNum + } +} diff --git a/submissions/CorGit/code/webapp/src/utils/constants.ts b/submissions/CorGit/code/webapp/src/utils/constants.ts new file mode 100644 index 0000000..4a3e0d4 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/utils/constants.ts @@ -0,0 +1,19 @@ +import {DeploymentContractsDetails} from "./ProjectTypes/Project.types"; + +export const CONTRACTS_DETAILS: {5: DeploymentContractsDetails, 280: DeploymentContractsDetails} = { + 5: { + CG_FACTORY: "0x7c31AD652FdAC7955CD81287343846873B558453", + GITHUB_ADDRESS_REGISTER: "0xd5F210aAe5330308ebc2B015Bfb0e70839251811", + CG_FACTORY_ABI: [{"inputs":[{"internalType":"address","name":"_githubAddressRegister","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_addr","type":"address"},{"indexed":false,"internalType":"string","name":"_name","type":"string"},{"indexed":false,"internalType":"string","name":"_symbol","type":"string"},{"indexed":false,"internalType":"uint16","name":"_percFundingDistribute","type":"uint16"}],"name":"NewCgTokenCreated","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"cgTokenList","outputs":[{"internalType":"contract cgToken","name":"tokenContract","type":"address"},{"internalType":"uint256","name":"createdAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint16","name":"_percFundingDistribute","type":"uint16"}],"name":"generate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"githubAddressRegister","outputs":[{"internalType":"contract GithubAddressRegister","name":"","type":"address"}],"stateMutability":"view","type":"function"}], + GITHUB_ADDRESS_REGISTER_ABI: [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"uint256","name":"_githubID","type":"uint256"},{"internalType":"address","name":"_wallet","type":"address"}],"name":"addAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToGithubID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"githubIDToAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_githubID","type":"uint256"},{"internalType":"address","name":"_addressToRemove","type":"address"}],"name":"removeAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}], + CG_PROJECT_ABI: [{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint256","name":"_initialSupply","type":"uint256"},{"internalType":"uint16","name":"_percFundingDistribute","type":"uint16"},{"internalType":"address","name":"_githubAddressRegister","type":"address"},{"internalType":"address","name":"creator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_numOfUsers","type":"uint256"}],"name":"NewGroupPayment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_githubID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_paymentId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"PaymentClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_githubID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"PaymentPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAYER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_paymentId","type":"uint256"}],"name":"collectPayment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contribute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"convert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockedTokensForPayments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextPaymentId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_githubID","type":"uint256[]"},{"internalType":"uint256[]","name":"_amount","type":"uint256[]"},{"internalType":"string","name":"_name","type":"string"}],"name":"pay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"paymentAmounts","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"paid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"payments","outputs":[{"internalType":"uint256","name":"creation","type":"uint256"},{"internalType":"uint256","name":"totalTokenAmount","type":"uint256"},{"internalType":"uint256","name":"totalTokenClaimed","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"numOfUsers","type":"uint256"},{"internalType":"bool","name":"claimCompleted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"percFundingDistributed","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"percFundingForNewTokens","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userPayments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] + }, + 280: { + CG_FACTORY: "0x84360Fc81b1be9860A37859Be3b48e505D494F38", + GITHUB_ADDRESS_REGISTER: "0xa5B07286eA9a9f7deC44104Cb621f1cf55AA9634", + CG_FACTORY_ABI: [{"inputs":[{"internalType":"address","name":"_githubAddressRegister","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_addr","type":"address"},{"indexed":false,"internalType":"string","name":"_name","type":"string"},{"indexed":false,"internalType":"string","name":"_symbol","type":"string"},{"indexed":false,"internalType":"uint16","name":"_percFundingDistribute","type":"uint16"}],"name":"NewCgTokenCreated","type":"event"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"cgTokenList","outputs":[{"internalType":"contract cgToken","name":"tokenContract","type":"address"},{"internalType":"uint256","name":"createdAt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint16","name":"_percFundingDistribute","type":"uint16"}],"name":"generate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"githubAddressRegister","outputs":[{"internalType":"contract GithubAddressRegister","name":"","type":"address"}],"stateMutability":"view","type":"function"}], + GITHUB_ADDRESS_REGISTER_ABI: [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"uint256","name":"_githubID","type":"uint256"},{"internalType":"address","name":"_wallet","type":"address"}],"name":"addAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToGithubID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"githubIDToAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_githubID","type":"uint256"},{"internalType":"address","name":"_addressToRemove","type":"address"}],"name":"removeAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}], + CG_PROJECT_ABI: [{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint256","name":"_initialSupply","type":"uint256"},{"internalType":"uint16","name":"_percFundingDistribute","type":"uint16"},{"internalType":"address","name":"_githubAddressRegister","type":"address"},{"internalType":"address","name":"creator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_numOfUsers","type":"uint256"}],"name":"NewGroupPayment","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_githubID","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_paymentId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"PaymentClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_githubID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"PaymentPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAYER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_paymentId","type":"uint256"}],"name":"collectPayment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contribute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_to","type":"address"}],"name":"convert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockedTokensForPayments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextPaymentId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_githubID","type":"uint256[]"},{"internalType":"uint256[]","name":"_amount","type":"uint256[]"},{"internalType":"string","name":"_name","type":"string"}],"name":"pay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"paymentAmounts","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"paid","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"payments","outputs":[{"internalType":"uint256","name":"creation","type":"uint256"},{"internalType":"uint256","name":"totalTokenAmount","type":"uint256"},{"internalType":"uint256","name":"totalTokenClaimed","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256","name":"numOfUsers","type":"uint256"},{"internalType":"bool","name":"claimCompleted","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"percFundingDistributed","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"percFundingForNewTokens","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"userPayments","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}] + } +}; + diff --git a/submissions/CorGit/code/webapp/src/utils/localStorage/LocalStorageKeys.ts b/submissions/CorGit/code/webapp/src/utils/localStorage/LocalStorageKeys.ts new file mode 100644 index 0000000..eeb9dc5 --- /dev/null +++ b/submissions/CorGit/code/webapp/src/utils/localStorage/LocalStorageKeys.ts @@ -0,0 +1,8 @@ + +/** + * Enumeration of key values for local storage + * @property {LocalStorageKeys.AuthData} AuthToken - Stores a JSON of type .... + */ +export enum LocalStorageKeys { + AuthData = "authData" +} diff --git a/submissions/CorGit/code/webapp/tsconfig.json b/submissions/CorGit/code/webapp/tsconfig.json new file mode 100644 index 0000000..b958231 --- /dev/null +++ b/submissions/CorGit/code/webapp/tsconfig.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": [ + "src" + ] +}