├── .github └── FUNDING.yml ├── .gitignore ├── README.md ├── compile-generate-soccermanager.sh ├── documentation ├── ethereum-api.jpeg ├── player-api.jpeg ├── project-diagram.excalidraw └── project-diagram.jpeg ├── ethereum-api ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── ivanfranchin │ │ │ └── ethereumapi │ │ │ ├── EthereumApiApplication.java │ │ │ ├── config │ │ │ ├── ErrorAttributesConfig.java │ │ │ ├── SwaggerConfig.java │ │ │ └── Web3jConfig.java │ │ │ ├── contract │ │ │ └── SoccerManager.java │ │ │ ├── rest │ │ │ ├── EthereumController.java │ │ │ └── dto │ │ │ │ ├── CreateWalletDto.java │ │ │ │ ├── DeployContractDto.java │ │ │ │ ├── GetWalletAddressDto.java │ │ │ │ ├── TransferDto.java │ │ │ │ └── WalletDto.java │ │ │ └── service │ │ │ ├── EthereumService.java │ │ │ └── EthereumServiceImpl.java │ └── resources │ │ ├── application.properties │ │ └── banner.txt │ └── test │ └── java │ └── com │ └── ivanfranchin │ └── ethereumapi │ └── EthereumApiApplicationTests.java ├── player-api ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ └── maven-wrapper.properties ├── mvnw ├── mvnw.cmd ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── ivanfranchin │ │ │ └── playerapi │ │ │ ├── PlayerApiApplication.java │ │ │ ├── config │ │ │ ├── ErrorAttributesConfig.java │ │ │ ├── SwaggerConfig.java │ │ │ └── Web3jConfig.java │ │ │ ├── contract │ │ │ └── SoccerManager.java │ │ │ ├── exception │ │ │ └── ContractAddressNotInformedException.java │ │ │ ├── rest │ │ │ ├── PlayerApiController.java │ │ │ └── dto │ │ │ │ ├── AddPlayerDto.java │ │ │ │ ├── BasePlayerDto.java │ │ │ │ ├── BuyPlayerDto.java │ │ │ │ ├── GetPlayerDto.java │ │ │ │ ├── PlayerDto.java │ │ │ │ └── UpdatePlayerDto.java │ │ │ └── service │ │ │ ├── SoccerManagerService.java │ │ │ └── SoccerManagerServiceImpl.java │ └── resources │ │ ├── application.properties │ │ └── banner.txt │ └── test │ └── java │ └── com │ └── ivanfranchin │ └── playerapi │ └── PlayerApiApplicationTests.java └── solidity └── SoccerManager.sol /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ivangfr 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /solidity/*.bin 2 | /solidity/*.abi 3 | 4 | UTC--*.json 5 | 6 | target/ 7 | !.mvn/wrapper/maven-wrapper.jar 8 | !**/src/main/**/target/ 9 | !**/src/test/**/target/ 10 | 11 | ### STS ### 12 | .apt_generated 13 | .classpath 14 | .factorypath 15 | .project 16 | .settings 17 | .springBeans 18 | .sts4-cache 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | 26 | ### NetBeans ### 27 | /nbproject/private/ 28 | /nbbuild/ 29 | /dist/ 30 | /nbdist/ 31 | /.nb-gradle/ 32 | build/ 33 | !**/src/main/**/build/ 34 | !**/src/test/**/build/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | 39 | ### MAC OS ### 40 | *.DS_Store 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ethereum-springboot-react 2 | 3 | The project goals are: 4 | 5 | 1. Implement an **Ethereum Smart Contract** called `SoccerManager` (using [Solidity](https://solidity.readthedocs.io/en/latest) programming language) and deploy it to [Ethereum Blockchain](https://www.ethereum.org) running locally using [ethereum/client-go](https://github.com/ethereum/go-ethereum) docker image; 6 | 7 | 1. Implement two [`Spring Boot`](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) backend applications, `ethereum-api` and `player-api`, that uses [Web3j](https://docs.web3j.io/getting_started.html) library to communicate with Ethereum blockchain; 8 | 9 | 1. Implement two [`React`](https://react.dev/) frontend applications, `ethereum-ui` and `player-ui`, that communicate to their respective backend application. 10 | 11 | ## Proof-of-Concepts & Articles 12 | 13 | On [ivangfr.github.io](https://ivangfr.github.io), I have compiled my Proof-of-Concepts (PoCs) and articles. You can easily search for the technology you are interested in by using the filter. Who knows, perhaps I have already implemented a PoC or written an article about what you are looking for. 14 | 15 | ## Project Diagram 16 | 17 | ![project-diagram](documentation/project-diagram.jpeg) 18 | 19 | ## Ethereum Smart Contract 20 | 21 | `Ethereum Smart Contract` is a program that runs on an `EVM` (`Ethereum Virtual Machine`) similar to a Java program that runs on `JVM` (`Java Virtual Machine`). A contract is a collection of code (its functions) and data (its state) that resides at a specific address on the Ethereum Blockchain. Ethereum Smart Contracts are usually written in `Solidity` programming language. 22 | 23 | In order to implement smart contracts we used [Remix](https://remix.ethereum.org). It's a powerful, open source tool that helps you write contracts using Solidity straight from the browser. 24 | 25 | - **SoccerManager** 26 | 27 | `SoccerManager` is a smart contract that handles soccer players. Once deployed, it has some pre-defined soccer players registered. Initially, the agent of those pre-defined players is the owner of the contract (the wallet address used to deploy the contract). Besides, only the owner of the contract can add players. Other wallets (agent wallets) can buy soccer players and, once it is done, the agent wallet becomes the owner of the player. 28 | 29 | ## Applications 30 | 31 | - **ethereum-api** 32 | 33 | `Spring Boot`application that communicates with Ethereum Blockchain, using `Web3j` library. `ethereum-api` provides some endpoints to create a new wallet, transfer ether from one wallet to another, etc. 34 | 35 | ![ethereum-api](documentation/ethereum-api.jpeg) 36 | 37 | - **player-api** 38 | 39 | `Spring Boot` application that calls `SoccerManager` smart contract `public functions` using `Web3j`. It exposes some endpoints so that you can buy a player, get info about the player, add players, etc. 40 | 41 | Some endpoints, such `POST /api/players/add`, requires the use of the **owner contract wallet**, i.e, the wallet that was used to deploy `SoccerManager` smart contract. 42 | 43 | ![player-api](documentation/player-api.jpeg) 44 | 45 | - **ethereum-ui** (TODO) 46 | 47 | `React` frontend application that provides a User Interface so that we can create a wallet, check its balance, transfer ethereum to other wallets, etc. 48 | 49 | - **player-ui** (TODO) 50 | 51 | `React` frontend application that provides a User Interface to easily play with Ethereum Blockchain and `SoccerManager` smart contract. Using `Web3j`, it listens to `PlayerAdded`, `PlayerUpdated` and `PlayerBought` event emitted from `SoccerManager` contract (and some other logs from Ethereum Blockchain) and updates the screen on-the-fly. Besides, `player-ui` communicates directly with `player-api` whenever it needs some information from `SoccerManager` contract. 52 | 53 | ## Prerequisites 54 | 55 | - [`Java 17+`](https://www.oracle.com/java/technologies/downloads/#java17) 56 | - [`Docker`](https://www.docker.com/) 57 | - [`jq`](https://stedolan.github.io/jq) 58 | 59 | ## Run Ethereum locally 60 | 61 | In a terminal, run the docker command below. It starts a container in development mode and exposes `Ethereum RPC API` on port `8545`. 62 | ``` 63 | docker run -d --rm --name ethereum \ 64 | -p 8545:8545 -p 30303:30303 \ 65 | ethereum/client-go:v1.9.25 \ 66 | --rpc --rpcaddr "0.0.0.0" --rpcapi="db,eth,net,web3,personal" --rpccorsdomain "*" --dev 67 | ``` 68 | 69 | > Run the following `docker exec` command if you want to enter in the `Geth`’s interactive JavaScript console inside Docker container. It provides a lot of features such as: create a wallet, check waller balance, transfer ether from one address to another, etc. We won't focus on it because we've decided to implement such features in `ethereum-api` using `Web3j`. In order to get more information visite [Geth JavaScript console documentation](https://geth.ethereum.org/docs/interacting-with-geth/javascript-console) 70 | > ``` 71 | > docker exec -it ethereum geth attach ipc:/tmp/geth.ipc 72 | > ``` 73 | 74 | ## Compile Smart Contract 75 | 76 | - Access https://github.com/web3j/web3j/releases/tag/v4.5.5 and download `web3j-4.5.5.zip` 77 | 78 | - Unzip it to your preferred location 79 | 80 | - In a terminal, navigate to `ethereum-springboot-react` root folder 81 | 82 | - Export to `WEB3J_PATH` environment variable, the absolute path of `Web3j` where you have unzipped 83 | ``` 84 | export WEB3J_PATH=path/to/web3j-4.5.5 85 | ``` 86 | 87 | - Run the following script. It will compile Solidity `SoccerManager` code, `solidity/SoccerManager.sol`. When the compilation finishes, it will produce the files: `solidity/SoccerManager.abi` and `solidity/SoccerManager.bin`. Then, the script uses these two files to generate the `SoccerManager.java` in `ethereum-api` and `player-api`. 88 | ``` 89 | ./compile-generate-soccermanager.sh 90 | ``` 91 | 92 | ## Start applications & deploy Smart Contract 93 | 94 | - ### Start ethereum-api 95 | 96 | - Open a new terminal and navigate to `ethereum-springboot-react/ethereum-api` folder 97 | 98 | - Run following command to start application 99 | ``` 100 | ./mvnw clean spring-boot:run 101 | ``` 102 | 103 | - Wait for it to start before continuing 104 | 105 | - ### Deploy Smart Contract 106 | 107 | - In a terminal, make sure you are inside `ethereum-springboot-react` root folder 108 | 109 | - Create the `contract owner` wallet 110 | ``` 111 | CONTRACT_OWNER_WALLET=$(curl -s -X POST "http://localhost:8080/api/wallets/create" \ 112 | -H "Content-Type: application/json" \ 113 | -d "{ \"password\": 123, \"initialBalance\": 10000000000000000000}" | jq '.') 114 | CONTRACT_OWNER_WALLET_FILE=$(echo $CONTRACT_OWNER_WALLET | jq -r '.file') 115 | CONTRACT_OWNER_WALLET_ADDR=$(echo $CONTRACT_OWNER_WALLET | jq -r '.address') 116 | ``` 117 | 118 | - To check `contract owner` wallet 119 | ``` 120 | echo "CONTRACT_OWNER_WALLET=$CONTRACT_OWNER_WALLET" 121 | echo "CONTRACT_OWNER_WALLET_FILE=$CONTRACT_OWNER_WALLET_FILE" 122 | echo "CONTRACT_OWNER_WALLET_ADDR=$CONTRACT_OWNER_WALLET_ADDR" 123 | ``` 124 | 125 | - Deploy `SoccerManager` contract using the `contract owner` wallet 126 | ``` 127 | ETHEREUM_CONTRACT_SOCCERMANAGER_ADDRESS=$(curl -s \ 128 | -X POST "http://localhost:8080/api/contracts/deploy/soccerManager" \ 129 | -H "Content-Type: application/json" \ 130 | -d "{ \"password\": 123, \"file\": \"$CONTRACT_OWNER_WALLET_FILE\", \"gasPrice\": 1, \"gasLimit\": 3000000 }") 131 | ``` 132 | 133 | - To check `SoccerManager` contract address 134 | ``` 135 | echo "ETHEREUM_CONTRACT_SOCCERMANAGER_ADDRESS=$ETHEREUM_CONTRACT_SOCCERMANAGER_ADDRESS" 136 | ``` 137 | 138 | - ### Start player-api 139 | 140 | - In a terminal, make sure you are inside `ethereum-springboot-react/player-api` folder 141 | 142 | - Export to `ETHEREUM_CONTRACT_SOCCERMANAGER_ADDRESS` environment variable the `SoccerManager` contract address obtained at [Deploy Smart Contract](#deploy-smart-contract) step 143 | ``` 144 | export ETHEREUM_CONTRACT_SOCCERMANAGER_ADDRESS=... 145 | ``` 146 | 147 | - Run following command to start application 148 | ``` 149 | ./mvnw clean spring-boot:run 150 | ``` 151 | 152 | ## Application URLs 153 | 154 | | Application | URL | 155 | |----------------|---------------------------------------| 156 | | `ethereum-api` | http://localhost:8080/swagger-ui.html | 157 | | `player-api` | http://localhost:8081/swagger-ui.html | 158 | 159 | ## Test player-api 160 | 161 | - In a new terminal and inside `ethereum-springboot-react` root folder, run the following commands to create `new agent` wallet 162 | ``` 163 | NEW_AGENT_WALLET=$(curl -s -X POST "http://localhost:8080/api/wallets/create" \ 164 | -H "Content-Type: application/json" \ 165 | -d "{ \"password\": 123, \"initialBalance\": 10000000000000000000}" | jq '.') 166 | NEW_AGENT_WALLET_FILE=$(echo $NEW_AGENT_WALLET | jq -r '.file') 167 | NEW_AGENT_WALLET_ADDR=$(echo $NEW_AGENT_WALLET | jq -r '.address') 168 | ``` 169 | 170 | - To check `new agent` wallet 171 | ``` 172 | echo "NEW_AGENT_WALLET = $NEW_AGENT_WALLET" 173 | echo "NEW_AGENT_WALLET_FILE = $NEW_AGENT_WALLET_FILE" 174 | echo "NEW_AGENT_WALLET_ADDR = $NEW_AGENT_WALLET_ADDR" 175 | ``` 176 | 177 | - Get player with id `1` using `new agent` wallet 178 | ``` 179 | curl -s -X POST "http://localhost:8081/api/players/get" \ 180 | -H "Content-Type: application/json" \ 181 | -d "{ \"password\": 123, \"file\": \"$NEW_AGENT_WALLET_FILE\", \"gasPrice\": 1, \"gasLimit\": 3000000, \"playerId\": 1}" | jq '.' 182 | ``` 183 | 184 | - Buy player with id `1` using `new agent` wallet 185 | ``` 186 | curl -s -X POST "http://localhost:8081/api/players/buy" \ 187 | -H "Content-Type: application/json" \ 188 | -d "{ \"password\": 123, \"file\": \"$NEW_AGENT_WALLET_FILE\", \"gasPrice\": 1, \"gasLimit\": 3000000, \"playerId\": 1, \"weiValue\": 1000000000000000000}" | jq '.' 189 | ``` 190 | 191 | - Get the players `new agent` has 192 | ``` 193 | curl -s -X POST "http://localhost:8081/api/agents/players" \ 194 | -H "Content-Type: application/json" \ 195 | -d "{ \"password\": 123, \"file\": \"$NEW_AGENT_WALLET_FILE\", \"gasPrice\": 1, \"gasLimit\": 3000000}" | jq '.' 196 | ``` 197 | 198 | ## Shutdown 199 | 200 | - To stop `ethereum-api` and `player-api`, just go to the terminals where they are running and press `Ctrl+C` 201 | - To stop `ethereum/client-go` docker container, run the following command in a terminal 202 | ``` 203 | docker stop ethereum 204 | ``` 205 | 206 | ## TODO 207 | 208 | - implement `ethereum-ui` and `player-ui` 209 | 210 | ## References 211 | 212 | - https://piotrminkowski.wordpress.com/2018/06/22/introduction-to-blockchain-with-java-using-ethereum-web3j-and-spring-boot/ 213 | - https://piotrminkowski.wordpress.com/2018/07/25/intro-to-blockchain-with-ethereum-web3j-and-spring-boot-smart-contracts/ 214 | -------------------------------------------------------------------------------- /compile-generate-soccermanager.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z "$WEB3J_PATH" ]; then 4 | echo "WARNING: export to WEB3J_PATH environment variable the absolute path to Web3j!" 5 | exit 1 6 | fi 7 | 8 | WEB3J_PATH="${WEB3J_PATH}/bin" 9 | 10 | PROJECT_PATH=$PWD 11 | SOLIDITY_PATH=${PROJECT_PATH}/solidity 12 | ETHEREUM_API_PATH=${PROJECT_PATH}/ethereum-api 13 | PLAYER_API_PATH=${PROJECT_PATH}/player-api 14 | 15 | docker run --rm -v ${SOLIDITY_PATH}:/build ethereum/solc:0.5.17 /build/SoccerManager.sol --bin --abi --optimize --overwrite -o /build 16 | 17 | ${WEB3J_PATH}/web3j solidity generate -a=${SOLIDITY_PATH}/SoccerManager.abi -b=${SOLIDITY_PATH}/SoccerManager.bin -p com.ivanfranchin.ethereumapi.contract -o ${ETHEREUM_API_PATH}/src/main/java 18 | ${WEB3J_PATH}/web3j solidity generate -a=${SOLIDITY_PATH}/SoccerManager.abi -b=${SOLIDITY_PATH}/SoccerManager.bin -p com.ivanfranchin.playerapi.contract -o ${PLAYER_API_PATH}/src/main/java 19 | -------------------------------------------------------------------------------- /documentation/ethereum-api.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivangfr/ethereum-springboot-react/5a88e42e28c5056439672a8493120bfbe967bfb0/documentation/ethereum-api.jpeg -------------------------------------------------------------------------------- /documentation/player-api.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivangfr/ethereum-springboot-react/5a88e42e28c5056439672a8493120bfbe967bfb0/documentation/player-api.jpeg -------------------------------------------------------------------------------- /documentation/project-diagram.excalidraw: -------------------------------------------------------------------------------- 1 | { 2 | "type": "excalidraw", 3 | "version": 2, 4 | "source": "https://excalidraw.com", 5 | "elements": [ 6 | { 7 | "type": "rectangle", 8 | "version": 3196, 9 | "versionNonce": 1006753270, 10 | "isDeleted": false, 11 | "id": "GdyILsCD4rTRfB2LlEajF", 12 | "fillStyle": "hachure", 13 | "strokeWidth": 1, 14 | "strokeStyle": "solid", 15 | "roughness": 1, 16 | "opacity": 100, 17 | "angle": 0, 18 | "x": 401.13548465958706, 19 | "y": -527.3386737757633, 20 | "strokeColor": "#000000", 21 | "backgroundColor": "#ced4da", 22 | "width": 628.8997192382816, 23 | "height": 461.69219970703125, 24 | "seed": 1424004022, 25 | "groupIds": [], 26 | "frameId": null, 27 | "roundness": { 28 | "type": 3 29 | }, 30 | "boundElements": [], 31 | "updated": 1689969674871, 32 | "link": null, 33 | "locked": false 34 | }, 35 | { 36 | "type": "text", 37 | "version": 517, 38 | "versionNonce": 1608061468, 39 | "isDeleted": false, 40 | "id": "ogirD_sYiZPw-C6dGjCiH", 41 | "fillStyle": "cross-hatch", 42 | "strokeWidth": 2, 43 | "strokeStyle": "solid", 44 | "roughness": 0, 45 | "opacity": 100, 46 | "angle": 0, 47 | "x": 417.6398986881785, 48 | "y": -504.4703076365563, 49 | "strokeColor": "#000000", 50 | "backgroundColor": "#82c91e", 51 | "width": 246.2318878173828, 52 | "height": 33.6, 53 | "seed": 1836146562, 54 | "groupIds": [], 55 | "frameId": null, 56 | "roundness": null, 57 | "boundElements": [], 58 | "updated": 1677767849924, 59 | "link": null, 60 | "locked": false, 61 | "fontSize": 28, 62 | "fontFamily": 1, 63 | "text": "Ethereum Network", 64 | "textAlign": "left", 65 | "verticalAlign": "top", 66 | "containerId": null, 67 | "originalText": "Ethereum Network", 68 | "lineHeight": 1.2, 69 | "baseline": 24 70 | }, 71 | { 72 | "type": "rectangle", 73 | "version": 811, 74 | "versionNonce": 757584694, 75 | "isDeleted": false, 76 | "id": "iasX7BrL9p63UfTm1fstZ", 77 | "fillStyle": "cross-hatch", 78 | "strokeWidth": 1, 79 | "strokeStyle": "solid", 80 | "roughness": 1, 81 | "opacity": 100, 82 | "angle": 0, 83 | "x": 749.3163055485301, 84 | "y": -413.2027478221032, 85 | "strokeColor": "#000000", 86 | "backgroundColor": "#fa5252", 87 | "width": 211, 88 | "height": 107, 89 | "seed": 770326634, 90 | "groupIds": [], 91 | "frameId": null, 92 | "roundness": { 93 | "type": 3 94 | }, 95 | "boundElements": [ 96 | { 97 | "type": "text", 98 | "id": "fzCXN2ThumUG80tK0jZRe" 99 | }, 100 | { 101 | "id": "kdSxQkudl_4cgqcPQ6xcs", 102 | "type": "arrow" 103 | } 104 | ], 105 | "updated": 1689969639895, 106 | "link": null, 107 | "locked": false 108 | }, 109 | { 110 | "type": "text", 111 | "version": 690, 112 | "versionNonce": 396914332, 113 | "isDeleted": false, 114 | "id": "fzCXN2ThumUG80tK0jZRe", 115 | "fillStyle": "cross-hatch", 116 | "strokeWidth": 2, 117 | "strokeStyle": "solid", 118 | "roughness": 0, 119 | "opacity": 100, 120 | "angle": 0, 121 | "x": 756.4383529728465, 122 | "y": -376.50274782210323, 123 | "strokeColor": "#000000", 124 | "backgroundColor": "#82c91e", 125 | "width": 196.7559051513672, 126 | "height": 33.6, 127 | "seed": 1953780610, 128 | "groupIds": [], 129 | "frameId": null, 130 | "roundness": null, 131 | "boundElements": [], 132 | "updated": 1677767849924, 133 | "link": null, 134 | "locked": false, 135 | "fontSize": 28, 136 | "fontFamily": 1, 137 | "text": "Etherum-Client", 138 | "textAlign": "center", 139 | "verticalAlign": "middle", 140 | "containerId": "iasX7BrL9p63UfTm1fstZ", 141 | "originalText": "Etherum-Client", 142 | "lineHeight": 1.2, 143 | "baseline": 24 144 | }, 145 | { 146 | "type": "arrow", 147 | "version": 2376, 148 | "versionNonce": 1877776042, 149 | "isDeleted": false, 150 | "id": "jzrlCHb-PXCgBo4JxFQic", 151 | "fillStyle": "cross-hatch", 152 | "strokeWidth": 1, 153 | "strokeStyle": "solid", 154 | "roughness": 1, 155 | "opacity": 100, 156 | "angle": 0, 157 | "x": 673.2194578462057, 158 | "y": -305.6605695862503, 159 | "strokeColor": "#000000", 160 | "backgroundColor": "#868e96", 161 | "width": 78.52182517317112, 162 | "height": 46.326131747185286, 163 | "seed": 2019205930, 164 | "groupIds": [], 165 | "frameId": null, 166 | "roundness": { 167 | "type": 2 168 | }, 169 | "boundElements": [], 170 | "updated": 1689969647448, 171 | "link": null, 172 | "locked": false, 173 | "startBinding": { 174 | "elementId": "OgsaF-unh8ZXBU7Zv5han", 175 | "focus": 0.3489495295317687, 176 | "gap": 11.738281081855348 177 | }, 178 | "endBinding": null, 179 | "lastCommittedPoint": null, 180 | "startArrowhead": null, 181 | "endArrowhead": null, 182 | "points": [ 183 | [ 184 | 0, 185 | 0 186 | ], 187 | [ 188 | 78.52182517317112, 189 | -46.326131747185286 190 | ] 191 | ] 192 | }, 193 | { 194 | "type": "arrow", 195 | "version": 1945, 196 | "versionNonce": 1137508598, 197 | "isDeleted": false, 198 | "id": "kdSxQkudl_4cgqcPQ6xcs", 199 | "fillStyle": "cross-hatch", 200 | "strokeWidth": 1, 201 | "strokeStyle": "solid", 202 | "roughness": 1, 203 | "opacity": 100, 204 | "angle": 0, 205 | "x": 850.5591230747885, 206 | "y": -299.33028001474577, 207 | "strokeColor": "#000000", 208 | "backgroundColor": "#868e96", 209 | "width": 1.6595333076771794, 210 | "height": 46.7342766619812, 211 | "seed": 1144040938, 212 | "groupIds": [], 213 | "frameId": null, 214 | "roundness": { 215 | "type": 2 216 | }, 217 | "boundElements": [], 218 | "updated": 1689969647448, 219 | "link": null, 220 | "locked": false, 221 | "startBinding": { 222 | "elementId": "iasX7BrL9p63UfTm1fstZ", 223 | "focus": 0.059599819100763576, 224 | "gap": 6.872467807357452 225 | }, 226 | "endBinding": { 227 | "elementId": "SJKe9uJGEPDKDkcF9xSLN", 228 | "focus": -0.003821687237777013, 229 | "gap": 5.978704749411349 230 | }, 231 | "lastCommittedPoint": null, 232 | "startArrowhead": null, 233 | "endArrowhead": null, 234 | "points": [ 235 | [ 236 | 0, 237 | 0 238 | ], 239 | [ 240 | 1.6595333076771794, 241 | 46.7342766619812 242 | ] 243 | ] 244 | }, 245 | { 246 | "type": "arrow", 247 | "version": 2553, 248 | "versionNonce": 1815621994, 249 | "isDeleted": false, 250 | "id": "Vpv8KLhArC7oORbHy5mNF", 251 | "fillStyle": "cross-hatch", 252 | "strokeWidth": 1, 253 | "strokeStyle": "solid", 254 | "roughness": 1, 255 | "opacity": 100, 256 | "angle": 0, 257 | "x": 671.10558605736, 258 | "y": -251.64798222337197, 259 | "strokeColor": "#000000", 260 | "backgroundColor": "#868e96", 261 | "width": 57.1646969665145, 262 | "height": 42.50043084579261, 263 | "seed": 645357738, 264 | "groupIds": [], 265 | "frameId": null, 266 | "roundness": { 267 | "type": 2 268 | }, 269 | "boundElements": [], 270 | "updated": 1689969647448, 271 | "link": null, 272 | "locked": false, 273 | "startBinding": { 274 | "elementId": "OgsaF-unh8ZXBU7Zv5han", 275 | "focus": -0.45449265922694226, 276 | "gap": 9.624409293009649 277 | }, 278 | "endBinding": { 279 | "elementId": "SJKe9uJGEPDKDkcF9xSLN", 280 | "focus": -0.564697020864534, 281 | "gap": 20.04602252465554 282 | }, 283 | "lastCommittedPoint": null, 284 | "startArrowhead": null, 285 | "endArrowhead": null, 286 | "points": [ 287 | [ 288 | 0, 289 | 0 290 | ], 291 | [ 292 | 57.1646969665145, 293 | 42.50043084579261 294 | ] 295 | ] 296 | }, 297 | { 298 | "type": "text", 299 | "version": 543, 300 | "versionNonce": 333521948, 301 | "isDeleted": false, 302 | "id": "cl55RC4nNT0V_Eksk0VGJ", 303 | "fillStyle": "cross-hatch", 304 | "strokeWidth": 2, 305 | "strokeStyle": "solid", 306 | "roughness": 0, 307 | "opacity": 100, 308 | "angle": 0, 309 | "x": 980.2114013737254, 310 | "y": -378.0960278514001, 311 | "strokeColor": "#000000", 312 | "backgroundColor": "#868e96", 313 | "width": 16.439987182617188, 314 | "height": 24, 315 | "seed": 1934681922, 316 | "groupIds": [], 317 | "frameId": null, 318 | "roundness": null, 319 | "boundElements": [], 320 | "updated": 1677767849924, 321 | "link": null, 322 | "locked": false, 323 | "fontSize": 20, 324 | "fontFamily": 1, 325 | "text": "...", 326 | "textAlign": "left", 327 | "verticalAlign": "top", 328 | "containerId": null, 329 | "originalText": "...", 330 | "lineHeight": 1.2, 331 | "baseline": 17 332 | }, 333 | { 334 | "type": "text", 335 | "version": 530, 336 | "versionNonce": 1900526500, 337 | "isDeleted": false, 338 | "id": "HRHjGW2swNG2GPISwD5WQ", 339 | "fillStyle": "cross-hatch", 340 | "strokeWidth": 2, 341 | "strokeStyle": "solid", 342 | "roughness": 0, 343 | "opacity": 100, 344 | "angle": 0, 345 | "x": 975.1447280949168, 346 | "y": -209.91359376936884, 347 | "strokeColor": "#000000", 348 | "backgroundColor": "#868e96", 349 | "width": 16.439987182617188, 350 | "height": 24, 351 | "seed": 972628802, 352 | "groupIds": [], 353 | "frameId": null, 354 | "roundness": null, 355 | "boundElements": [], 356 | "updated": 1677767849924, 357 | "link": null, 358 | "locked": false, 359 | "fontSize": 20, 360 | "fontFamily": 1, 361 | "text": "...", 362 | "textAlign": "left", 363 | "verticalAlign": "top", 364 | "containerId": null, 365 | "originalText": "...", 366 | "lineHeight": 1.2, 367 | "baseline": 17 368 | }, 369 | { 370 | "type": "rectangle", 371 | "version": 2182, 372 | "versionNonce": 1375742774, 373 | "isDeleted": false, 374 | "id": "NKmNZxYxWMCKh3prRiPwX", 375 | "fillStyle": "hachure", 376 | "strokeWidth": 1, 377 | "strokeStyle": "solid", 378 | "roughness": 1, 379 | "opacity": 100, 380 | "angle": 0, 381 | "x": -18.14538624571145, 382 | "y": -377.18772502690626, 383 | "strokeColor": "#000000", 384 | "backgroundColor": "#82c91e", 385 | "width": 225, 386 | "height": 100, 387 | "seed": 1701341290, 388 | "groupIds": [ 389 | "PzE-h1T952fBK12VK9GfH" 390 | ], 391 | "frameId": null, 392 | "roundness": { 393 | "type": 3 394 | }, 395 | "boundElements": [ 396 | { 397 | "type": "text", 398 | "id": "GrVT2PZuYu1cRv3sXwFoI" 399 | }, 400 | { 401 | "id": "5S3C3DA_r92gW3HE1y0L9", 402 | "type": "arrow" 403 | } 404 | ], 405 | "updated": 1689969573374, 406 | "link": null, 407 | "locked": false 408 | }, 409 | { 410 | "type": "text", 411 | "version": 1140, 412 | "versionNonce": 1841174302, 413 | "isDeleted": false, 414 | "id": "GrVT2PZuYu1cRv3sXwFoI", 415 | "fillStyle": "hachure", 416 | "strokeWidth": 1, 417 | "strokeStyle": "solid", 418 | "roughness": 0, 419 | "opacity": 100, 420 | "angle": 0, 421 | "x": 27.966643783585425, 422 | "y": -360.7877250269063, 423 | "strokeColor": "#000000", 424 | "backgroundColor": "transparent", 425 | "width": 132.77593994140625, 426 | "height": 67.2, 427 | "seed": 294979727, 428 | "groupIds": [ 429 | "PzE-h1T952fBK12VK9GfH" 430 | ], 431 | "frameId": null, 432 | "roundness": null, 433 | "boundElements": [], 434 | "updated": 1677767367188, 435 | "link": null, 436 | "locked": false, 437 | "fontSize": 28, 438 | "fontFamily": 1, 439 | "text": "ethereum-\napi", 440 | "textAlign": "center", 441 | "verticalAlign": "middle", 442 | "containerId": "NKmNZxYxWMCKh3prRiPwX", 443 | "originalText": "ethereum-\napi", 444 | "lineHeight": 1.2, 445 | "baseline": 58 446 | }, 447 | { 448 | "type": "rectangle", 449 | "version": 336, 450 | "versionNonce": 709604406, 451 | "isDeleted": false, 452 | "id": "gwCJOyYDfvbVGHHFJln2R", 453 | "fillStyle": "cross-hatch", 454 | "strokeWidth": 1, 455 | "strokeStyle": "solid", 456 | "roughness": 1, 457 | "opacity": 100, 458 | "angle": 0, 459 | "x": 167.85870972333475, 460 | "y": -343.4130831173001, 461 | "strokeColor": "#000000", 462 | "backgroundColor": "#9775fa", 463 | "width": 77.860595703125, 464 | "height": 34.714111328125, 465 | "seed": 2030095338, 466 | "groupIds": [ 467 | "PzE-h1T952fBK12VK9GfH" 468 | ], 469 | "frameId": null, 470 | "roundness": { 471 | "type": 3 472 | }, 473 | "boundElements": [ 474 | { 475 | "type": "text", 476 | "id": "vfzA6m6N1hhWC2J8LGEi4" 477 | }, 478 | { 479 | "id": "18PIhYY3lq15JjREJNaJA", 480 | "type": "arrow" 481 | } 482 | ], 483 | "updated": 1689969604293, 484 | "link": null, 485 | "locked": false 486 | }, 487 | { 488 | "type": "text", 489 | "version": 191, 490 | "versionNonce": 577340254, 491 | "isDeleted": false, 492 | "id": "vfzA6m6N1hhWC2J8LGEi4", 493 | "fillStyle": "cross-hatch", 494 | "strokeWidth": 2, 495 | "strokeStyle": "solid", 496 | "roughness": 0, 497 | "opacity": 100, 498 | "angle": 0, 499 | "x": 178.68902435956522, 500 | "y": -338.0560274532376, 501 | "strokeColor": "#000000", 502 | "backgroundColor": "#868e96", 503 | "width": 56.19996643066406, 504 | "height": 24, 505 | "seed": 51029406, 506 | "groupIds": [ 507 | "PzE-h1T952fBK12VK9GfH" 508 | ], 509 | "frameId": null, 510 | "roundness": null, 511 | "boundElements": [], 512 | "updated": 1677767367188, 513 | "link": null, 514 | "locked": false, 515 | "fontSize": 20, 516 | "fontFamily": 1, 517 | "text": "Web3j", 518 | "textAlign": "center", 519 | "verticalAlign": "middle", 520 | "containerId": "gwCJOyYDfvbVGHHFJln2R", 521 | "originalText": "Web3j", 522 | "lineHeight": 1.2, 523 | "baseline": 17 524 | }, 525 | { 526 | "type": "rectangle", 527 | "version": 2256, 528 | "versionNonce": 1420899510, 529 | "isDeleted": false, 530 | "id": "pEzzV---Z5MvPOaMkQSr_", 531 | "fillStyle": "hachure", 532 | "strokeWidth": 1, 533 | "strokeStyle": "solid", 534 | "roughness": 1, 535 | "opacity": 100, 536 | "angle": 0, 537 | "x": -16.9089970855552, 538 | "y": -220.09435310579084, 539 | "strokeColor": "#000000", 540 | "backgroundColor": "#fab005", 541 | "width": 224, 542 | "height": 100, 543 | "seed": 1842596650, 544 | "groupIds": [ 545 | "pcgLIzBUcrRkON39wO7I6" 546 | ], 547 | "frameId": null, 548 | "roundness": { 549 | "type": 3 550 | }, 551 | "boundElements": [ 552 | { 553 | "type": "text", 554 | "id": "Yb8w3i19Rgs4PslNl0Txc" 555 | }, 556 | { 557 | "id": "LdifYcmvoISr-O_tBwMDQ", 558 | "type": "arrow" 559 | } 560 | ], 561 | "updated": 1689969595295, 562 | "link": null, 563 | "locked": false 564 | }, 565 | { 566 | "type": "text", 567 | "version": 1224, 568 | "versionNonce": 1079333994, 569 | "isDeleted": false, 570 | "id": "Yb8w3i19Rgs4PslNl0Txc", 571 | "fillStyle": "hachure", 572 | "strokeWidth": 1, 573 | "strokeStyle": "solid", 574 | "roughness": 0, 575 | "opacity": 100, 576 | "angle": 0, 577 | "x": 29.86504313661277, 578 | "y": -186.89435310579086, 579 | "strokeColor": "#000000", 580 | "backgroundColor": "transparent", 581 | "width": 130.45191955566406, 582 | "height": 33.6, 583 | "seed": 442613663, 584 | "groupIds": [ 585 | "pcgLIzBUcrRkON39wO7I6" 586 | ], 587 | "frameId": null, 588 | "roundness": null, 589 | "boundElements": [], 590 | "updated": 1689969595297, 591 | "link": null, 592 | "locked": false, 593 | "fontSize": 28, 594 | "fontFamily": 1, 595 | "text": "player-api", 596 | "textAlign": "center", 597 | "verticalAlign": "middle", 598 | "containerId": "pEzzV---Z5MvPOaMkQSr_", 599 | "originalText": "player-api", 600 | "lineHeight": 1.2, 601 | "baseline": 24 602 | }, 603 | { 604 | "type": "rectangle", 605 | "version": 454, 606 | "versionNonce": 139130026, 607 | "isDeleted": false, 608 | "id": "eWgZGAu3WZLvr1zYuyOU_", 609 | "fillStyle": "cross-hatch", 610 | "strokeWidth": 1, 611 | "strokeStyle": "solid", 612 | "roughness": 1, 613 | "opacity": 100, 614 | "angle": 0, 615 | "x": 167.9824279850535, 616 | "y": -188.04875240169685, 617 | "strokeColor": "#000000", 618 | "backgroundColor": "#9775fa", 619 | "width": 77.860595703125, 620 | "height": 34.714111328125, 621 | "seed": 347756906, 622 | "groupIds": [ 623 | "pcgLIzBUcrRkON39wO7I6" 624 | ], 625 | "frameId": null, 626 | "roundness": { 627 | "type": 3 628 | }, 629 | "boundElements": [ 630 | { 631 | "type": "text", 632 | "id": "KA5q5zsWQUuK_zUZfrs3q" 633 | }, 634 | { 635 | "id": "AsR2kjKvBls7VJ6J8NsO5", 636 | "type": "arrow" 637 | } 638 | ], 639 | "updated": 1689969599267, 640 | "link": null, 641 | "locked": false 642 | }, 643 | { 644 | "type": "text", 645 | "version": 307, 646 | "versionNonce": 501662186, 647 | "isDeleted": false, 648 | "id": "KA5q5zsWQUuK_zUZfrs3q", 649 | "fillStyle": "cross-hatch", 650 | "strokeWidth": 2, 651 | "strokeStyle": "solid", 652 | "roughness": 0, 653 | "opacity": 100, 654 | "angle": 0, 655 | "x": 178.81274262128397, 656 | "y": -182.69169673763435, 657 | "strokeColor": "#000000", 658 | "backgroundColor": "#868e96", 659 | "width": 56.19996643066406, 660 | "height": 24, 661 | "seed": 1978967966, 662 | "groupIds": [ 663 | "pcgLIzBUcrRkON39wO7I6" 664 | ], 665 | "frameId": null, 666 | "roundness": null, 667 | "boundElements": [], 668 | "updated": 1689969595298, 669 | "link": null, 670 | "locked": false, 671 | "fontSize": 20, 672 | "fontFamily": 1, 673 | "text": "Web3j", 674 | "textAlign": "center", 675 | "verticalAlign": "middle", 676 | "containerId": "eWgZGAu3WZLvr1zYuyOU_", 677 | "originalText": "Web3j", 678 | "lineHeight": 1.2, 679 | "baseline": 17 680 | }, 681 | { 682 | "type": "arrow", 683 | "version": 2214, 684 | "versionNonce": 351070518, 685 | "isDeleted": false, 686 | "id": "18PIhYY3lq15JjREJNaJA", 687 | "fillStyle": "cross-hatch", 688 | "strokeWidth": 1, 689 | "strokeStyle": "solid", 690 | "roughness": 1, 691 | "opacity": 100, 692 | "angle": 0, 693 | "x": 253.84625855145975, 694 | "y": -329.0114999453972, 695 | "strokeColor": "#000000", 696 | "backgroundColor": "#fd7e14", 697 | "width": 186.73202514648438, 698 | "height": 38.364491964963065, 699 | "seed": 1934139062, 700 | "groupIds": [], 701 | "frameId": null, 702 | "roundness": { 703 | "type": 2 704 | }, 705 | "boundElements": [ 706 | { 707 | "type": "text", 708 | "id": "2YHfpUE1TRrUJEl9TrHPf" 709 | } 710 | ], 711 | "updated": 1689969619275, 712 | "link": null, 713 | "locked": false, 714 | "startBinding": { 715 | "elementId": "gwCJOyYDfvbVGHHFJln2R", 716 | "gap": 8.126953125, 717 | "focus": -0.4969036240346405 718 | }, 719 | "endBinding": { 720 | "elementId": "OgsaF-unh8ZXBU7Zv5han", 721 | "gap": 10.90289306640625, 722 | "focus": -0.1363812149788098 723 | }, 724 | "lastCommittedPoint": null, 725 | "startArrowhead": "arrow", 726 | "endArrowhead": "arrow", 727 | "points": [ 728 | [ 729 | 0, 730 | 0 731 | ], 732 | [ 733 | 186.73202514648438, 734 | 38.364491964963065 735 | ] 736 | ] 737 | }, 738 | { 739 | "type": "text", 740 | "version": 42, 741 | "versionNonce": 241796772, 742 | "isDeleted": false, 743 | "id": "2YHfpUE1TRrUJEl9TrHPf", 744 | "fillStyle": "cross-hatch", 745 | "strokeWidth": 2, 746 | "strokeStyle": "solid", 747 | "roughness": 0, 748 | "opacity": 100, 749 | "angle": 0, 750 | "x": 291.2402938908152, 751 | "y": -313.5346806381698, 752 | "strokeColor": "#000000", 753 | "backgroundColor": "#fd7e14", 754 | "width": 100.11991882324219, 755 | "height": 24, 756 | "seed": 1467204802, 757 | "groupIds": [], 758 | "frameId": null, 759 | "roundness": null, 760 | "boundElements": [], 761 | "updated": 1677767616923, 762 | "link": null, 763 | "locked": false, 764 | "fontSize": 20, 765 | "fontFamily": 1, 766 | "text": "JSON-RPC", 767 | "textAlign": "center", 768 | "verticalAlign": "middle", 769 | "containerId": "18PIhYY3lq15JjREJNaJA", 770 | "originalText": "JSON-RPC", 771 | "lineHeight": 1.2, 772 | "baseline": 17 773 | }, 774 | { 775 | "type": "arrow", 776 | "version": 2524, 777 | "versionNonce": 731508010, 778 | "isDeleted": false, 779 | "id": "AsR2kjKvBls7VJ6J8NsO5", 780 | "fillStyle": "cross-hatch", 781 | "strokeWidth": 1, 782 | "strokeStyle": "solid", 783 | "roughness": 1, 784 | "opacity": 100, 785 | "angle": 0, 786 | "x": 250.30280152020975, 787 | "y": -169.75704968009632, 788 | "strokeColor": "#000000", 789 | "backgroundColor": "#fd7e14", 790 | "width": 190.79116821289062, 791 | "height": 103.00856580436584, 792 | "seed": 963598326, 793 | "groupIds": [], 794 | "frameId": null, 795 | "roundness": { 796 | "type": 2 797 | }, 798 | "boundElements": [ 799 | { 800 | "type": "text", 801 | "id": "DsHf--gGC3vFaGsWxsthP" 802 | } 803 | ], 804 | "updated": 1689969619275, 805 | "link": null, 806 | "locked": false, 807 | "startBinding": { 808 | "elementId": "eWgZGAu3WZLvr1zYuyOU_", 809 | "gap": 4.45977783203125, 810 | "focus": 0.6348476048216786 811 | }, 812 | "endBinding": { 813 | "elementId": "OgsaF-unh8ZXBU7Zv5han", 814 | "gap": 10.38720703125, 815 | "focus": 0.5262780208674982 816 | }, 817 | "lastCommittedPoint": null, 818 | "startArrowhead": "arrow", 819 | "endArrowhead": "arrow", 820 | "points": [ 821 | [ 822 | 0, 823 | 0 824 | ], 825 | [ 826 | 190.79116821289062, 827 | -103.00856580436584 828 | ] 829 | ] 830 | }, 831 | { 832 | "type": "text", 833 | "version": 40, 834 | "versionNonce": 198354332, 835 | "isDeleted": false, 836 | "id": "DsHf--gGC3vFaGsWxsthP", 837 | "fillStyle": "cross-hatch", 838 | "strokeWidth": 2, 839 | "strokeStyle": "solid", 840 | "roughness": 0, 841 | "opacity": 100, 842 | "angle": 0, 843 | "x": 289.664549261909, 844 | "y": -228.77719747062227, 845 | "strokeColor": "#000000", 846 | "backgroundColor": "#fd7e14", 847 | "width": 100.11991882324219, 848 | "height": 24, 849 | "seed": 1773232834, 850 | "groupIds": [], 851 | "frameId": null, 852 | "roundness": null, 853 | "boundElements": [], 854 | "updated": 1677767616924, 855 | "link": null, 856 | "locked": false, 857 | "fontSize": 20, 858 | "fontFamily": 1, 859 | "text": "JSON-RPC", 860 | "textAlign": "center", 861 | "verticalAlign": "middle", 862 | "containerId": "AsR2kjKvBls7VJ6J8NsO5", 863 | "originalText": "JSON-RPC", 864 | "lineHeight": 1.2, 865 | "baseline": 17 866 | }, 867 | { 868 | "type": "rectangle", 869 | "version": 2423, 870 | "versionNonce": 864769462, 871 | "isDeleted": false, 872 | "id": "20OwUs1115u8W4enUZKQ-", 873 | "fillStyle": "hachure", 874 | "strokeWidth": 1, 875 | "strokeStyle": "solid", 876 | "roughness": 1, 877 | "opacity": 40, 878 | "angle": 0, 879 | "x": -380.85103326851254, 880 | "y": -377.1804313257344, 881 | "strokeColor": "#000000", 882 | "backgroundColor": "#12b886", 883 | "width": 225, 884 | "height": 100, 885 | "seed": 4511210, 886 | "groupIds": [ 887 | "nYUHtBz-33YpbMnNBFxps" 888 | ], 889 | "frameId": null, 890 | "roundness": { 891 | "type": 3 892 | }, 893 | "boundElements": [ 894 | { 895 | "type": "text", 896 | "id": "pHQ63mP916u87nxqdFxG6" 897 | }, 898 | { 899 | "id": "5S3C3DA_r92gW3HE1y0L9", 900 | "type": "arrow" 901 | } 902 | ], 903 | "updated": 1689969573374, 904 | "link": null, 905 | "locked": false 906 | }, 907 | { 908 | "type": "text", 909 | "version": 1390, 910 | "versionNonce": 1849896694, 911 | "isDeleted": false, 912 | "id": "pHQ63mP916u87nxqdFxG6", 913 | "fillStyle": "hachure", 914 | "strokeWidth": 1, 915 | "strokeStyle": "solid", 916 | "roughness": 1, 917 | "opacity": 40, 918 | "angle": 0, 919 | "x": -334.73900323921566, 920 | "y": -360.7804313257344, 921 | "strokeColor": "#000000", 922 | "backgroundColor": "#12b886", 923 | "width": 132.77593994140625, 924 | "height": 67.2, 925 | "seed": 499550378, 926 | "groupIds": [ 927 | "nYUHtBz-33YpbMnNBFxps" 928 | ], 929 | "frameId": null, 930 | "roundness": { 931 | "type": 2 932 | }, 933 | "boundElements": [], 934 | "updated": 1689969573374, 935 | "link": null, 936 | "locked": false, 937 | "fontSize": 28, 938 | "fontFamily": 1, 939 | "text": "ethereum-\nui", 940 | "textAlign": "center", 941 | "verticalAlign": "middle", 942 | "containerId": "20OwUs1115u8W4enUZKQ-", 943 | "originalText": "ethereum-\nui", 944 | "lineHeight": 1.2, 945 | "baseline": 58 946 | }, 947 | { 948 | "type": "rectangle", 949 | "version": 2519, 950 | "versionNonce": 1328406582, 951 | "isDeleted": false, 952 | "id": "wMt-59su8pINqTWOXkE-3", 953 | "fillStyle": "hachure", 954 | "strokeWidth": 1, 955 | "strokeStyle": "solid", 956 | "roughness": 1, 957 | "opacity": 40, 958 | "angle": 0, 959 | "x": -380.4659929853094, 960 | "y": -219.96923103547834, 961 | "strokeColor": "#000000", 962 | "backgroundColor": "#fd7e14", 963 | "width": 224, 964 | "height": 100, 965 | "seed": 1845748586, 966 | "groupIds": [ 967 | "vGKqdB_rmCV1SR0cBybWA" 968 | ], 969 | "frameId": null, 970 | "roundness": { 971 | "type": 3 972 | }, 973 | "boundElements": [ 974 | { 975 | "type": "text", 976 | "id": "worTA7jd_DNvi8mtjST2u" 977 | }, 978 | { 979 | "id": "LdifYcmvoISr-O_tBwMDQ", 980 | "type": "arrow" 981 | } 982 | ], 983 | "updated": 1689969573374, 984 | "link": null, 985 | "locked": false 986 | }, 987 | { 988 | "type": "text", 989 | "version": 1494, 990 | "versionNonce": 976251254, 991 | "isDeleted": false, 992 | "id": "worTA7jd_DNvi8mtjST2u", 993 | "fillStyle": "hachure", 994 | "strokeWidth": 1, 995 | "strokeStyle": "solid", 996 | "roughness": 1, 997 | "opacity": 40, 998 | "angle": 0, 999 | "x": -325.4039583783758, 1000 | "y": -186.76923103547836, 1001 | "strokeColor": "#000000", 1002 | "backgroundColor": "#fd7e14", 1003 | "width": 113.87593078613281, 1004 | "height": 33.6, 1005 | "seed": 1799874090, 1006 | "groupIds": [ 1007 | "vGKqdB_rmCV1SR0cBybWA" 1008 | ], 1009 | "frameId": null, 1010 | "roundness": null, 1011 | "boundElements": [], 1012 | "updated": 1689969573374, 1013 | "link": null, 1014 | "locked": false, 1015 | "fontSize": 28, 1016 | "fontFamily": 1, 1017 | "text": "player-ui", 1018 | "textAlign": "center", 1019 | "verticalAlign": "middle", 1020 | "containerId": "wMt-59su8pINqTWOXkE-3", 1021 | "originalText": "player-ui", 1022 | "lineHeight": 1.2, 1023 | "baseline": 24 1024 | }, 1025 | { 1026 | "type": "arrow", 1027 | "version": 440, 1028 | "versionNonce": 1318046390, 1029 | "isDeleted": false, 1030 | "id": "5S3C3DA_r92gW3HE1y0L9", 1031 | "fillStyle": "cross-hatch", 1032 | "strokeWidth": 1, 1033 | "strokeStyle": "solid", 1034 | "roughness": 1, 1035 | "opacity": 40, 1036 | "angle": 0, 1037 | "x": -147.93036040605978, 1038 | "y": -323.841258036175, 1039 | "strokeColor": "#000000", 1040 | "backgroundColor": "#fd7e14", 1041 | "width": 126.33939361572266, 1042 | "height": 2.263068198671931, 1043 | "seed": 243110122, 1044 | "groupIds": [], 1045 | "frameId": null, 1046 | "roundness": { 1047 | "type": 2 1048 | }, 1049 | "boundElements": [], 1050 | "updated": 1689969573374, 1051 | "link": null, 1052 | "locked": false, 1053 | "startBinding": { 1054 | "elementId": "20OwUs1115u8W4enUZKQ-", 1055 | "focus": 0.10602050452578751, 1056 | "gap": 7.920672862452761 1057 | }, 1058 | "endBinding": { 1059 | "elementId": "NKmNZxYxWMCKh3prRiPwX", 1060 | "focus": 0.019099987975376074, 1061 | "gap": 3.4455805446256704 1062 | }, 1063 | "lastCommittedPoint": null, 1064 | "startArrowhead": "arrow", 1065 | "endArrowhead": "arrow", 1066 | "points": [ 1067 | [ 1068 | 0, 1069 | 0 1070 | ], 1071 | [ 1072 | 126.33939361572266, 1073 | -2.263068198671931 1074 | ] 1075 | ] 1076 | }, 1077 | { 1078 | "type": "arrow", 1079 | "version": 543, 1080 | "versionNonce": 1297427958, 1081 | "isDeleted": false, 1082 | "id": "LdifYcmvoISr-O_tBwMDQ", 1083 | "fillStyle": "cross-hatch", 1084 | "strokeWidth": 1, 1085 | "strokeStyle": "solid", 1086 | "roughness": 1, 1087 | "opacity": 40, 1088 | "angle": 0, 1089 | "x": -146.64302977861837, 1090 | "y": -165.0107417494333, 1091 | "strokeColor": "#000000", 1092 | "backgroundColor": "#fd7e14", 1093 | "width": 120.263671875, 1094 | "height": 0.2641218590468668, 1095 | "seed": 2066736042, 1096 | "groupIds": [], 1097 | "frameId": null, 1098 | "roundness": { 1099 | "type": 2 1100 | }, 1101 | "boundElements": [], 1102 | "updated": 1689969595297, 1103 | "link": null, 1104 | "locked": false, 1105 | "startBinding": { 1106 | "elementId": "wMt-59su8pINqTWOXkE-3", 1107 | "focus": 0.10401141045648506, 1108 | "gap": 9.822963206691043 1109 | }, 1110 | "endBinding": { 1111 | "elementId": "pEzzV---Z5MvPOaMkQSr_", 1112 | "focus": -0.09060860451585798, 1113 | "gap": 9.470360818063185 1114 | }, 1115 | "lastCommittedPoint": null, 1116 | "startArrowhead": "arrow", 1117 | "endArrowhead": "arrow", 1118 | "points": [ 1119 | [ 1120 | 0, 1121 | 0 1122 | ], 1123 | [ 1124 | 120.263671875, 1125 | -0.2641218590468668 1126 | ] 1127 | ] 1128 | }, 1129 | { 1130 | "type": "rectangle", 1131 | "version": 1003, 1132 | "versionNonce": 589470186, 1133 | "isDeleted": false, 1134 | "id": "mlJY8OLBwnWi1yfUQ1iN_", 1135 | "fillStyle": "hachure", 1136 | "strokeWidth": 1, 1137 | "strokeStyle": "solid", 1138 | "roughness": 1, 1139 | "opacity": 100, 1140 | "angle": 0, 1141 | "x": 469.4811767643504, 1142 | "y": -224.57639894515012, 1143 | "strokeColor": "#000000", 1144 | "backgroundColor": "#3bc9db", 1145 | "width": 174, 1146 | "height": 48.4, 1147 | "seed": 1524218730, 1148 | "groupIds": [], 1149 | "frameId": null, 1150 | "roundness": { 1151 | "type": 3 1152 | }, 1153 | "boundElements": [ 1154 | { 1155 | "type": "text", 1156 | "id": "vVOeoRte1RvsyVURJUeds" 1157 | }, 1158 | { 1159 | "id": "jzrlCHb-PXCgBo4JxFQic", 1160 | "type": "arrow" 1161 | }, 1162 | { 1163 | "id": "Vpv8KLhArC7oORbHy5mNF", 1164 | "type": "arrow" 1165 | } 1166 | ], 1167 | "updated": 1689969680724, 1168 | "link": null, 1169 | "locked": false 1170 | }, 1171 | { 1172 | "type": "text", 1173 | "version": 795, 1174 | "versionNonce": 2140710692, 1175 | "isDeleted": false, 1176 | "id": "vVOeoRte1RvsyVURJUeds", 1177 | "fillStyle": "hachure", 1178 | "strokeWidth": 1, 1179 | "strokeStyle": "solid", 1180 | "roughness": 0, 1181 | "opacity": 100, 1182 | "angle": 0, 1183 | "x": 485.38522186933085, 1184 | "y": -219.57639894515012, 1185 | "strokeColor": "#000000", 1186 | "backgroundColor": "#fa5252", 1187 | "width": 142.19190979003906, 1188 | "height": 38.4, 1189 | "seed": 1487955358, 1190 | "groupIds": [], 1191 | "frameId": null, 1192 | "roundness": null, 1193 | "boundElements": [], 1194 | "updated": 1677767849924, 1195 | "link": null, 1196 | "locked": false, 1197 | "fontSize": 16, 1198 | "fontFamily": 1, 1199 | "text": "SoccerManager\n", 1200 | "textAlign": "center", 1201 | "verticalAlign": "middle", 1202 | "containerId": "mlJY8OLBwnWi1yfUQ1iN_", 1203 | "originalText": "SoccerManager\n", 1204 | "lineHeight": 1.2, 1205 | "baseline": 33 1206 | }, 1207 | { 1208 | "type": "rectangle", 1209 | "version": 1176, 1210 | "versionNonce": 157012406, 1211 | "isDeleted": false, 1212 | "id": "pOr_MYBaStcZgnTYCS1NG", 1213 | "fillStyle": "hachure", 1214 | "strokeWidth": 1, 1215 | "strokeStyle": "solid", 1216 | "roughness": 1, 1217 | "opacity": 100, 1218 | "angle": 0, 1219 | "x": 767.8163055485301, 1220 | "y": -136.2845837596032, 1221 | "strokeColor": "#000000", 1222 | "backgroundColor": "#3bc9db", 1223 | "width": 174, 1224 | "height": 48.4, 1225 | "seed": 1872936490, 1226 | "groupIds": [], 1227 | "frameId": null, 1228 | "roundness": { 1229 | "type": 3 1230 | }, 1231 | "boundElements": [ 1232 | { 1233 | "type": "text", 1234 | "id": "hot6X8-GvUJnYMuT4-MKc" 1235 | } 1236 | ], 1237 | "updated": 1689969680724, 1238 | "link": null, 1239 | "locked": false 1240 | }, 1241 | { 1242 | "type": "text", 1243 | "version": 968, 1244 | "versionNonce": 1845201052, 1245 | "isDeleted": false, 1246 | "id": "hot6X8-GvUJnYMuT4-MKc", 1247 | "fillStyle": "hachure", 1248 | "strokeWidth": 1, 1249 | "strokeStyle": "solid", 1250 | "roughness": 0, 1251 | "opacity": 100, 1252 | "angle": 0, 1253 | "x": 783.7203506535105, 1254 | "y": -131.2845837596032, 1255 | "strokeColor": "#000000", 1256 | "backgroundColor": "#fa5252", 1257 | "width": 142.19190979003906, 1258 | "height": 38.4, 1259 | "seed": 1071238530, 1260 | "groupIds": [], 1261 | "frameId": null, 1262 | "roundness": null, 1263 | "boundElements": [], 1264 | "updated": 1677767862280, 1265 | "link": null, 1266 | "locked": false, 1267 | "fontSize": 16, 1268 | "fontFamily": 1, 1269 | "text": "SoccerManager\n", 1270 | "textAlign": "center", 1271 | "verticalAlign": "middle", 1272 | "containerId": "pOr_MYBaStcZgnTYCS1NG", 1273 | "originalText": "SoccerManager\n", 1274 | "lineHeight": 1.2, 1275 | "baseline": 33 1276 | }, 1277 | { 1278 | "type": "rectangle", 1279 | "version": 1244, 1280 | "versionNonce": 676056234, 1281 | "isDeleted": false, 1282 | "id": "UOnmWJ2-O5Bbh4qa9r44X", 1283 | "fillStyle": "hachure", 1284 | "strokeWidth": 1, 1285 | "strokeStyle": "solid", 1286 | "roughness": 1, 1287 | "opacity": 100, 1288 | "angle": 0, 1289 | "x": 767.8163055485301, 1290 | "y": -462.5838086131188, 1291 | "strokeColor": "#000000", 1292 | "backgroundColor": "#3bc9db", 1293 | "width": 174, 1294 | "height": 48.4, 1295 | "seed": 1908238570, 1296 | "groupIds": [], 1297 | "frameId": null, 1298 | "roundness": { 1299 | "type": 3 1300 | }, 1301 | "boundElements": [ 1302 | { 1303 | "type": "text", 1304 | "id": "20fMu97BbuHfRo_9ensJk" 1305 | } 1306 | ], 1307 | "updated": 1689969680724, 1308 | "link": null, 1309 | "locked": false 1310 | }, 1311 | { 1312 | "type": "text", 1313 | "version": 1037, 1314 | "versionNonce": 208416292, 1315 | "isDeleted": false, 1316 | "id": "20fMu97BbuHfRo_9ensJk", 1317 | "fillStyle": "hachure", 1318 | "strokeWidth": 1, 1319 | "strokeStyle": "solid", 1320 | "roughness": 0, 1321 | "opacity": 100, 1322 | "angle": 0, 1323 | "x": 783.7203506535105, 1324 | "y": -457.5838086131188, 1325 | "strokeColor": "#000000", 1326 | "backgroundColor": "#fa5252", 1327 | "width": 142.19190979003906, 1328 | "height": 38.4, 1329 | "seed": 648525890, 1330 | "groupIds": [], 1331 | "frameId": null, 1332 | "roundness": null, 1333 | "boundElements": [], 1334 | "updated": 1677767849924, 1335 | "link": null, 1336 | "locked": false, 1337 | "fontSize": 16, 1338 | "fontFamily": 1, 1339 | "text": "SoccerManager\n", 1340 | "textAlign": "center", 1341 | "verticalAlign": "middle", 1342 | "containerId": "UOnmWJ2-O5Bbh4qa9r44X", 1343 | "originalText": "SoccerManager\n", 1344 | "lineHeight": 1.2, 1345 | "baseline": 33 1346 | }, 1347 | { 1348 | "type": "rectangle", 1349 | "version": 1063, 1350 | "versionNonce": 113617910, 1351 | "isDeleted": false, 1352 | "id": "SJKe9uJGEPDKDkcF9xSLN", 1353 | "fillStyle": "cross-hatch", 1354 | "strokeWidth": 1, 1355 | "strokeStyle": "solid", 1356 | "roughness": 1, 1357 | "opacity": 100, 1358 | "angle": 0, 1359 | "x": 748.3163055485301, 1360 | "y": -246.61729860335322, 1361 | "strokeColor": "#000000", 1362 | "backgroundColor": "#fa5252", 1363 | "width": 213, 1364 | "height": 111, 1365 | "seed": 414497706, 1366 | "groupIds": [], 1367 | "frameId": null, 1368 | "roundness": { 1369 | "type": 3 1370 | }, 1371 | "boundElements": [ 1372 | { 1373 | "id": "jzrlCHb-PXCgBo4JxFQic", 1374 | "type": "arrow" 1375 | }, 1376 | { 1377 | "id": "kdSxQkudl_4cgqcPQ6xcs", 1378 | "type": "arrow" 1379 | }, 1380 | { 1381 | "type": "text", 1382 | "id": "6XInDICZjBoQz2vVSKpyP" 1383 | }, 1384 | { 1385 | "id": "Vpv8KLhArC7oORbHy5mNF", 1386 | "type": "arrow" 1387 | } 1388 | ], 1389 | "updated": 1689969639895, 1390 | "link": null, 1391 | "locked": false 1392 | }, 1393 | { 1394 | "type": "text", 1395 | "version": 949, 1396 | "versionNonce": 1384827300, 1397 | "isDeleted": false, 1398 | "id": "6XInDICZjBoQz2vVSKpyP", 1399 | "fillStyle": "cross-hatch", 1400 | "strokeWidth": 2, 1401 | "strokeStyle": "solid", 1402 | "roughness": 0, 1403 | "opacity": 100, 1404 | "angle": 0, 1405 | "x": 756.4383529728465, 1406 | "y": -224.7172986033532, 1407 | "strokeColor": "#000000", 1408 | "backgroundColor": "#82c91e", 1409 | "width": 196.7559051513672, 1410 | "height": 67.2, 1411 | "seed": 614290468, 1412 | "groupIds": [], 1413 | "frameId": null, 1414 | "roundness": null, 1415 | "boundElements": [], 1416 | "updated": 1677767849924, 1417 | "link": null, 1418 | "locked": false, 1419 | "fontSize": 28, 1420 | "fontFamily": 1, 1421 | "text": "Etherum-Client\n", 1422 | "textAlign": "center", 1423 | "verticalAlign": "middle", 1424 | "containerId": "SJKe9uJGEPDKDkcF9xSLN", 1425 | "originalText": "Etherum-Client\n", 1426 | "lineHeight": 1.2, 1427 | "baseline": 58 1428 | }, 1429 | { 1430 | "type": "rectangle", 1431 | "version": 1248, 1432 | "versionNonce": 207142198, 1433 | "isDeleted": false, 1434 | "id": "OgsaF-unh8ZXBU7Zv5han", 1435 | "fillStyle": "cross-hatch", 1436 | "strokeWidth": 1, 1437 | "strokeStyle": "solid", 1438 | "roughness": 1, 1439 | "opacity": 100, 1440 | "angle": 0, 1441 | "x": 451.4811767643504, 1442 | "y": -330.5729870799157, 1443 | "strokeColor": "#000000", 1444 | "backgroundColor": "#fa5252", 1445 | "width": 210, 1446 | "height": 107, 1447 | "seed": 1924380266, 1448 | "groupIds": [], 1449 | "frameId": null, 1450 | "roundness": { 1451 | "type": 3 1452 | }, 1453 | "boundElements": [ 1454 | { 1455 | "id": "jzrlCHb-PXCgBo4JxFQic", 1456 | "type": "arrow" 1457 | }, 1458 | { 1459 | "id": "kdSxQkudl_4cgqcPQ6xcs", 1460 | "type": "arrow" 1461 | }, 1462 | { 1463 | "type": "text", 1464 | "id": "7iDGztf57GII7V1f2pp4W" 1465 | }, 1466 | { 1467 | "id": "18PIhYY3lq15JjREJNaJA", 1468 | "type": "arrow" 1469 | }, 1470 | { 1471 | "id": "AsR2kjKvBls7VJ6J8NsO5", 1472 | "type": "arrow" 1473 | }, 1474 | { 1475 | "id": "Vpv8KLhArC7oORbHy5mNF", 1476 | "type": "arrow" 1477 | } 1478 | ], 1479 | "updated": 1689969639895, 1480 | "link": null, 1481 | "locked": false 1482 | }, 1483 | { 1484 | "type": "text", 1485 | "version": 1124, 1486 | "versionNonce": 907522212, 1487 | "isDeleted": false, 1488 | "id": "7iDGztf57GII7V1f2pp4W", 1489 | "fillStyle": "cross-hatch", 1490 | "strokeWidth": 2, 1491 | "strokeStyle": "solid", 1492 | "roughness": 0, 1493 | "opacity": 100, 1494 | "angle": 0, 1495 | "x": 458.1032241886668, 1496 | "y": -293.87298707991573, 1497 | "strokeColor": "#000000", 1498 | "backgroundColor": "#82c91e", 1499 | "width": 196.7559051513672, 1500 | "height": 33.6, 1501 | "seed": 1006558620, 1502 | "groupIds": [], 1503 | "frameId": null, 1504 | "roundness": null, 1505 | "boundElements": [], 1506 | "updated": 1677767849925, 1507 | "link": null, 1508 | "locked": false, 1509 | "fontSize": 28, 1510 | "fontFamily": 1, 1511 | "text": "Etherum-Client", 1512 | "textAlign": "center", 1513 | "verticalAlign": "middle", 1514 | "containerId": "OgsaF-unh8ZXBU7Zv5han", 1515 | "originalText": "Etherum-Client", 1516 | "lineHeight": 1.2, 1517 | "baseline": 24 1518 | } 1519 | ], 1520 | "appState": { 1521 | "gridSize": null, 1522 | "viewBackgroundColor": "#ffffff" 1523 | }, 1524 | "files": {} 1525 | } -------------------------------------------------------------------------------- /documentation/project-diagram.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivangfr/ethereum-springboot-react/5a88e42e28c5056439672a8493120bfbe967bfb0/documentation/project-diagram.jpeg -------------------------------------------------------------------------------- /ethereum-api/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivangfr/ethereum-springboot-react/5a88e42e28c5056439672a8493120bfbe967bfb0/ethereum-api/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /ethereum-api/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 3 | -------------------------------------------------------------------------------- /ethereum-api/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.2.0 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | # e.g. to debug Maven itself, use 32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | # ---------------------------------------------------------------------------- 35 | 36 | if [ -z "$MAVEN_SKIP_RC" ] ; then 37 | 38 | if [ -f /usr/local/etc/mavenrc ] ; then 39 | . /usr/local/etc/mavenrc 40 | fi 41 | 42 | if [ -f /etc/mavenrc ] ; then 43 | . /etc/mavenrc 44 | fi 45 | 46 | if [ -f "$HOME/.mavenrc" ] ; then 47 | . "$HOME/.mavenrc" 48 | fi 49 | 50 | fi 51 | 52 | # OS specific support. $var _must_ be set to either true or false. 53 | cygwin=false; 54 | darwin=false; 55 | mingw=false 56 | case "$(uname)" in 57 | CYGWIN*) cygwin=true ;; 58 | MINGW*) mingw=true;; 59 | Darwin*) darwin=true 60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 62 | if [ -z "$JAVA_HOME" ]; then 63 | if [ -x "/usr/libexec/java_home" ]; then 64 | JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME 65 | else 66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME 67 | fi 68 | fi 69 | ;; 70 | esac 71 | 72 | if [ -z "$JAVA_HOME" ] ; then 73 | if [ -r /etc/gentoo-release ] ; then 74 | JAVA_HOME=$(java-config --jre-home) 75 | fi 76 | fi 77 | 78 | # For Cygwin, ensure paths are in UNIX format before anything is touched 79 | if $cygwin ; then 80 | [ -n "$JAVA_HOME" ] && 81 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME") 82 | [ -n "$CLASSPATH" ] && 83 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH") 84 | fi 85 | 86 | # For Mingw, ensure paths are in UNIX format before anything is touched 87 | if $mingw ; then 88 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && 89 | JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" 90 | fi 91 | 92 | if [ -z "$JAVA_HOME" ]; then 93 | javaExecutable="$(which javac)" 94 | if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then 95 | # readlink(1) is not available as standard on Solaris 10. 96 | readLink=$(which readlink) 97 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then 98 | if $darwin ; then 99 | javaHome="$(dirname "\"$javaExecutable\"")" 100 | javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" 101 | else 102 | javaExecutable="$(readlink -f "\"$javaExecutable\"")" 103 | fi 104 | javaHome="$(dirname "\"$javaExecutable\"")" 105 | javaHome=$(expr "$javaHome" : '\(.*\)/bin') 106 | JAVA_HOME="$javaHome" 107 | export JAVA_HOME 108 | fi 109 | fi 110 | fi 111 | 112 | if [ -z "$JAVACMD" ] ; then 113 | if [ -n "$JAVA_HOME" ] ; then 114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 115 | # IBM's JDK on AIX uses strange locations for the executables 116 | JAVACMD="$JAVA_HOME/jre/sh/java" 117 | else 118 | JAVACMD="$JAVA_HOME/bin/java" 119 | fi 120 | else 121 | JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" 122 | fi 123 | fi 124 | 125 | if [ ! -x "$JAVACMD" ] ; then 126 | echo "Error: JAVA_HOME is not defined correctly." >&2 127 | echo " We cannot execute $JAVACMD" >&2 128 | exit 1 129 | fi 130 | 131 | if [ -z "$JAVA_HOME" ] ; then 132 | echo "Warning: JAVA_HOME environment variable is not set." 133 | fi 134 | 135 | # traverses directory structure from process work directory to filesystem root 136 | # first directory with .mvn subdirectory is considered project base directory 137 | find_maven_basedir() { 138 | if [ -z "$1" ] 139 | then 140 | echo "Path not specified to find_maven_basedir" 141 | return 1 142 | fi 143 | 144 | basedir="$1" 145 | wdir="$1" 146 | while [ "$wdir" != '/' ] ; do 147 | if [ -d "$wdir"/.mvn ] ; then 148 | basedir=$wdir 149 | break 150 | fi 151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 152 | if [ -d "${wdir}" ]; then 153 | wdir=$(cd "$wdir/.." || exit 1; pwd) 154 | fi 155 | # end of workaround 156 | done 157 | printf '%s' "$(cd "$basedir" || exit 1; pwd)" 158 | } 159 | 160 | # concatenates all lines of a file 161 | concat_lines() { 162 | if [ -f "$1" ]; then 163 | # Remove \r in case we run on Windows within Git Bash 164 | # and check out the repository with auto CRLF management 165 | # enabled. Otherwise, we may read lines that are delimited with 166 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word 167 | # splitting rules. 168 | tr -s '\r\n' ' ' < "$1" 169 | fi 170 | } 171 | 172 | log() { 173 | if [ "$MVNW_VERBOSE" = true ]; then 174 | printf '%s\n' "$1" 175 | fi 176 | } 177 | 178 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")") 179 | if [ -z "$BASE_DIR" ]; then 180 | exit 1; 181 | fi 182 | 183 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR 184 | log "$MAVEN_PROJECTBASEDIR" 185 | 186 | ########################################################################################## 187 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 188 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 189 | ########################################################################################## 190 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" 191 | if [ -r "$wrapperJarPath" ]; then 192 | log "Found $wrapperJarPath" 193 | else 194 | log "Couldn't find $wrapperJarPath, downloading it ..." 195 | 196 | if [ -n "$MVNW_REPOURL" ]; then 197 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 198 | else 199 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 200 | fi 201 | while IFS="=" read -r key value; do 202 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) 203 | safeValue=$(echo "$value" | tr -d '\r') 204 | case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; 205 | esac 206 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 207 | log "Downloading from: $wrapperUrl" 208 | 209 | if $cygwin; then 210 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") 211 | fi 212 | 213 | if command -v wget > /dev/null; then 214 | log "Found wget ... using wget" 215 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" 216 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 217 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 218 | else 219 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 220 | fi 221 | elif command -v curl > /dev/null; then 222 | log "Found curl ... using curl" 223 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" 224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 226 | else 227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 228 | fi 229 | else 230 | log "Falling back to using Java to download" 231 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" 232 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" 233 | # For Cygwin, switch paths to Windows format before running javac 234 | if $cygwin; then 235 | javaSource=$(cygpath --path --windows "$javaSource") 236 | javaClass=$(cygpath --path --windows "$javaClass") 237 | fi 238 | if [ -e "$javaSource" ]; then 239 | if [ ! -e "$javaClass" ]; then 240 | log " - Compiling MavenWrapperDownloader.java ..." 241 | ("$JAVA_HOME/bin/javac" "$javaSource") 242 | fi 243 | if [ -e "$javaClass" ]; then 244 | log " - Running MavenWrapperDownloader.java ..." 245 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" 246 | fi 247 | fi 248 | fi 249 | fi 250 | ########################################################################################## 251 | # End of extension 252 | ########################################################################################## 253 | 254 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file 255 | wrapperSha256Sum="" 256 | while IFS="=" read -r key value; do 257 | case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; 258 | esac 259 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 260 | if [ -n "$wrapperSha256Sum" ]; then 261 | wrapperSha256Result=false 262 | if command -v sha256sum > /dev/null; then 263 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then 264 | wrapperSha256Result=true 265 | fi 266 | elif command -v shasum > /dev/null; then 267 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then 268 | wrapperSha256Result=true 269 | fi 270 | else 271 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." 272 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." 273 | exit 1 274 | fi 275 | if [ $wrapperSha256Result = false ]; then 276 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 277 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 278 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 279 | exit 1 280 | fi 281 | fi 282 | 283 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 284 | 285 | # For Cygwin, switch paths to Windows format before running java 286 | if $cygwin; then 287 | [ -n "$JAVA_HOME" ] && 288 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") 289 | [ -n "$CLASSPATH" ] && 290 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH") 291 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 292 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") 293 | fi 294 | 295 | # Provide a "standardized" way to retrieve the CLI args that will 296 | # work with both Windows and non-Windows executions. 297 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" 298 | export MAVEN_CMD_LINE_ARGS 299 | 300 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 301 | 302 | # shellcheck disable=SC2086 # safe args 303 | exec "$JAVACMD" \ 304 | $MAVEN_OPTS \ 305 | $MAVEN_DEBUG_OPTS \ 306 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 307 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 308 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 309 | -------------------------------------------------------------------------------- /ethereum-api/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 168 | " exit 1;"^ 169 | "}"^ 170 | "}" 171 | if ERRORLEVEL 1 goto error 172 | ) 173 | 174 | @REM Provide a "standardized" way to retrieve the CLI args that will 175 | @REM work with both Windows and non-Windows executions. 176 | set MAVEN_CMD_LINE_ARGS=%* 177 | 178 | %MAVEN_JAVA_EXE% ^ 179 | %JVM_CONFIG_MAVEN_PROPS% ^ 180 | %MAVEN_OPTS% ^ 181 | %MAVEN_DEBUG_OPTS% ^ 182 | -classpath %WRAPPER_JAR% ^ 183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 185 | if ERRORLEVEL 1 goto error 186 | goto end 187 | 188 | :error 189 | set ERROR_CODE=1 190 | 191 | :end 192 | @endlocal & set ERROR_CODE=%ERROR_CODE% 193 | 194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 198 | :skipRcPost 199 | 200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 202 | 203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 204 | 205 | cmd /C exit /B %ERROR_CODE% 206 | -------------------------------------------------------------------------------- /ethereum-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.1.2 9 | 10 | 11 | 12 | com.ivanfranchin 13 | ethereum-api 14 | 0.0.1-SNAPSHOT 15 | ethereum-api 16 | Demo project for Spring Boot 17 | 18 | 19 | 17 20 | 4.5.5 21 | 2.1.0 22 | 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-actuator 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-web 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-validation 36 | 37 | 38 | 39 | 40 | org.web3j 41 | core 42 | ${web3j.version} 43 | 44 | 45 | 46 | 47 | org.springdoc 48 | springdoc-openapi-starter-webmvc-ui 49 | ${springdoc-openapi.version} 50 | 51 | 52 | 53 | org.projectlombok 54 | lombok 55 | true 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-starter-test 60 | test 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-maven-plugin 69 | 70 | 71 | 72 | org.projectlombok 73 | lombok 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/EthereumApiApplication.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class EthereumApiApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(EthereumApiApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/config/ErrorAttributesConfig.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.config; 2 | 3 | import org.springframework.boot.web.error.ErrorAttributeOptions; 4 | import org.springframework.boot.web.error.ErrorAttributeOptions.Include; 5 | import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; 6 | import org.springframework.boot.web.servlet.error.ErrorAttributes; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.web.context.request.WebRequest; 10 | 11 | import java.util.Map; 12 | 13 | @Configuration 14 | public class ErrorAttributesConfig { 15 | 16 | @Bean 17 | public ErrorAttributes errorAttributes() { 18 | return new DefaultErrorAttributes() { 19 | @Override 20 | public Map getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { 21 | return super.getErrorAttributes(webRequest, options.including(Include.EXCEPTION, Include.MESSAGE, Include.BINDING_ERRORS)); 22 | } 23 | }; 24 | } 25 | } -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.config; 2 | 3 | import io.swagger.v3.oas.models.Components; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.oas.models.info.Info; 6 | import org.springdoc.core.models.GroupedOpenApi; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | @Configuration 12 | public class SwaggerConfig { 13 | 14 | @Value("${spring.application.name}") 15 | private String applicationName; 16 | 17 | @Bean 18 | public OpenAPI customOpenAPI() { 19 | return new OpenAPI().components(new Components()).info(new Info().title(applicationName)); 20 | } 21 | 22 | @Bean 23 | public GroupedOpenApi customApi() { 24 | return GroupedOpenApi.builder().group("api").pathsToMatch("/api/**").build(); 25 | } 26 | 27 | @Bean 28 | public GroupedOpenApi actuatorApi() { 29 | return GroupedOpenApi.builder().group("actuator").pathsToMatch("/actuator/**").build(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/config/Web3jConfig.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.web3j.protocol.Web3j; 6 | import org.web3j.protocol.http.HttpService; 7 | 8 | @Configuration 9 | public class Web3jConfig { 10 | 11 | @Bean 12 | public Web3j web3j() { 13 | return Web3j.build(new HttpService()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/contract/SoccerManager.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.contract; 2 | 3 | import io.reactivex.Flowable; 4 | import io.reactivex.functions.Function; 5 | import java.math.BigInteger; 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.concurrent.Callable; 11 | import org.web3j.abi.EventEncoder; 12 | import org.web3j.abi.TypeReference; 13 | import org.web3j.abi.datatypes.Address; 14 | import org.web3j.abi.datatypes.Bool; 15 | import org.web3j.abi.datatypes.DynamicArray; 16 | import org.web3j.abi.datatypes.Event; 17 | import org.web3j.abi.datatypes.Type; 18 | import org.web3j.abi.datatypes.Utf8String; 19 | import org.web3j.abi.datatypes.generated.Uint256; 20 | import org.web3j.crypto.Credentials; 21 | import org.web3j.protocol.Web3j; 22 | import org.web3j.protocol.core.DefaultBlockParameter; 23 | import org.web3j.protocol.core.RemoteCall; 24 | import org.web3j.protocol.core.RemoteFunctionCall; 25 | import org.web3j.protocol.core.methods.request.EthFilter; 26 | import org.web3j.protocol.core.methods.response.BaseEventResponse; 27 | import org.web3j.protocol.core.methods.response.Log; 28 | import org.web3j.protocol.core.methods.response.TransactionReceipt; 29 | import org.web3j.tuples.generated.Tuple6; 30 | import org.web3j.tx.Contract; 31 | import org.web3j.tx.TransactionManager; 32 | import org.web3j.tx.gas.ContractGasProvider; 33 | 34 | /** 35 | *

Auto generated code. 36 | *

Do not modify! 37 | *

Please use the web3j command line tools, 38 | * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the 39 | * codegen module to update. 40 | * 41 | *

Generated with web3j version 4.5.5. 42 | */ 43 | @SuppressWarnings("rawtypes") 44 | public class SoccerManager extends Contract { 45 | private static final String BINARY = "60806040523480156200001157600080fd5b50600080546001600160a01b03191633179055620000376001600160e01b036200003d16565b62000342565b62000095604051806040016040528060088152602001672ba2ab22a92a27a760c11b815250670de0b6b3a76400006040518060800160405280605981526020016200131e60599139336001600160e01b036200015216565b50620000f16040518060400160405280600b81526020016a46454c495045204d454c4f60a81b815250671bc16d674ec800006040518060800160405280605c8152602001620013d5605c9139336001600160e01b036200015216565b506200014f6040518060400160405280600d81526020016c23aaa9aa20ab279023a7a6a2ad60991b8152506729a2241af62c00006040518060800160405280605e815260200162001377605e9139336001600160e01b036200015216565b50565b6001805481018082556040805160e08101825282815260208082018581528284018a8152606084018a90526080840189905260a084018790526001600160a01b03881660c085015260008681526002808552958120855181559251978301805460ff19169815159890981790975551805193949193620001db939285019291909101906200029d565b506060820151600382015560808201518051620002039160048401916020909101906200029d565b5060a0820151600591909101805460c09093015160ff1990931691151591909117610100600160a81b0319166101006001600160a01b03938416021790556004805460018181019092557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b018390559316600090815260036020908152604082208054958601815582529020909201829055509392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620002e057805160ff191683800117855562000310565b8280016001018555821562000310579182015b8281111562000310578251825591602001919060010190620002f3565b506200031e92915062000322565b5090565b6200033f91905b808211156200031e576000815560010162000329565b90565b610fcc80620003526000396000f3fe6080604052600436106100555760003560e01c80637de81fca1461005a578063aab48c521461008b578063e55ae4e8146101df578063eca2f3c81461030f578063fc94f54014610374578063fd6673f5146103a6575b600080fd5b6100776004803603602081101561007057600080fd5b50356103bb565b604080519115158252519081900360200190f35b34801561009757600080fd5b506101cd600480360360608110156100ae57600080fd5b8101906020810181356401000000008111156100c957600080fd5b8201836020820111156100db57600080fd5b803590602001918460018302840111640100000000831117156100fd57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295843595909490935060408101925060200135905064010000000081111561015857600080fd5b82018360208201111561016a57600080fd5b8035906020019184600183028401116401000000008311171561018c57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506106c8945050505050565b60408051918252519081900360200190f35b3480156101eb57600080fd5b506102096004803603602081101561020257600080fd5b503561088b565b6040805187815290810185905282151560808201526001600160a01b03821660a082015260c060208083018281528851928401929092528751606084019160e0850191908a019080838360005b8381101561026e578181015183820152602001610256565b50505050905090810190601f16801561029b5780820380516001836020036101000a031916815260200191505b50838103825286518152865160209182019188019080838360005b838110156102ce5781810151838201526020016102b6565b50505050905090810190601f1680156102fb5780820380516001836020036101000a031916815260200191505b509850505050505050505060405180910390f35b34801561031b57600080fd5b50610324610a54565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610360578181015183820152602001610348565b505050509050019250505060405180910390f35b34801561038057600080fd5b506100776004803603604081101561039757600080fd5b50803590602001351515610ab6565b3480156103b257600080fd5b506101cd610c34565b60008181526002602052604081206001015460ff16610418576040805162461bcd60e51b8152602060048201526014602482015273141b185e595c88191bd95cdb89dd08195e1a5cdd60621b604482015290519081900360640190fd5b60008281526002602052604090206005015460ff16610477576040805162461bcd60e51b8152602060048201526016602482015275506c61796572206973206e6f7420666f722073616c6560501b604482015290519081900360640190fd5b60008281526002602052604090206005015461010090046001600160a01b03163314156104eb576040805162461bcd60e51b815260206004820181905260248201527f596f752061726520616c72656164792074686520706c61796572206167656e74604482015290519081900360640190fd5b6000828152600260205260409020600301543414610550576040805162461bcd60e51b815260206004820152601860248201527f416d6f756e742073656e7420697320696e636f72726563740000000000000000604482015290519081900360640190fd5b600082815260026020526040902060030154333110156105a15760405162461bcd60e51b815260040180806020018281038252602c815260200180610ef3602c913960400191505060405180910390fd5b6000828152600260209081526040808320600501805433610100908102610100600160a81b031983161792839055918290046001600160a01b03908116865260038552838620805460018101825590875294862090940187905504919091168083529120610615908463ffffffff610c3a16565b506000838152600260205260408082206003015490516001600160a01b0384169282156108fc02929190818181858888f1935050505015801561065c573d6000803e3d6000fd5b506000838152600260209081526040918290206003015482518681526001600160a01b038516928101929092523382840152606082015290517f87e04f5f6a6261cdd2f775831d9ffe91bb9cacb100d78a01bba93e0be84c18069181900360800190a150600192915050565b600080546001600160a01b03163314610728576040805162461bcd60e51b815260206004820152601e60248201527f596f7520617265206e6f742074686520636f6e7472616374206f776e65720000604482015290519081900360640190fd5b60008451116107685760405162461bcd60e51b8152600401808060200182810382526023815260200180610ed06023913960400191505060405180910390fd5b600083116107bd576040805162461bcd60e51b815260206004820181905260248201527f54686520617267756d656e742027707269636527206d757374206265203e2030604482015290519081900360640190fd5b60006107cb85858533610cc7565b90507f0c67b919d648c2b4bd260225fe9b485cc70a7e619047dcf5bb598ae38a0b72888186866001604051808581526020018060200184815260200183151515158152602001828103825285818151815260200191508051906020019080838360005b8381101561084657818101518382015260200161082e565b50505050905090810190601f1680156108735780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1949350505050565b600081815260026020526040812060010154606090829082908290819060ff166108e65760405162461bcd60e51b8152600401808060200182810382526029815260200180610f6f6029913960400191505060405180910390fd5b60008781526002602081815260409283902080546003820154600583015483860180548851610100600183161581026000190190921698909804601f81018890048802890188019099528888529497939690959294600489019460ff841694919093046001600160a01b03169287918301828280156109a65780601f1061097b576101008083540402835291602001916109a6565b820191906000526020600020905b81548152906001019060200180831161098957829003601f168201915b5050865460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152959a5088945092508401905082828015610a345780601f10610a0957610100808354040283529160200191610a34565b820191906000526020600020905b815481529060010190602001808311610a1757829003601f168201915b505050505092508090509650965096509650965096505091939550919395565b33600090815260036020908152604091829020805483518184028101840190945280845260609392830182828015610aab57602002820191906000526020600020905b815481526020019060010190808311610a97575b505050505090505b90565b60008281526002602052604081206001015460ff16610b065760405162461bcd60e51b8152600401808060200182810382526029815260200180610f6f6029913960400191505060405180910390fd5b60008381526002602052604090206005015460ff1615158215151415610b5d5760405162461bcd60e51b815260040180806020018281038252602d815260200180610f42602d913960400191505060405180910390fd5b60008381526002602052604090206005015461010090046001600160a01b03163314610bba5760405162461bcd60e51b8152600401808060200182810382526023815260200180610f1f6023913960400191505060405180910390fd5b600083815260026020908152604091829020600501805485151560ff1982168117909255835187815260ff9091168015159382019390935280840191909152915190917f47116610d83e5898152286936a924c1198dab9b12f87e2eafc3bc39a14570f98919081900360600190a160019150505b92915050565b60045490565b6000805b8354811015610cbd5782848281548110610c5457fe5b90600052602060002001541415610cb557835484906000198101908110610c7757fe5b9060005260206000200154848281548110610c8e57fe5b6000918252602090912001558354610caa856000198301610e0e565b506001915050610c2e565b600101610c3e565b5060009392505050565b6001805481018082556040805160e08101825282815260208082018581528284018a8152606084018a90526080840189905260a084018790526001600160a01b03881660c085015260008681526002808552958120855181559251978301805460ff19169815159890981790975551805193949193610d4e93928501929190910190610e37565b506060820151600382015560808201518051610d74916004840191602090910190610e37565b5060a0820151600591909101805460c09093015160ff1990931691151591909117610100600160a81b0319166101006001600160a01b03938416021790556004805460018181019092557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b018390559316600090815260036020908152604082208054958601815582529020909201829055509392505050565b815481835581811115610e3257600083815260209020610e32918101908301610eb5565b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610e7857805160ff1916838001178555610ea5565b82800160010185558215610ea5579182015b82811115610ea5578251825591602001919060010190610e8a565b50610eb1929150610eb5565b5090565b610ab391905b80821115610eb15760008155600101610ebb56fe54686520617267756d656e7420276e616d65272063616e6e6f7420626520656d707479596f7520646f6e2774206861766520656e6f75676874206574686572206f6e20796f75722062616c616e6365596f7520617265206e6f7420746865206167656e74206f662074686520706c61796572506c6179657220666f7253616c6520737461747573206973207468652073616d6520616c726561647920736574506c6179657220776974682074686520696e666f726d656420696420646f65736e2774206578697374a265627a7a72315820e2f95131317593473c441de172cdbd408c050bd5a70408ce2f447fd90352502f64736f6c6343000511003268747470733a2f2f73332e616d617a6f6e6177732e636f6d2f7365702d6275636b65742d70726f642f77702d636f6e74656e742f75706c6f6164732f323031392f30352f30323038323733312f7765766572746f6e2e706e6768747470733a2f2f73332e616d617a6f6e6177732e636f6d2f7365702d6275636b65742d70726f642f77702d636f6e74656e742f75706c6f6164732f323032302f30312f30323038303135332f6775737461766f2d676f6d657a2e706e6768747470733a2f2f73332e616d617a6f6e6177732e636f6d2f7365702d6275636b65742d70726f642f77702d636f6e74656e742f75706c6f6164732f323031392f30382f30323038303034392f66656c6970652d6d656c6f2e706e67"; 46 | 47 | public static final String FUNC_ADDPLAYER = "addPlayer"; 48 | 49 | public static final String FUNC_BUYPLAYER = "buyPlayer"; 50 | 51 | public static final String FUNC_GETNUMBEROFPLAYERS = "getNumberOfPlayers"; 52 | 53 | public static final String FUNC_GETPLAYER = "getPlayer"; 54 | 55 | public static final String FUNC_GETPLAYERSOFAGENT = "getPlayersOfAgent"; 56 | 57 | public static final String FUNC_UPDATEPLAYER = "updatePlayer"; 58 | 59 | public static final Event PLAYERADDED_EVENT = new Event("PlayerAdded", 60 | Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference() {})); 61 | ; 62 | 63 | public static final Event PLAYERBOUGHT_EVENT = new Event("PlayerBought", 64 | Arrays.>asList(new TypeReference() {}, new TypeReference

() {}, new TypeReference
() {}, new TypeReference() {})); 65 | ; 66 | 67 | public static final Event PLAYERUPDATED_EVENT = new Event("PlayerUpdated", 68 | Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference() {})); 69 | ; 70 | 71 | @Deprecated 72 | protected SoccerManager(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { 73 | super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); 74 | } 75 | 76 | protected SoccerManager(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { 77 | super(BINARY, contractAddress, web3j, credentials, contractGasProvider); 78 | } 79 | 80 | @Deprecated 81 | protected SoccerManager(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { 82 | super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); 83 | } 84 | 85 | protected SoccerManager(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { 86 | super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); 87 | } 88 | 89 | public List getPlayerAddedEvents(TransactionReceipt transactionReceipt) { 90 | List valueList = extractEventParametersWithLog(PLAYERADDED_EVENT, transactionReceipt); 91 | ArrayList responses = new ArrayList(valueList.size()); 92 | for (Contract.EventValuesWithLog eventValues : valueList) { 93 | PlayerAddedEventResponse typedResponse = new PlayerAddedEventResponse(); 94 | typedResponse.log = eventValues.getLog(); 95 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 96 | typedResponse.name = (String) eventValues.getNonIndexedValues().get(1).getValue(); 97 | typedResponse.price = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); 98 | typedResponse.forSale = (Boolean) eventValues.getNonIndexedValues().get(3).getValue(); 99 | responses.add(typedResponse); 100 | } 101 | return responses; 102 | } 103 | 104 | public Flowable playerAddedEventFlowable(EthFilter filter) { 105 | return web3j.ethLogFlowable(filter).map(new Function() { 106 | @Override 107 | public PlayerAddedEventResponse apply(Log log) { 108 | Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(PLAYERADDED_EVENT, log); 109 | PlayerAddedEventResponse typedResponse = new PlayerAddedEventResponse(); 110 | typedResponse.log = log; 111 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 112 | typedResponse.name = (String) eventValues.getNonIndexedValues().get(1).getValue(); 113 | typedResponse.price = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); 114 | typedResponse.forSale = (Boolean) eventValues.getNonIndexedValues().get(3).getValue(); 115 | return typedResponse; 116 | } 117 | }); 118 | } 119 | 120 | public Flowable playerAddedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { 121 | EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); 122 | filter.addSingleTopic(EventEncoder.encode(PLAYERADDED_EVENT)); 123 | return playerAddedEventFlowable(filter); 124 | } 125 | 126 | public List getPlayerBoughtEvents(TransactionReceipt transactionReceipt) { 127 | List valueList = extractEventParametersWithLog(PLAYERBOUGHT_EVENT, transactionReceipt); 128 | ArrayList responses = new ArrayList(valueList.size()); 129 | for (Contract.EventValuesWithLog eventValues : valueList) { 130 | PlayerBoughtEventResponse typedResponse = new PlayerBoughtEventResponse(); 131 | typedResponse.log = eventValues.getLog(); 132 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 133 | typedResponse.oldAgent = (String) eventValues.getNonIndexedValues().get(1).getValue(); 134 | typedResponse.newAgent = (String) eventValues.getNonIndexedValues().get(2).getValue(); 135 | typedResponse.price = (BigInteger) eventValues.getNonIndexedValues().get(3).getValue(); 136 | responses.add(typedResponse); 137 | } 138 | return responses; 139 | } 140 | 141 | public Flowable playerBoughtEventFlowable(EthFilter filter) { 142 | return web3j.ethLogFlowable(filter).map(new Function() { 143 | @Override 144 | public PlayerBoughtEventResponse apply(Log log) { 145 | Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(PLAYERBOUGHT_EVENT, log); 146 | PlayerBoughtEventResponse typedResponse = new PlayerBoughtEventResponse(); 147 | typedResponse.log = log; 148 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 149 | typedResponse.oldAgent = (String) eventValues.getNonIndexedValues().get(1).getValue(); 150 | typedResponse.newAgent = (String) eventValues.getNonIndexedValues().get(2).getValue(); 151 | typedResponse.price = (BigInteger) eventValues.getNonIndexedValues().get(3).getValue(); 152 | return typedResponse; 153 | } 154 | }); 155 | } 156 | 157 | public Flowable playerBoughtEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { 158 | EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); 159 | filter.addSingleTopic(EventEncoder.encode(PLAYERBOUGHT_EVENT)); 160 | return playerBoughtEventFlowable(filter); 161 | } 162 | 163 | public List getPlayerUpdatedEvents(TransactionReceipt transactionReceipt) { 164 | List valueList = extractEventParametersWithLog(PLAYERUPDATED_EVENT, transactionReceipt); 165 | ArrayList responses = new ArrayList(valueList.size()); 166 | for (Contract.EventValuesWithLog eventValues : valueList) { 167 | PlayerUpdatedEventResponse typedResponse = new PlayerUpdatedEventResponse(); 168 | typedResponse.log = eventValues.getLog(); 169 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 170 | typedResponse.oldForSaleValue = (Boolean) eventValues.getNonIndexedValues().get(1).getValue(); 171 | typedResponse.newForSaleValue = (Boolean) eventValues.getNonIndexedValues().get(2).getValue(); 172 | responses.add(typedResponse); 173 | } 174 | return responses; 175 | } 176 | 177 | public Flowable playerUpdatedEventFlowable(EthFilter filter) { 178 | return web3j.ethLogFlowable(filter).map(new Function() { 179 | @Override 180 | public PlayerUpdatedEventResponse apply(Log log) { 181 | Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(PLAYERUPDATED_EVENT, log); 182 | PlayerUpdatedEventResponse typedResponse = new PlayerUpdatedEventResponse(); 183 | typedResponse.log = log; 184 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 185 | typedResponse.oldForSaleValue = (Boolean) eventValues.getNonIndexedValues().get(1).getValue(); 186 | typedResponse.newForSaleValue = (Boolean) eventValues.getNonIndexedValues().get(2).getValue(); 187 | return typedResponse; 188 | } 189 | }); 190 | } 191 | 192 | public Flowable playerUpdatedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { 193 | EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); 194 | filter.addSingleTopic(EventEncoder.encode(PLAYERUPDATED_EVENT)); 195 | return playerUpdatedEventFlowable(filter); 196 | } 197 | 198 | public RemoteFunctionCall addPlayer(String name, BigInteger price, String image) { 199 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( 200 | FUNC_ADDPLAYER, 201 | Arrays.asList(new org.web3j.abi.datatypes.Utf8String(name), 202 | new org.web3j.abi.datatypes.generated.Uint256(price), 203 | new org.web3j.abi.datatypes.Utf8String(image)), 204 | Collections.>emptyList()); 205 | return executeRemoteCallTransaction(function); 206 | } 207 | 208 | public RemoteFunctionCall buyPlayer(BigInteger id, BigInteger weiValue) { 209 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( 210 | FUNC_BUYPLAYER, 211 | Arrays.asList(new org.web3j.abi.datatypes.generated.Uint256(id)), 212 | Collections.>emptyList()); 213 | return executeRemoteCallTransaction(function, weiValue); 214 | } 215 | 216 | public RemoteFunctionCall getNumberOfPlayers() { 217 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETNUMBEROFPLAYERS, 218 | Arrays.asList(), 219 | Arrays.>asList(new TypeReference() {})); 220 | return executeRemoteCallSingleValueReturn(function, BigInteger.class); 221 | } 222 | 223 | public RemoteFunctionCall> getPlayer(BigInteger id) { 224 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETPLAYER, 225 | Arrays.asList(new org.web3j.abi.datatypes.generated.Uint256(id)), 226 | Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference
() {})); 227 | return new RemoteFunctionCall>(function, 228 | new Callable>() { 229 | @Override 230 | public Tuple6 call() throws Exception { 231 | List results = executeCallMultipleValueReturn(function); 232 | return new Tuple6( 233 | (BigInteger) results.get(0).getValue(), 234 | (String) results.get(1).getValue(), 235 | (BigInteger) results.get(2).getValue(), 236 | (String) results.get(3).getValue(), 237 | (Boolean) results.get(4).getValue(), 238 | (String) results.get(5).getValue()); 239 | } 240 | }); 241 | } 242 | 243 | public RemoteFunctionCall getPlayersOfAgent() { 244 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETPLAYERSOFAGENT, 245 | Arrays.asList(), 246 | Arrays.>asList(new TypeReference>() {})); 247 | return new RemoteFunctionCall(function, 248 | new Callable() { 249 | @Override 250 | @SuppressWarnings("unchecked") 251 | public List call() throws Exception { 252 | List result = (List) executeCallSingleValueReturn(function, List.class); 253 | return convertToNative(result); 254 | } 255 | }); 256 | } 257 | 258 | public RemoteFunctionCall updatePlayer(BigInteger id, Boolean forSale) { 259 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( 260 | FUNC_UPDATEPLAYER, 261 | Arrays.asList(new org.web3j.abi.datatypes.generated.Uint256(id), 262 | new org.web3j.abi.datatypes.Bool(forSale)), 263 | Collections.>emptyList()); 264 | return executeRemoteCallTransaction(function); 265 | } 266 | 267 | @Deprecated 268 | public static SoccerManager load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { 269 | return new SoccerManager(contractAddress, web3j, credentials, gasPrice, gasLimit); 270 | } 271 | 272 | @Deprecated 273 | public static SoccerManager load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { 274 | return new SoccerManager(contractAddress, web3j, transactionManager, gasPrice, gasLimit); 275 | } 276 | 277 | public static SoccerManager load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { 278 | return new SoccerManager(contractAddress, web3j, credentials, contractGasProvider); 279 | } 280 | 281 | public static SoccerManager load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { 282 | return new SoccerManager(contractAddress, web3j, transactionManager, contractGasProvider); 283 | } 284 | 285 | public static RemoteCall deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { 286 | return deployRemoteCall(SoccerManager.class, web3j, credentials, contractGasProvider, BINARY, ""); 287 | } 288 | 289 | public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { 290 | return deployRemoteCall(SoccerManager.class, web3j, transactionManager, contractGasProvider, BINARY, ""); 291 | } 292 | 293 | @Deprecated 294 | public static RemoteCall deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { 295 | return deployRemoteCall(SoccerManager.class, web3j, credentials, gasPrice, gasLimit, BINARY, ""); 296 | } 297 | 298 | @Deprecated 299 | public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { 300 | return deployRemoteCall(SoccerManager.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, ""); 301 | } 302 | 303 | public static class PlayerAddedEventResponse extends BaseEventResponse { 304 | public BigInteger playerId; 305 | 306 | public String name; 307 | 308 | public BigInteger price; 309 | 310 | public Boolean forSale; 311 | } 312 | 313 | public static class PlayerBoughtEventResponse extends BaseEventResponse { 314 | public BigInteger playerId; 315 | 316 | public String oldAgent; 317 | 318 | public String newAgent; 319 | 320 | public BigInteger price; 321 | } 322 | 323 | public static class PlayerUpdatedEventResponse extends BaseEventResponse { 324 | public BigInteger playerId; 325 | 326 | public Boolean oldForSaleValue; 327 | 328 | public Boolean newForSaleValue; 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/rest/EthereumController.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.rest; 2 | 3 | import com.ivanfranchin.ethereumapi.rest.dto.CreateWalletDto; 4 | import com.ivanfranchin.ethereumapi.rest.dto.DeployContractDto; 5 | import com.ivanfranchin.ethereumapi.rest.dto.GetWalletAddressDto; 6 | import com.ivanfranchin.ethereumapi.rest.dto.TransferDto; 7 | import com.ivanfranchin.ethereumapi.rest.dto.WalletDto; 8 | import com.ivanfranchin.ethereumapi.service.EthereumService; 9 | import jakarta.validation.Valid; 10 | import lombok.RequiredArgsConstructor; 11 | import org.springframework.http.HttpStatus; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.bind.annotation.PathVariable; 14 | import org.springframework.web.bind.annotation.PostMapping; 15 | import org.springframework.web.bind.annotation.RequestBody; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.ResponseStatus; 18 | import org.springframework.web.bind.annotation.RestController; 19 | import org.web3j.crypto.CipherException; 20 | import org.web3j.crypto.Credentials; 21 | import org.web3j.protocol.core.methods.response.EthSendTransaction; 22 | 23 | import java.io.IOException; 24 | import java.math.BigInteger; 25 | import java.util.List; 26 | 27 | @RequiredArgsConstructor 28 | @RestController 29 | @RequestMapping("/api") 30 | public class EthereumController { 31 | 32 | private final EthereumService ethereumService; 33 | 34 | @GetMapping("/accounts") 35 | public List getAccounts() throws IOException { 36 | return ethereumService.getAccounts(); 37 | } 38 | 39 | @ResponseStatus(HttpStatus.CREATED) 40 | @PostMapping("/wallets/create") 41 | public WalletDto createWallet(@Valid @RequestBody CreateWalletDto createWalletDto) throws Exception { 42 | String file = ethereumService.createWallet(createWalletDto.getPassword()); 43 | String address = ethereumService.getWallet(createWalletDto.getPassword(), file).getAddress(); 44 | 45 | if (createWalletDto.getInitialBalance().signum() == 1) { 46 | String account0Address = ethereumService.getAccounts().get(0); 47 | ethereumService.transfer(account0Address, address, createWalletDto.getInitialBalance(), 48 | BigInteger.valueOf(20000000000L), BigInteger.valueOf(21000L)); 49 | } 50 | 51 | return new WalletDto(file, address); 52 | } 53 | 54 | @PostMapping("/wallets/get") 55 | public Credentials getWallet(@Valid @RequestBody GetWalletAddressDto getWalletAddressDto) throws IOException, CipherException { 56 | return ethereumService.getWallet(getWalletAddressDto.getPassword(), getWalletAddressDto.getFile()); 57 | } 58 | 59 | @GetMapping("/wallets/{address}/balance") 60 | public BigInteger getWalletBalance(@PathVariable String address) throws IOException { 61 | return ethereumService.getWalletBalance(address); 62 | } 63 | 64 | @PostMapping("/wallets/transfer") 65 | public EthSendTransaction transfer(@Valid @RequestBody TransferDto transferDto) throws IOException { 66 | return ethereumService.transfer( 67 | transferDto.getFromAddress(), 68 | transferDto.getToAddress(), 69 | transferDto.getAmount(), 70 | transferDto.getGasPrice(), 71 | transferDto.getGasLimit()); 72 | } 73 | 74 | @ResponseStatus(HttpStatus.CREATED) 75 | @PostMapping("/contracts/deploy/soccerManager") 76 | public String deploySoccerManagerContract(@Valid @RequestBody DeployContractDto deployContractDto) throws Exception { 77 | return ethereumService.deploySoccerManagerContract( 78 | deployContractDto.getPassword(), 79 | deployContractDto.getFile(), 80 | deployContractDto.getGasPrice(), 81 | deployContractDto.getGasLimit()); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/rest/dto/CreateWalletDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.rest.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.NotNull; 6 | import lombok.Data; 7 | 8 | import java.math.BigInteger; 9 | 10 | @Data 11 | public class CreateWalletDto { 12 | 13 | @Schema(example = "123") 14 | @NotBlank 15 | private String password; 16 | 17 | @Schema(example = "10000000000000000000") 18 | @NotNull 19 | private BigInteger initialBalance; 20 | } 21 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/rest/dto/DeployContractDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.rest.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.NotNull; 6 | import jakarta.validation.constraints.Positive; 7 | import lombok.Data; 8 | 9 | import java.math.BigInteger; 10 | 11 | @Data 12 | public class DeployContractDto { 13 | 14 | @Schema(example = "123") 15 | @NotBlank 16 | private String password; 17 | 18 | @Schema(example = "/path/to/UTC...") 19 | @NotBlank 20 | private String file; 21 | 22 | @Schema(example = "1") 23 | @NotNull 24 | @Positive 25 | private BigInteger gasPrice; 26 | 27 | @Schema(example = "3000000") 28 | @NotNull 29 | @Positive 30 | private BigInteger gasLimit; 31 | } 32 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/rest/dto/GetWalletAddressDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.rest.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotBlank; 5 | import lombok.Data; 6 | 7 | @Data 8 | public class GetWalletAddressDto { 9 | 10 | @Schema(example = "123") 11 | @NotBlank 12 | private String password; 13 | 14 | @Schema(example = "/path/to/UTC...") 15 | @NotBlank 16 | private String file; 17 | } 18 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/rest/dto/TransferDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.rest.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.NotNull; 6 | import jakarta.validation.constraints.Positive; 7 | import lombok.Data; 8 | 9 | import java.math.BigInteger; 10 | 11 | @Data 12 | public class TransferDto { 13 | 14 | @Schema(example = "0x...") 15 | @NotBlank 16 | private String fromAddress; 17 | 18 | @Schema(example = "0x...") 19 | @NotBlank 20 | private String toAddress; 21 | 22 | @Schema(example = "10000000000000000000") 23 | @NotNull 24 | @Positive 25 | private BigInteger amount; 26 | 27 | @Schema(example = "20000000000") 28 | @NotNull 29 | @Positive 30 | private BigInteger gasPrice; 31 | 32 | @Schema(example = "21000") 33 | @NotNull 34 | @Positive 35 | private BigInteger gasLimit; 36 | } 37 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/rest/dto/WalletDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.rest.dto; 2 | 3 | public record WalletDto(String file, String address) { 4 | } 5 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/service/EthereumService.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.service; 2 | 3 | import org.web3j.crypto.CipherException; 4 | import org.web3j.crypto.Credentials; 5 | import org.web3j.protocol.core.methods.response.EthSendTransaction; 6 | 7 | import java.io.IOException; 8 | import java.math.BigInteger; 9 | import java.util.List; 10 | 11 | public interface EthereumService { 12 | 13 | List getAccounts() throws IOException; 14 | 15 | String createWallet(String password) throws Exception; 16 | 17 | Credentials getWallet(String password, String file) throws IOException, CipherException; 18 | 19 | BigInteger getWalletBalance(String address) throws IOException; 20 | 21 | EthSendTransaction transfer(String fromAddress, String toAddress, BigInteger amount, BigInteger gasPrice, BigInteger gasLimit) throws IOException; 22 | 23 | String deploySoccerManagerContract(String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception; 24 | } 25 | -------------------------------------------------------------------------------- /ethereum-api/src/main/java/com/ivanfranchin/ethereumapi/service/EthereumServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi.service; 2 | 3 | import com.ivanfranchin.ethereumapi.contract.SoccerManager; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Service; 6 | import org.web3j.crypto.CipherException; 7 | import org.web3j.crypto.Credentials; 8 | import org.web3j.crypto.WalletUtils; 9 | import org.web3j.protocol.Web3j; 10 | import org.web3j.protocol.core.DefaultBlockParameterName; 11 | import org.web3j.protocol.core.methods.request.Transaction; 12 | import org.web3j.protocol.core.methods.response.EthAccounts; 13 | import org.web3j.protocol.core.methods.response.EthCoinbase; 14 | import org.web3j.protocol.core.methods.response.EthGetBalance; 15 | import org.web3j.protocol.core.methods.response.EthGetTransactionCount; 16 | import org.web3j.protocol.core.methods.response.EthSendTransaction; 17 | import org.web3j.tx.gas.ContractGasProvider; 18 | import org.web3j.tx.gas.StaticGasProvider; 19 | 20 | import java.io.File; 21 | import java.io.IOException; 22 | import java.math.BigInteger; 23 | import java.util.List; 24 | 25 | @Slf4j 26 | @Service 27 | public class EthereumServiceImpl implements EthereumService { 28 | 29 | private final File walletDirectory; 30 | private final Web3j web3j; 31 | 32 | public EthereumServiceImpl(Web3j web3j) { 33 | this.web3j = web3j; 34 | walletDirectory = new File(System.getProperty("user.dir")); 35 | log.info("Wallet Directory: {}", walletDirectory); 36 | } 37 | 38 | @Override 39 | public List getAccounts() throws IOException { 40 | EthAccounts ethAccounts = web3j.ethAccounts().send(); 41 | return ethAccounts.getAccounts(); 42 | } 43 | 44 | @Override 45 | public String createWallet(String password) throws Exception { 46 | String walletFile = WalletUtils.generateLightNewWalletFile(password, walletDirectory); 47 | return String.format("%s%s%s", walletDirectory.getAbsolutePath(), File.separator, walletFile); 48 | } 49 | 50 | @Override 51 | public Credentials getWallet(String password, String file) throws IOException, CipherException { 52 | return WalletUtils.loadCredentials(password, file); 53 | } 54 | 55 | @Override 56 | public BigInteger getWalletBalance(String address) throws IOException { 57 | EthGetBalance ethGetBalance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send(); 58 | return ethGetBalance.getBalance(); 59 | } 60 | 61 | @Override 62 | public EthSendTransaction transfer(String fromAddress, String toAddress, BigInteger amount, BigInteger gasPrice, BigInteger gasLimit) throws IOException { 63 | EthCoinbase coinbase = web3j.ethCoinbase().send(); 64 | 65 | EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount( 66 | coinbase.getAddress(), 67 | DefaultBlockParameterName.LATEST).send(); 68 | 69 | Transaction transaction = Transaction.createEtherTransaction( 70 | fromAddress, transactionCount.getTransactionCount(), gasPrice, gasLimit, toAddress, amount); 71 | 72 | return web3j.ethSendTransaction(transaction).send(); 73 | } 74 | 75 | @Override 76 | public String deploySoccerManagerContract(String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception { 77 | Credentials credentials = WalletUtils.loadCredentials(password, file); 78 | 79 | ContractGasProvider gasContractProvider = new StaticGasProvider(gasPrice, gasLimit); 80 | SoccerManager soccerManager = SoccerManager.deploy(web3j, credentials, gasContractProvider).send(); 81 | 82 | return soccerManager.getContractAddress(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /ethereum-api/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8080 2 | 3 | spring.application.name=ethereum-api 4 | 5 | management.endpoints.web.exposure.include=beans,env,health,info,metrics,mappings 6 | management.endpoint.health.show-details=always 7 | 8 | springdoc.show-actuator=true 9 | springdoc.swagger-ui.groups-order=DESC 10 | springdoc.swagger-ui.disable-swagger-default-url=true 11 | -------------------------------------------------------------------------------- /ethereum-api/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | _ _ _ 2 | ___| |_| |__ ___ _ __ ___ _ _ _ __ ___ __ _ _ __ (_) 3 | / _ \ __| '_ \ / _ \ '__/ _ \ | | | '_ ` _ \ _____ / _` | '_ \| | 4 | | __/ |_| | | | __/ | | __/ |_| | | | | | |_____| (_| | |_) | | 5 | \___|\__|_| |_|\___|_| \___|\__,_|_| |_| |_| \__,_| .__/|_| 6 | |_| 7 | :: Spring Boot :: ${spring-boot.formatted-version} 8 | -------------------------------------------------------------------------------- /ethereum-api/src/test/java/com/ivanfranchin/ethereumapi/EthereumApiApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.ethereumapi; 2 | 3 | import org.junit.jupiter.api.Disabled; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @Disabled 8 | @SpringBootTest 9 | class EthereumApiApplicationTests { 10 | 11 | @Test 12 | public void contextLoads() { 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /player-api/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivangfr/ethereum-springboot-react/5a88e42e28c5056439672a8493120bfbe967bfb0/player-api/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /player-api/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 3 | -------------------------------------------------------------------------------- /player-api/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.2.0 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | # e.g. to debug Maven itself, use 32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | # ---------------------------------------------------------------------------- 35 | 36 | if [ -z "$MAVEN_SKIP_RC" ] ; then 37 | 38 | if [ -f /usr/local/etc/mavenrc ] ; then 39 | . /usr/local/etc/mavenrc 40 | fi 41 | 42 | if [ -f /etc/mavenrc ] ; then 43 | . /etc/mavenrc 44 | fi 45 | 46 | if [ -f "$HOME/.mavenrc" ] ; then 47 | . "$HOME/.mavenrc" 48 | fi 49 | 50 | fi 51 | 52 | # OS specific support. $var _must_ be set to either true or false. 53 | cygwin=false; 54 | darwin=false; 55 | mingw=false 56 | case "$(uname)" in 57 | CYGWIN*) cygwin=true ;; 58 | MINGW*) mingw=true;; 59 | Darwin*) darwin=true 60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 62 | if [ -z "$JAVA_HOME" ]; then 63 | if [ -x "/usr/libexec/java_home" ]; then 64 | JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME 65 | else 66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME 67 | fi 68 | fi 69 | ;; 70 | esac 71 | 72 | if [ -z "$JAVA_HOME" ] ; then 73 | if [ -r /etc/gentoo-release ] ; then 74 | JAVA_HOME=$(java-config --jre-home) 75 | fi 76 | fi 77 | 78 | # For Cygwin, ensure paths are in UNIX format before anything is touched 79 | if $cygwin ; then 80 | [ -n "$JAVA_HOME" ] && 81 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME") 82 | [ -n "$CLASSPATH" ] && 83 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH") 84 | fi 85 | 86 | # For Mingw, ensure paths are in UNIX format before anything is touched 87 | if $mingw ; then 88 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && 89 | JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" 90 | fi 91 | 92 | if [ -z "$JAVA_HOME" ]; then 93 | javaExecutable="$(which javac)" 94 | if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then 95 | # readlink(1) is not available as standard on Solaris 10. 96 | readLink=$(which readlink) 97 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then 98 | if $darwin ; then 99 | javaHome="$(dirname "\"$javaExecutable\"")" 100 | javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" 101 | else 102 | javaExecutable="$(readlink -f "\"$javaExecutable\"")" 103 | fi 104 | javaHome="$(dirname "\"$javaExecutable\"")" 105 | javaHome=$(expr "$javaHome" : '\(.*\)/bin') 106 | JAVA_HOME="$javaHome" 107 | export JAVA_HOME 108 | fi 109 | fi 110 | fi 111 | 112 | if [ -z "$JAVACMD" ] ; then 113 | if [ -n "$JAVA_HOME" ] ; then 114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 115 | # IBM's JDK on AIX uses strange locations for the executables 116 | JAVACMD="$JAVA_HOME/jre/sh/java" 117 | else 118 | JAVACMD="$JAVA_HOME/bin/java" 119 | fi 120 | else 121 | JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" 122 | fi 123 | fi 124 | 125 | if [ ! -x "$JAVACMD" ] ; then 126 | echo "Error: JAVA_HOME is not defined correctly." >&2 127 | echo " We cannot execute $JAVACMD" >&2 128 | exit 1 129 | fi 130 | 131 | if [ -z "$JAVA_HOME" ] ; then 132 | echo "Warning: JAVA_HOME environment variable is not set." 133 | fi 134 | 135 | # traverses directory structure from process work directory to filesystem root 136 | # first directory with .mvn subdirectory is considered project base directory 137 | find_maven_basedir() { 138 | if [ -z "$1" ] 139 | then 140 | echo "Path not specified to find_maven_basedir" 141 | return 1 142 | fi 143 | 144 | basedir="$1" 145 | wdir="$1" 146 | while [ "$wdir" != '/' ] ; do 147 | if [ -d "$wdir"/.mvn ] ; then 148 | basedir=$wdir 149 | break 150 | fi 151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 152 | if [ -d "${wdir}" ]; then 153 | wdir=$(cd "$wdir/.." || exit 1; pwd) 154 | fi 155 | # end of workaround 156 | done 157 | printf '%s' "$(cd "$basedir" || exit 1; pwd)" 158 | } 159 | 160 | # concatenates all lines of a file 161 | concat_lines() { 162 | if [ -f "$1" ]; then 163 | # Remove \r in case we run on Windows within Git Bash 164 | # and check out the repository with auto CRLF management 165 | # enabled. Otherwise, we may read lines that are delimited with 166 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word 167 | # splitting rules. 168 | tr -s '\r\n' ' ' < "$1" 169 | fi 170 | } 171 | 172 | log() { 173 | if [ "$MVNW_VERBOSE" = true ]; then 174 | printf '%s\n' "$1" 175 | fi 176 | } 177 | 178 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")") 179 | if [ -z "$BASE_DIR" ]; then 180 | exit 1; 181 | fi 182 | 183 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR 184 | log "$MAVEN_PROJECTBASEDIR" 185 | 186 | ########################################################################################## 187 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 188 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 189 | ########################################################################################## 190 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" 191 | if [ -r "$wrapperJarPath" ]; then 192 | log "Found $wrapperJarPath" 193 | else 194 | log "Couldn't find $wrapperJarPath, downloading it ..." 195 | 196 | if [ -n "$MVNW_REPOURL" ]; then 197 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 198 | else 199 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 200 | fi 201 | while IFS="=" read -r key value; do 202 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) 203 | safeValue=$(echo "$value" | tr -d '\r') 204 | case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; 205 | esac 206 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 207 | log "Downloading from: $wrapperUrl" 208 | 209 | if $cygwin; then 210 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") 211 | fi 212 | 213 | if command -v wget > /dev/null; then 214 | log "Found wget ... using wget" 215 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" 216 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 217 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 218 | else 219 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 220 | fi 221 | elif command -v curl > /dev/null; then 222 | log "Found curl ... using curl" 223 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" 224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 226 | else 227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 228 | fi 229 | else 230 | log "Falling back to using Java to download" 231 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" 232 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" 233 | # For Cygwin, switch paths to Windows format before running javac 234 | if $cygwin; then 235 | javaSource=$(cygpath --path --windows "$javaSource") 236 | javaClass=$(cygpath --path --windows "$javaClass") 237 | fi 238 | if [ -e "$javaSource" ]; then 239 | if [ ! -e "$javaClass" ]; then 240 | log " - Compiling MavenWrapperDownloader.java ..." 241 | ("$JAVA_HOME/bin/javac" "$javaSource") 242 | fi 243 | if [ -e "$javaClass" ]; then 244 | log " - Running MavenWrapperDownloader.java ..." 245 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" 246 | fi 247 | fi 248 | fi 249 | fi 250 | ########################################################################################## 251 | # End of extension 252 | ########################################################################################## 253 | 254 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file 255 | wrapperSha256Sum="" 256 | while IFS="=" read -r key value; do 257 | case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; 258 | esac 259 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 260 | if [ -n "$wrapperSha256Sum" ]; then 261 | wrapperSha256Result=false 262 | if command -v sha256sum > /dev/null; then 263 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then 264 | wrapperSha256Result=true 265 | fi 266 | elif command -v shasum > /dev/null; then 267 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then 268 | wrapperSha256Result=true 269 | fi 270 | else 271 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." 272 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." 273 | exit 1 274 | fi 275 | if [ $wrapperSha256Result = false ]; then 276 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 277 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 278 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 279 | exit 1 280 | fi 281 | fi 282 | 283 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 284 | 285 | # For Cygwin, switch paths to Windows format before running java 286 | if $cygwin; then 287 | [ -n "$JAVA_HOME" ] && 288 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") 289 | [ -n "$CLASSPATH" ] && 290 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH") 291 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 292 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") 293 | fi 294 | 295 | # Provide a "standardized" way to retrieve the CLI args that will 296 | # work with both Windows and non-Windows executions. 297 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" 298 | export MAVEN_CMD_LINE_ARGS 299 | 300 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 301 | 302 | # shellcheck disable=SC2086 # safe args 303 | exec "$JAVACMD" \ 304 | $MAVEN_OPTS \ 305 | $MAVEN_DEBUG_OPTS \ 306 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 307 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 308 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 309 | -------------------------------------------------------------------------------- /player-api/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 168 | " exit 1;"^ 169 | "}"^ 170 | "}" 171 | if ERRORLEVEL 1 goto error 172 | ) 173 | 174 | @REM Provide a "standardized" way to retrieve the CLI args that will 175 | @REM work with both Windows and non-Windows executions. 176 | set MAVEN_CMD_LINE_ARGS=%* 177 | 178 | %MAVEN_JAVA_EXE% ^ 179 | %JVM_CONFIG_MAVEN_PROPS% ^ 180 | %MAVEN_OPTS% ^ 181 | %MAVEN_DEBUG_OPTS% ^ 182 | -classpath %WRAPPER_JAR% ^ 183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 185 | if ERRORLEVEL 1 goto error 186 | goto end 187 | 188 | :error 189 | set ERROR_CODE=1 190 | 191 | :end 192 | @endlocal & set ERROR_CODE=%ERROR_CODE% 193 | 194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 198 | :skipRcPost 199 | 200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 202 | 203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 204 | 205 | cmd /C exit /B %ERROR_CODE% 206 | -------------------------------------------------------------------------------- /player-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 3.1.2 9 | 10 | 11 | 12 | com.ivanfranchin 13 | player-api 14 | 0.0.1-SNAPSHOT 15 | player-api 16 | Demo project for Spring Boot 17 | 18 | 19 | 17 20 | 4.5.5 21 | 2.1.0 22 | 23 | 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-actuator 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-web 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-validation 36 | 37 | 38 | 39 | 40 | org.web3j 41 | core 42 | ${web3j.version} 43 | 44 | 45 | 46 | 47 | org.springdoc 48 | springdoc-openapi-starter-webmvc-ui 49 | ${springdoc-openapi.version} 50 | 51 | 52 | 53 | org.projectlombok 54 | lombok 55 | true 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-starter-test 60 | test 61 | 62 | 63 | 64 | 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-maven-plugin 69 | 70 | 71 | 72 | org.projectlombok 73 | lombok 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/PlayerApiApplication.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class PlayerApiApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(PlayerApiApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/config/ErrorAttributesConfig.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.config; 2 | 3 | import org.springframework.boot.web.error.ErrorAttributeOptions; 4 | import org.springframework.boot.web.error.ErrorAttributeOptions.Include; 5 | import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; 6 | import org.springframework.boot.web.servlet.error.ErrorAttributes; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.web.context.request.WebRequest; 10 | 11 | import java.util.Map; 12 | 13 | @Configuration 14 | public class ErrorAttributesConfig { 15 | 16 | @Bean 17 | public ErrorAttributes errorAttributes() { 18 | return new DefaultErrorAttributes() { 19 | @Override 20 | public Map getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { 21 | return super.getErrorAttributes(webRequest, options.including(Include.EXCEPTION, Include.MESSAGE, Include.BINDING_ERRORS)); 22 | } 23 | }; 24 | } 25 | } -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.config; 2 | 3 | import io.swagger.v3.oas.models.Components; 4 | import io.swagger.v3.oas.models.OpenAPI; 5 | import io.swagger.v3.oas.models.info.Info; 6 | import org.springdoc.core.models.GroupedOpenApi; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | 11 | @Configuration 12 | public class SwaggerConfig { 13 | 14 | @Value("${spring.application.name}") 15 | private String applicationName; 16 | 17 | @Bean 18 | public OpenAPI customOpenAPI() { 19 | return new OpenAPI().components(new Components()).info(new Info().title(applicationName)); 20 | } 21 | 22 | @Bean 23 | public GroupedOpenApi customApi() { 24 | return GroupedOpenApi.builder().group("api").pathsToMatch("/api/**").build(); 25 | } 26 | 27 | @Bean 28 | public GroupedOpenApi actuatorApi() { 29 | return GroupedOpenApi.builder().group("actuator").pathsToMatch("/actuator/**").build(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/config/Web3jConfig.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.web3j.protocol.Web3j; 6 | import org.web3j.protocol.http.HttpService; 7 | 8 | @Configuration 9 | public class Web3jConfig { 10 | 11 | @Bean 12 | public Web3j web3j() { 13 | return Web3j.build(new HttpService()); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/contract/SoccerManager.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.contract; 2 | 3 | import io.reactivex.Flowable; 4 | import io.reactivex.functions.Function; 5 | import java.math.BigInteger; 6 | import java.util.ArrayList; 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import java.util.concurrent.Callable; 11 | import org.web3j.abi.EventEncoder; 12 | import org.web3j.abi.TypeReference; 13 | import org.web3j.abi.datatypes.Address; 14 | import org.web3j.abi.datatypes.Bool; 15 | import org.web3j.abi.datatypes.DynamicArray; 16 | import org.web3j.abi.datatypes.Event; 17 | import org.web3j.abi.datatypes.Type; 18 | import org.web3j.abi.datatypes.Utf8String; 19 | import org.web3j.abi.datatypes.generated.Uint256; 20 | import org.web3j.crypto.Credentials; 21 | import org.web3j.protocol.Web3j; 22 | import org.web3j.protocol.core.DefaultBlockParameter; 23 | import org.web3j.protocol.core.RemoteCall; 24 | import org.web3j.protocol.core.RemoteFunctionCall; 25 | import org.web3j.protocol.core.methods.request.EthFilter; 26 | import org.web3j.protocol.core.methods.response.BaseEventResponse; 27 | import org.web3j.protocol.core.methods.response.Log; 28 | import org.web3j.protocol.core.methods.response.TransactionReceipt; 29 | import org.web3j.tuples.generated.Tuple6; 30 | import org.web3j.tx.Contract; 31 | import org.web3j.tx.TransactionManager; 32 | import org.web3j.tx.gas.ContractGasProvider; 33 | 34 | /** 35 | *

Auto generated code. 36 | *

Do not modify! 37 | *

Please use the web3j command line tools, 38 | * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the 39 | * codegen module to update. 40 | * 41 | *

Generated with web3j version 4.5.5. 42 | */ 43 | @SuppressWarnings("rawtypes") 44 | public class SoccerManager extends Contract { 45 | private static final String BINARY = "60806040523480156200001157600080fd5b50600080546001600160a01b03191633179055620000376001600160e01b036200003d16565b62000342565b62000095604051806040016040528060088152602001672ba2ab22a92a27a760c11b815250670de0b6b3a76400006040518060800160405280605981526020016200131e60599139336001600160e01b036200015216565b50620000f16040518060400160405280600b81526020016a46454c495045204d454c4f60a81b815250671bc16d674ec800006040518060800160405280605c8152602001620013d5605c9139336001600160e01b036200015216565b506200014f6040518060400160405280600d81526020016c23aaa9aa20ab279023a7a6a2ad60991b8152506729a2241af62c00006040518060800160405280605e815260200162001377605e9139336001600160e01b036200015216565b50565b6001805481018082556040805160e08101825282815260208082018581528284018a8152606084018a90526080840189905260a084018790526001600160a01b03881660c085015260008681526002808552958120855181559251978301805460ff19169815159890981790975551805193949193620001db939285019291909101906200029d565b506060820151600382015560808201518051620002039160048401916020909101906200029d565b5060a0820151600591909101805460c09093015160ff1990931691151591909117610100600160a81b0319166101006001600160a01b03938416021790556004805460018181019092557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b018390559316600090815260036020908152604082208054958601815582529020909201829055509392505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10620002e057805160ff191683800117855562000310565b8280016001018555821562000310579182015b8281111562000310578251825591602001919060010190620002f3565b506200031e92915062000322565b5090565b6200033f91905b808211156200031e576000815560010162000329565b90565b610fcc80620003526000396000f3fe6080604052600436106100555760003560e01c80637de81fca1461005a578063aab48c521461008b578063e55ae4e8146101df578063eca2f3c81461030f578063fc94f54014610374578063fd6673f5146103a6575b600080fd5b6100776004803603602081101561007057600080fd5b50356103bb565b604080519115158252519081900360200190f35b34801561009757600080fd5b506101cd600480360360608110156100ae57600080fd5b8101906020810181356401000000008111156100c957600080fd5b8201836020820111156100db57600080fd5b803590602001918460018302840111640100000000831117156100fd57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295843595909490935060408101925060200135905064010000000081111561015857600080fd5b82018360208201111561016a57600080fd5b8035906020019184600183028401116401000000008311171561018c57600080fd5b91908080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509295506106c8945050505050565b60408051918252519081900360200190f35b3480156101eb57600080fd5b506102096004803603602081101561020257600080fd5b503561088b565b6040805187815290810185905282151560808201526001600160a01b03821660a082015260c060208083018281528851928401929092528751606084019160e0850191908a019080838360005b8381101561026e578181015183820152602001610256565b50505050905090810190601f16801561029b5780820380516001836020036101000a031916815260200191505b50838103825286518152865160209182019188019080838360005b838110156102ce5781810151838201526020016102b6565b50505050905090810190601f1680156102fb5780820380516001836020036101000a031916815260200191505b509850505050505050505060405180910390f35b34801561031b57600080fd5b50610324610a54565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610360578181015183820152602001610348565b505050509050019250505060405180910390f35b34801561038057600080fd5b506100776004803603604081101561039757600080fd5b50803590602001351515610ab6565b3480156103b257600080fd5b506101cd610c34565b60008181526002602052604081206001015460ff16610418576040805162461bcd60e51b8152602060048201526014602482015273141b185e595c88191bd95cdb89dd08195e1a5cdd60621b604482015290519081900360640190fd5b60008281526002602052604090206005015460ff16610477576040805162461bcd60e51b8152602060048201526016602482015275506c61796572206973206e6f7420666f722073616c6560501b604482015290519081900360640190fd5b60008281526002602052604090206005015461010090046001600160a01b03163314156104eb576040805162461bcd60e51b815260206004820181905260248201527f596f752061726520616c72656164792074686520706c61796572206167656e74604482015290519081900360640190fd5b6000828152600260205260409020600301543414610550576040805162461bcd60e51b815260206004820152601860248201527f416d6f756e742073656e7420697320696e636f72726563740000000000000000604482015290519081900360640190fd5b600082815260026020526040902060030154333110156105a15760405162461bcd60e51b815260040180806020018281038252602c815260200180610ef3602c913960400191505060405180910390fd5b6000828152600260209081526040808320600501805433610100908102610100600160a81b031983161792839055918290046001600160a01b03908116865260038552838620805460018101825590875294862090940187905504919091168083529120610615908463ffffffff610c3a16565b506000838152600260205260408082206003015490516001600160a01b0384169282156108fc02929190818181858888f1935050505015801561065c573d6000803e3d6000fd5b506000838152600260209081526040918290206003015482518681526001600160a01b038516928101929092523382840152606082015290517f87e04f5f6a6261cdd2f775831d9ffe91bb9cacb100d78a01bba93e0be84c18069181900360800190a150600192915050565b600080546001600160a01b03163314610728576040805162461bcd60e51b815260206004820152601e60248201527f596f7520617265206e6f742074686520636f6e7472616374206f776e65720000604482015290519081900360640190fd5b60008451116107685760405162461bcd60e51b8152600401808060200182810382526023815260200180610ed06023913960400191505060405180910390fd5b600083116107bd576040805162461bcd60e51b815260206004820181905260248201527f54686520617267756d656e742027707269636527206d757374206265203e2030604482015290519081900360640190fd5b60006107cb85858533610cc7565b90507f0c67b919d648c2b4bd260225fe9b485cc70a7e619047dcf5bb598ae38a0b72888186866001604051808581526020018060200184815260200183151515158152602001828103825285818151815260200191508051906020019080838360005b8381101561084657818101518382015260200161082e565b50505050905090810190601f1680156108735780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1949350505050565b600081815260026020526040812060010154606090829082908290819060ff166108e65760405162461bcd60e51b8152600401808060200182810382526029815260200180610f6f6029913960400191505060405180910390fd5b60008781526002602081815260409283902080546003820154600583015483860180548851610100600183161581026000190190921698909804601f81018890048802890188019099528888529497939690959294600489019460ff841694919093046001600160a01b03169287918301828280156109a65780601f1061097b576101008083540402835291602001916109a6565b820191906000526020600020905b81548152906001019060200180831161098957829003601f168201915b5050865460408051602060026001851615610100026000190190941693909304601f8101849004840282018401909252818152959a5088945092508401905082828015610a345780601f10610a0957610100808354040283529160200191610a34565b820191906000526020600020905b815481529060010190602001808311610a1757829003601f168201915b505050505092508090509650965096509650965096505091939550919395565b33600090815260036020908152604091829020805483518184028101840190945280845260609392830182828015610aab57602002820191906000526020600020905b815481526020019060010190808311610a97575b505050505090505b90565b60008281526002602052604081206001015460ff16610b065760405162461bcd60e51b8152600401808060200182810382526029815260200180610f6f6029913960400191505060405180910390fd5b60008381526002602052604090206005015460ff1615158215151415610b5d5760405162461bcd60e51b815260040180806020018281038252602d815260200180610f42602d913960400191505060405180910390fd5b60008381526002602052604090206005015461010090046001600160a01b03163314610bba5760405162461bcd60e51b8152600401808060200182810382526023815260200180610f1f6023913960400191505060405180910390fd5b600083815260026020908152604091829020600501805485151560ff1982168117909255835187815260ff9091168015159382019390935280840191909152915190917f47116610d83e5898152286936a924c1198dab9b12f87e2eafc3bc39a14570f98919081900360600190a160019150505b92915050565b60045490565b6000805b8354811015610cbd5782848281548110610c5457fe5b90600052602060002001541415610cb557835484906000198101908110610c7757fe5b9060005260206000200154848281548110610c8e57fe5b6000918252602090912001558354610caa856000198301610e0e565b506001915050610c2e565b600101610c3e565b5060009392505050565b6001805481018082556040805160e08101825282815260208082018581528284018a8152606084018a90526080840189905260a084018790526001600160a01b03881660c085015260008681526002808552958120855181559251978301805460ff19169815159890981790975551805193949193610d4e93928501929190910190610e37565b506060820151600382015560808201518051610d74916004840191602090910190610e37565b5060a0820151600591909101805460c09093015160ff1990931691151591909117610100600160a81b0319166101006001600160a01b03938416021790556004805460018181019092557f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b018390559316600090815260036020908152604082208054958601815582529020909201829055509392505050565b815481835581811115610e3257600083815260209020610e32918101908301610eb5565b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610e7857805160ff1916838001178555610ea5565b82800160010185558215610ea5579182015b82811115610ea5578251825591602001919060010190610e8a565b50610eb1929150610eb5565b5090565b610ab391905b80821115610eb15760008155600101610ebb56fe54686520617267756d656e7420276e616d65272063616e6e6f7420626520656d707479596f7520646f6e2774206861766520656e6f75676874206574686572206f6e20796f75722062616c616e6365596f7520617265206e6f7420746865206167656e74206f662074686520706c61796572506c6179657220666f7253616c6520737461747573206973207468652073616d6520616c726561647920736574506c6179657220776974682074686520696e666f726d656420696420646f65736e2774206578697374a265627a7a72315820e2f95131317593473c441de172cdbd408c050bd5a70408ce2f447fd90352502f64736f6c6343000511003268747470733a2f2f73332e616d617a6f6e6177732e636f6d2f7365702d6275636b65742d70726f642f77702d636f6e74656e742f75706c6f6164732f323031392f30352f30323038323733312f7765766572746f6e2e706e6768747470733a2f2f73332e616d617a6f6e6177732e636f6d2f7365702d6275636b65742d70726f642f77702d636f6e74656e742f75706c6f6164732f323032302f30312f30323038303135332f6775737461766f2d676f6d657a2e706e6768747470733a2f2f73332e616d617a6f6e6177732e636f6d2f7365702d6275636b65742d70726f642f77702d636f6e74656e742f75706c6f6164732f323031392f30382f30323038303034392f66656c6970652d6d656c6f2e706e67"; 46 | 47 | public static final String FUNC_ADDPLAYER = "addPlayer"; 48 | 49 | public static final String FUNC_BUYPLAYER = "buyPlayer"; 50 | 51 | public static final String FUNC_GETNUMBEROFPLAYERS = "getNumberOfPlayers"; 52 | 53 | public static final String FUNC_GETPLAYER = "getPlayer"; 54 | 55 | public static final String FUNC_GETPLAYERSOFAGENT = "getPlayersOfAgent"; 56 | 57 | public static final String FUNC_UPDATEPLAYER = "updatePlayer"; 58 | 59 | public static final Event PLAYERADDED_EVENT = new Event("PlayerAdded", 60 | Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference() {})); 61 | ; 62 | 63 | public static final Event PLAYERBOUGHT_EVENT = new Event("PlayerBought", 64 | Arrays.>asList(new TypeReference() {}, new TypeReference

() {}, new TypeReference
() {}, new TypeReference() {})); 65 | ; 66 | 67 | public static final Event PLAYERUPDATED_EVENT = new Event("PlayerUpdated", 68 | Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference() {})); 69 | ; 70 | 71 | @Deprecated 72 | protected SoccerManager(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { 73 | super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); 74 | } 75 | 76 | protected SoccerManager(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { 77 | super(BINARY, contractAddress, web3j, credentials, contractGasProvider); 78 | } 79 | 80 | @Deprecated 81 | protected SoccerManager(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { 82 | super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); 83 | } 84 | 85 | protected SoccerManager(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { 86 | super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); 87 | } 88 | 89 | public List getPlayerAddedEvents(TransactionReceipt transactionReceipt) { 90 | List valueList = extractEventParametersWithLog(PLAYERADDED_EVENT, transactionReceipt); 91 | ArrayList responses = new ArrayList(valueList.size()); 92 | for (Contract.EventValuesWithLog eventValues : valueList) { 93 | PlayerAddedEventResponse typedResponse = new PlayerAddedEventResponse(); 94 | typedResponse.log = eventValues.getLog(); 95 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 96 | typedResponse.name = (String) eventValues.getNonIndexedValues().get(1).getValue(); 97 | typedResponse.price = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); 98 | typedResponse.forSale = (Boolean) eventValues.getNonIndexedValues().get(3).getValue(); 99 | responses.add(typedResponse); 100 | } 101 | return responses; 102 | } 103 | 104 | public Flowable playerAddedEventFlowable(EthFilter filter) { 105 | return web3j.ethLogFlowable(filter).map(new Function() { 106 | @Override 107 | public PlayerAddedEventResponse apply(Log log) { 108 | Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(PLAYERADDED_EVENT, log); 109 | PlayerAddedEventResponse typedResponse = new PlayerAddedEventResponse(); 110 | typedResponse.log = log; 111 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 112 | typedResponse.name = (String) eventValues.getNonIndexedValues().get(1).getValue(); 113 | typedResponse.price = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); 114 | typedResponse.forSale = (Boolean) eventValues.getNonIndexedValues().get(3).getValue(); 115 | return typedResponse; 116 | } 117 | }); 118 | } 119 | 120 | public Flowable playerAddedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { 121 | EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); 122 | filter.addSingleTopic(EventEncoder.encode(PLAYERADDED_EVENT)); 123 | return playerAddedEventFlowable(filter); 124 | } 125 | 126 | public List getPlayerBoughtEvents(TransactionReceipt transactionReceipt) { 127 | List valueList = extractEventParametersWithLog(PLAYERBOUGHT_EVENT, transactionReceipt); 128 | ArrayList responses = new ArrayList(valueList.size()); 129 | for (Contract.EventValuesWithLog eventValues : valueList) { 130 | PlayerBoughtEventResponse typedResponse = new PlayerBoughtEventResponse(); 131 | typedResponse.log = eventValues.getLog(); 132 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 133 | typedResponse.oldAgent = (String) eventValues.getNonIndexedValues().get(1).getValue(); 134 | typedResponse.newAgent = (String) eventValues.getNonIndexedValues().get(2).getValue(); 135 | typedResponse.price = (BigInteger) eventValues.getNonIndexedValues().get(3).getValue(); 136 | responses.add(typedResponse); 137 | } 138 | return responses; 139 | } 140 | 141 | public Flowable playerBoughtEventFlowable(EthFilter filter) { 142 | return web3j.ethLogFlowable(filter).map(new Function() { 143 | @Override 144 | public PlayerBoughtEventResponse apply(Log log) { 145 | Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(PLAYERBOUGHT_EVENT, log); 146 | PlayerBoughtEventResponse typedResponse = new PlayerBoughtEventResponse(); 147 | typedResponse.log = log; 148 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 149 | typedResponse.oldAgent = (String) eventValues.getNonIndexedValues().get(1).getValue(); 150 | typedResponse.newAgent = (String) eventValues.getNonIndexedValues().get(2).getValue(); 151 | typedResponse.price = (BigInteger) eventValues.getNonIndexedValues().get(3).getValue(); 152 | return typedResponse; 153 | } 154 | }); 155 | } 156 | 157 | public Flowable playerBoughtEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { 158 | EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); 159 | filter.addSingleTopic(EventEncoder.encode(PLAYERBOUGHT_EVENT)); 160 | return playerBoughtEventFlowable(filter); 161 | } 162 | 163 | public List getPlayerUpdatedEvents(TransactionReceipt transactionReceipt) { 164 | List valueList = extractEventParametersWithLog(PLAYERUPDATED_EVENT, transactionReceipt); 165 | ArrayList responses = new ArrayList(valueList.size()); 166 | for (Contract.EventValuesWithLog eventValues : valueList) { 167 | PlayerUpdatedEventResponse typedResponse = new PlayerUpdatedEventResponse(); 168 | typedResponse.log = eventValues.getLog(); 169 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 170 | typedResponse.oldForSaleValue = (Boolean) eventValues.getNonIndexedValues().get(1).getValue(); 171 | typedResponse.newForSaleValue = (Boolean) eventValues.getNonIndexedValues().get(2).getValue(); 172 | responses.add(typedResponse); 173 | } 174 | return responses; 175 | } 176 | 177 | public Flowable playerUpdatedEventFlowable(EthFilter filter) { 178 | return web3j.ethLogFlowable(filter).map(new Function() { 179 | @Override 180 | public PlayerUpdatedEventResponse apply(Log log) { 181 | Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(PLAYERUPDATED_EVENT, log); 182 | PlayerUpdatedEventResponse typedResponse = new PlayerUpdatedEventResponse(); 183 | typedResponse.log = log; 184 | typedResponse.playerId = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); 185 | typedResponse.oldForSaleValue = (Boolean) eventValues.getNonIndexedValues().get(1).getValue(); 186 | typedResponse.newForSaleValue = (Boolean) eventValues.getNonIndexedValues().get(2).getValue(); 187 | return typedResponse; 188 | } 189 | }); 190 | } 191 | 192 | public Flowable playerUpdatedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { 193 | EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); 194 | filter.addSingleTopic(EventEncoder.encode(PLAYERUPDATED_EVENT)); 195 | return playerUpdatedEventFlowable(filter); 196 | } 197 | 198 | public RemoteFunctionCall addPlayer(String name, BigInteger price, String image) { 199 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( 200 | FUNC_ADDPLAYER, 201 | Arrays.asList(new org.web3j.abi.datatypes.Utf8String(name), 202 | new org.web3j.abi.datatypes.generated.Uint256(price), 203 | new org.web3j.abi.datatypes.Utf8String(image)), 204 | Collections.>emptyList()); 205 | return executeRemoteCallTransaction(function); 206 | } 207 | 208 | public RemoteFunctionCall buyPlayer(BigInteger id, BigInteger weiValue) { 209 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( 210 | FUNC_BUYPLAYER, 211 | Arrays.asList(new org.web3j.abi.datatypes.generated.Uint256(id)), 212 | Collections.>emptyList()); 213 | return executeRemoteCallTransaction(function, weiValue); 214 | } 215 | 216 | public RemoteFunctionCall getNumberOfPlayers() { 217 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETNUMBEROFPLAYERS, 218 | Arrays.asList(), 219 | Arrays.>asList(new TypeReference() {})); 220 | return executeRemoteCallSingleValueReturn(function, BigInteger.class); 221 | } 222 | 223 | public RemoteFunctionCall> getPlayer(BigInteger id) { 224 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETPLAYER, 225 | Arrays.asList(new org.web3j.abi.datatypes.generated.Uint256(id)), 226 | Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference
() {})); 227 | return new RemoteFunctionCall>(function, 228 | new Callable>() { 229 | @Override 230 | public Tuple6 call() throws Exception { 231 | List results = executeCallMultipleValueReturn(function); 232 | return new Tuple6( 233 | (BigInteger) results.get(0).getValue(), 234 | (String) results.get(1).getValue(), 235 | (BigInteger) results.get(2).getValue(), 236 | (String) results.get(3).getValue(), 237 | (Boolean) results.get(4).getValue(), 238 | (String) results.get(5).getValue()); 239 | } 240 | }); 241 | } 242 | 243 | public RemoteFunctionCall getPlayersOfAgent() { 244 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETPLAYERSOFAGENT, 245 | Arrays.asList(), 246 | Arrays.>asList(new TypeReference>() {})); 247 | return new RemoteFunctionCall(function, 248 | new Callable() { 249 | @Override 250 | @SuppressWarnings("unchecked") 251 | public List call() throws Exception { 252 | List result = (List) executeCallSingleValueReturn(function, List.class); 253 | return convertToNative(result); 254 | } 255 | }); 256 | } 257 | 258 | public RemoteFunctionCall updatePlayer(BigInteger id, Boolean forSale) { 259 | final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( 260 | FUNC_UPDATEPLAYER, 261 | Arrays.asList(new org.web3j.abi.datatypes.generated.Uint256(id), 262 | new org.web3j.abi.datatypes.Bool(forSale)), 263 | Collections.>emptyList()); 264 | return executeRemoteCallTransaction(function); 265 | } 266 | 267 | @Deprecated 268 | public static SoccerManager load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { 269 | return new SoccerManager(contractAddress, web3j, credentials, gasPrice, gasLimit); 270 | } 271 | 272 | @Deprecated 273 | public static SoccerManager load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { 274 | return new SoccerManager(contractAddress, web3j, transactionManager, gasPrice, gasLimit); 275 | } 276 | 277 | public static SoccerManager load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { 278 | return new SoccerManager(contractAddress, web3j, credentials, contractGasProvider); 279 | } 280 | 281 | public static SoccerManager load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { 282 | return new SoccerManager(contractAddress, web3j, transactionManager, contractGasProvider); 283 | } 284 | 285 | public static RemoteCall deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { 286 | return deployRemoteCall(SoccerManager.class, web3j, credentials, contractGasProvider, BINARY, ""); 287 | } 288 | 289 | public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { 290 | return deployRemoteCall(SoccerManager.class, web3j, transactionManager, contractGasProvider, BINARY, ""); 291 | } 292 | 293 | @Deprecated 294 | public static RemoteCall deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { 295 | return deployRemoteCall(SoccerManager.class, web3j, credentials, gasPrice, gasLimit, BINARY, ""); 296 | } 297 | 298 | @Deprecated 299 | public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { 300 | return deployRemoteCall(SoccerManager.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, ""); 301 | } 302 | 303 | public static class PlayerAddedEventResponse extends BaseEventResponse { 304 | public BigInteger playerId; 305 | 306 | public String name; 307 | 308 | public BigInteger price; 309 | 310 | public Boolean forSale; 311 | } 312 | 313 | public static class PlayerBoughtEventResponse extends BaseEventResponse { 314 | public BigInteger playerId; 315 | 316 | public String oldAgent; 317 | 318 | public String newAgent; 319 | 320 | public BigInteger price; 321 | } 322 | 323 | public static class PlayerUpdatedEventResponse extends BaseEventResponse { 324 | public BigInteger playerId; 325 | 326 | public Boolean oldForSaleValue; 327 | 328 | public Boolean newForSaleValue; 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/exception/ContractAddressNotInformedException.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.exception; 2 | 3 | public class ContractAddressNotInformedException extends RuntimeException { 4 | 5 | public ContractAddressNotInformedException() { 6 | super("'ethereum.contract.soccermanager.address' not informed"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/rest/PlayerApiController.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.rest; 2 | 3 | import com.ivanfranchin.playerapi.rest.dto.AddPlayerDto; 4 | import com.ivanfranchin.playerapi.rest.dto.BasePlayerDto; 5 | import com.ivanfranchin.playerapi.rest.dto.BuyPlayerDto; 6 | import com.ivanfranchin.playerapi.rest.dto.GetPlayerDto; 7 | import com.ivanfranchin.playerapi.rest.dto.PlayerDto; 8 | import com.ivanfranchin.playerapi.rest.dto.UpdatePlayerDto; 9 | import com.ivanfranchin.playerapi.service.SoccerManagerService; 10 | import jakarta.validation.Valid; 11 | import lombok.RequiredArgsConstructor; 12 | import org.springframework.http.HttpStatus; 13 | import org.springframework.web.bind.annotation.PostMapping; 14 | import org.springframework.web.bind.annotation.RequestBody; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.ResponseStatus; 17 | import org.springframework.web.bind.annotation.RestController; 18 | import org.web3j.protocol.core.methods.response.TransactionReceipt; 19 | import org.web3j.tuples.generated.Tuple6; 20 | 21 | import java.math.BigInteger; 22 | import java.util.List; 23 | 24 | @RequiredArgsConstructor 25 | @RestController 26 | @RequestMapping("/api") 27 | public class PlayerApiController { 28 | 29 | private final SoccerManagerService soccerManagerService; 30 | 31 | @ResponseStatus(HttpStatus.CREATED) 32 | @PostMapping("/players/add") 33 | public TransactionReceipt addPlayer(@Valid @RequestBody AddPlayerDto addPlayerDto) throws Exception { 34 | return soccerManagerService.addPlayer( 35 | addPlayerDto.getPlayerName(), 36 | addPlayerDto.getPlayerPrice(), 37 | addPlayerDto.getImage(), 38 | addPlayerDto.getPassword(), 39 | addPlayerDto.getFile(), 40 | addPlayerDto.getGasPrice(), 41 | addPlayerDto.getGasLimit()); 42 | } 43 | 44 | @PostMapping("/players/get") 45 | public PlayerDto getPlayer(@Valid @RequestBody GetPlayerDto getPlayerDto) throws Exception { 46 | return mapToPlayerDto(soccerManagerService.getPlayer( 47 | getPlayerDto.getPlayerId(), 48 | getPlayerDto.getPassword(), 49 | getPlayerDto.getFile(), 50 | getPlayerDto.getGasPrice(), 51 | getPlayerDto.getGasLimit()) 52 | ); 53 | } 54 | 55 | private PlayerDto mapToPlayerDto(Tuple6 tuple6) { 56 | return new PlayerDto(tuple6.component1(), tuple6.component2(), tuple6.component3(), 57 | tuple6.component4(), tuple6.component5(), tuple6.component6()); 58 | } 59 | 60 | @PostMapping("/players/buy") 61 | public TransactionReceipt buyPlayer(@Valid @RequestBody BuyPlayerDto buyPlayerDto) throws Exception { 62 | return soccerManagerService.buyPlayer( 63 | buyPlayerDto.getPlayerId(), 64 | buyPlayerDto.getWeiValue(), 65 | buyPlayerDto.getPassword(), 66 | buyPlayerDto.getFile(), 67 | buyPlayerDto.getGasPrice(), 68 | buyPlayerDto.getGasLimit()); 69 | } 70 | 71 | @PostMapping("/players/update") 72 | public TransactionReceipt updatePlayer(@Valid @RequestBody UpdatePlayerDto updatePlayerDto) throws Exception { 73 | return soccerManagerService.updatePlayer( 74 | updatePlayerDto.getPlayerId(), 75 | updatePlayerDto.getForSale(), 76 | updatePlayerDto.getPassword(), 77 | updatePlayerDto.getFile(), 78 | updatePlayerDto.getGasPrice(), 79 | updatePlayerDto.getGasLimit()); 80 | } 81 | 82 | @PostMapping("/agents/players") 83 | public List getAgentPlayers(@Valid @RequestBody BasePlayerDto basePlayerDto) throws Exception { 84 | return soccerManagerService.getPlayersOfAgent( 85 | basePlayerDto.getPassword(), 86 | basePlayerDto.getFile(), 87 | basePlayerDto.getGasPrice(), 88 | basePlayerDto.getGasLimit()); 89 | } 90 | 91 | @PostMapping("/players/numberOf") 92 | public BigInteger getNumberOfPlayers(@Valid @RequestBody BasePlayerDto basePlayerDto) throws Exception { 93 | return soccerManagerService.getNumberOfPlayers( 94 | basePlayerDto.getPassword(), 95 | basePlayerDto.getFile(), 96 | basePlayerDto.getGasPrice(), 97 | basePlayerDto.getGasLimit()); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/rest/dto/AddPlayerDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.rest.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.NotNull; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | import java.math.BigInteger; 10 | 11 | @Data 12 | @EqualsAndHashCode(callSuper = true) 13 | public class AddPlayerDto extends BasePlayerDto { 14 | 15 | @Schema(example = "Ivan") 16 | @NotBlank 17 | private String playerName; 18 | 19 | @Schema(example = "1000000000000000000") 20 | @NotNull 21 | private BigInteger playerPrice; 22 | 23 | @Schema(example = "http://...") 24 | @NotBlank 25 | private String image; 26 | } 27 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/rest/dto/BasePlayerDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.rest.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotBlank; 5 | import jakarta.validation.constraints.NotNull; 6 | import jakarta.validation.constraints.Positive; 7 | import lombok.Data; 8 | 9 | import java.math.BigInteger; 10 | 11 | @Data 12 | public class BasePlayerDto { 13 | 14 | @Schema(example = "123") 15 | @NotBlank 16 | private String password; 17 | 18 | @Schema(example = "/path/to/UTC...") 19 | @NotBlank 20 | private String file; 21 | 22 | @Schema(example = "1") 23 | @NotNull 24 | @Positive 25 | private BigInteger gasPrice; 26 | 27 | @Schema(example = "3000000") 28 | @NotNull 29 | @Positive 30 | private BigInteger gasLimit; 31 | } 32 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/rest/dto/BuyPlayerDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.rest.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotNull; 5 | import jakarta.validation.constraints.Positive; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | import java.math.BigInteger; 10 | 11 | @Data 12 | @EqualsAndHashCode(callSuper = true) 13 | public class BuyPlayerDto extends BasePlayerDto { 14 | 15 | @Schema(example = "1") 16 | @NotNull 17 | @Positive 18 | private BigInteger playerId; 19 | 20 | @Schema(example = "1000000000000000000") 21 | @NotNull 22 | @Positive 23 | private BigInteger weiValue; 24 | } 25 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/rest/dto/GetPlayerDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.rest.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotNull; 5 | import jakarta.validation.constraints.Positive; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | import java.math.BigInteger; 10 | 11 | @Data 12 | @EqualsAndHashCode(callSuper = true) 13 | public class GetPlayerDto extends BasePlayerDto { 14 | 15 | @Schema(example = "1") 16 | @NotNull 17 | @Positive 18 | private BigInteger playerId; 19 | } 20 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/rest/dto/PlayerDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.rest.dto; 2 | 3 | import java.math.BigInteger; 4 | 5 | public record PlayerDto(BigInteger id, String name, BigInteger price, String image, Boolean forSale, String agent) { 6 | } 7 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/rest/dto/UpdatePlayerDto.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.rest.dto; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import jakarta.validation.constraints.NotNull; 5 | import jakarta.validation.constraints.Positive; 6 | import lombok.Data; 7 | import lombok.EqualsAndHashCode; 8 | 9 | import java.math.BigInteger; 10 | 11 | @Data 12 | @EqualsAndHashCode(callSuper = true) 13 | public class UpdatePlayerDto extends BasePlayerDto { 14 | 15 | @Schema(example = "1") 16 | @NotNull 17 | @Positive 18 | private BigInteger playerId; 19 | 20 | @Schema(example = "true") 21 | @NotNull 22 | private Boolean forSale; 23 | } 24 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/service/SoccerManagerService.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.service; 2 | 3 | import org.web3j.protocol.core.methods.response.TransactionReceipt; 4 | import org.web3j.tuples.generated.Tuple6; 5 | 6 | import java.math.BigInteger; 7 | import java.util.List; 8 | 9 | public interface SoccerManagerService { 10 | 11 | Tuple6 getPlayer(BigInteger playerId, String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception; 12 | 13 | TransactionReceipt addPlayer(String name, BigInteger price, String playerImageUrl, String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception; 14 | 15 | TransactionReceipt buyPlayer(BigInteger playerId, BigInteger weiValue, String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception; 16 | 17 | TransactionReceipt updatePlayer(BigInteger playerId, Boolean forSale, String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception; 18 | 19 | List getPlayersOfAgent(String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception; 20 | 21 | BigInteger getNumberOfPlayers(String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception; 22 | } 23 | -------------------------------------------------------------------------------- /player-api/src/main/java/com/ivanfranchin/playerapi/service/SoccerManagerServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi.service; 2 | 3 | import com.ivanfranchin.playerapi.contract.SoccerManager; 4 | import com.ivanfranchin.playerapi.exception.ContractAddressNotInformedException; 5 | import lombok.RequiredArgsConstructor; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.stereotype.Service; 9 | import org.web3j.crypto.CipherException; 10 | import org.web3j.crypto.Credentials; 11 | import org.web3j.crypto.WalletUtils; 12 | import org.web3j.protocol.Web3j; 13 | import org.web3j.protocol.core.methods.response.TransactionReceipt; 14 | import org.web3j.tuples.generated.Tuple6; 15 | import org.web3j.tx.gas.StaticGasProvider; 16 | 17 | import java.io.IOException; 18 | import java.math.BigInteger; 19 | import java.util.List; 20 | 21 | @Slf4j 22 | @RequiredArgsConstructor 23 | @Service 24 | public class SoccerManagerServiceImpl implements SoccerManagerService { 25 | 26 | private final Web3j web3j; 27 | 28 | private String contractAddress; 29 | 30 | @Value("${ethereum.contract.soccermanager.address}") 31 | public void setContractAddress(String contractAddress) { 32 | if (contractAddress.isEmpty()) { 33 | throw new ContractAddressNotInformedException(); 34 | } 35 | this.contractAddress = contractAddress; 36 | } 37 | 38 | @Override 39 | public Tuple6 getPlayer(BigInteger playerId, String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception { 40 | SoccerManager soccerManager = loadSoccerManager(password, file, gasPrice, gasLimit); 41 | return soccerManager.getPlayer(playerId).send(); 42 | } 43 | 44 | @Override 45 | public TransactionReceipt addPlayer(String playerName, BigInteger playerPrice, String playerImageUrl, String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception { 46 | SoccerManager soccerManager = loadSoccerManager(password, file, gasPrice, gasLimit); 47 | return soccerManager.addPlayer(playerName, playerPrice, playerImageUrl).send(); 48 | } 49 | 50 | @Override 51 | public TransactionReceipt buyPlayer(BigInteger playerId, BigInteger weiValue, String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception { 52 | SoccerManager soccerManager = loadSoccerManager(password, file, gasPrice, gasLimit); 53 | return soccerManager.buyPlayer(playerId, weiValue).send(); 54 | } 55 | 56 | @Override 57 | public TransactionReceipt updatePlayer(BigInteger playerId, Boolean forSale, String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception { 58 | SoccerManager soccerManager = loadSoccerManager(password, file, gasPrice, gasLimit); 59 | return soccerManager.updatePlayer(playerId, forSale).send(); 60 | } 61 | 62 | @Override 63 | public List getPlayersOfAgent(String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception { 64 | SoccerManager soccerManager = loadSoccerManager(password, file, gasPrice, gasLimit); 65 | return soccerManager.getPlayersOfAgent().send(); 66 | } 67 | 68 | @Override 69 | public BigInteger getNumberOfPlayers(String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws Exception { 70 | SoccerManager soccerManager = loadSoccerManager(password, file, gasPrice, gasLimit); 71 | return soccerManager.getNumberOfPlayers().send(); 72 | } 73 | 74 | private SoccerManager loadSoccerManager(String password, String file, BigInteger gasPrice, BigInteger gasLimit) throws IOException, CipherException { 75 | Credentials credentials = WalletUtils.loadCredentials(password, file); 76 | log.debug("Wallet credentials loaded! Address: {}", credentials.getAddress()); 77 | 78 | SoccerManager soccerManager = SoccerManager.load(contractAddress, web3j, credentials, new StaticGasProvider(gasPrice, gasLimit)); 79 | log.debug("SoccerManager contract loaded! Address: {}", soccerManager.getContractAddress()); 80 | 81 | return soccerManager; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /player-api/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8081 2 | 3 | spring.application.name=player-api 4 | 5 | ethereum.contract.soccermanager.address= 6 | 7 | management.endpoints.web.exposure.include=beans,env,health,info,metrics,mappings 8 | management.endpoint.health.show-details=always 9 | 10 | springdoc.show-actuator=true 11 | springdoc.swagger-ui.groups-order=DESC 12 | springdoc.swagger-ui.disable-swagger-default-url=true 13 | -------------------------------------------------------------------------------- /player-api/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | _ _ 2 | _ __ | | __ _ _ _ ___ _ __ __ _ _ __ (_) 3 | | '_ \| |/ _` | | | |/ _ \ '__|____ / _` | '_ \| | 4 | | |_) | | (_| | |_| | __/ | |_____| (_| | |_) | | 5 | | .__/|_|\__,_|\__, |\___|_| \__,_| .__/|_| 6 | |_| |___/ |_| 7 | :: Spring Boot :: ${spring-boot.formatted-version} 8 | -------------------------------------------------------------------------------- /player-api/src/test/java/com/ivanfranchin/playerapi/PlayerApiApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.ivanfranchin.playerapi; 2 | 3 | import org.junit.jupiter.api.Disabled; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | 7 | @Disabled 8 | @SpringBootTest 9 | class PlayerApiApplicationTests { 10 | 11 | @Test 12 | public void contextLoads() { 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /solidity/SoccerManager.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | // 1 ether = 1000000000000000000 wei 4 | 5 | contract SoccerManager { 6 | 7 | using MyUIntArraysLib for uint[]; 8 | 9 | address private owner; 10 | uint private numPlayers; 11 | mapping (uint => Player) private players; 12 | mapping (address => uint[]) private agents; 13 | uint[] playerIds; 14 | 15 | struct Player { 16 | uint id; 17 | bool status; 18 | string name; 19 | uint price; 20 | string image; 21 | bool forSale; 22 | address payable agent; 23 | } 24 | 25 | event PlayerAdded (uint playerId, string name, uint price, bool forSale); 26 | event PlayerUpdated (uint playerId, bool oldForSaleValue, bool newForSaleValue); 27 | event PlayerBought (uint playerId, address oldAgent, address newAgent, uint price); 28 | 29 | modifier isOwner { 30 | require(owner == msg.sender, "You are not the contract owner"); 31 | _; 32 | } 33 | 34 | constructor() public { 35 | owner = msg.sender; 36 | 37 | addPreDefinedPlayers(); 38 | } 39 | 40 | function addPreDefinedPlayers() private { 41 | insertPlayer("WEVERTON", 1000000000000000000, "https://s3.amazonaws.com/sep-bucket-prod/wp-content/uploads/2019/05/02082731/weverton.png", msg.sender); 42 | insertPlayer("FELIPE MELO", 2000000000000000000, "https://s3.amazonaws.com/sep-bucket-prod/wp-content/uploads/2019/08/02080049/felipe-melo.png", msg.sender); 43 | insertPlayer("GUSTAVO GOMEZ", 3000000000000000000, "https://s3.amazonaws.com/sep-bucket-prod/wp-content/uploads/2020/01/02080153/gustavo-gomez.png", msg.sender); 44 | } 45 | 46 | function addPlayer(string memory name, uint price, string memory image) public isOwner returns (uint) { 47 | require(bytes(name).length > 0, "The argument 'name' cannot be empty"); 48 | require(price > 0, "The argument 'price' must be > 0"); 49 | 50 | uint id = insertPlayer(name, price, image, msg.sender); 51 | 52 | emit PlayerAdded(id, name, price, true); 53 | return id; 54 | } 55 | 56 | function insertPlayer(string memory name, uint price, string memory image, address payable agent) private returns (uint) { 57 | uint id = ++numPlayers; 58 | players[id] = Player(id, true, name, price, image, true, agent); 59 | playerIds.push(id); 60 | agents[agent].push(id); 61 | return id; 62 | } 63 | 64 | function updatePlayer(uint id, bool forSale) public returns (bool) { 65 | require(players[id].status, "Player with the informed id doesn't exist"); 66 | require(players[id].forSale != forSale, "Player forSale status is the same already set"); 67 | require(players[id].agent == msg.sender, "You are not the agent of the player"); 68 | 69 | bool oldForSaleValue = players[id].forSale; 70 | players[id].forSale = forSale; 71 | 72 | emit PlayerUpdated(id, oldForSaleValue, forSale); 73 | return true; 74 | } 75 | 76 | function getPlayer(uint id) public view returns (uint, string memory, uint, string memory, bool, address) { 77 | require(players[id].status, "Player with the informed id doesn't exist"); 78 | Player storage player = players[id]; 79 | return (player.id, player.name, player.price, player.image, player.forSale, player.agent); 80 | } 81 | 82 | function getPlayersOfAgent() public view returns (uint[] memory) { 83 | return agents[msg.sender]; 84 | } 85 | 86 | function getNumberOfPlayers() public view returns (uint) { 87 | return playerIds.length; 88 | } 89 | 90 | function buyPlayer(uint id) public payable returns (bool) { 91 | require(players[id].status, "Player doesn't exist"); 92 | require(players[id].forSale, "Player is not for sale"); 93 | require(msg.sender != players[id].agent, "You are already the player agent"); 94 | require(msg.value == players[id].price, "Amount sent is incorrect"); 95 | require(msg.sender.balance >= players[id].price, "You don't have enought ether on your balance"); 96 | 97 | address payable fromAgent = players[id].agent; 98 | players[id].agent = msg.sender; // player has a new agent 99 | agents[players[id].agent].push(id); 100 | agents[fromAgent].removeValue(id); 101 | 102 | fromAgent.transfer(players[id].price); 103 | 104 | emit PlayerBought(id, fromAgent, msg.sender, players[id].price); 105 | return true; 106 | } 107 | 108 | } 109 | 110 | library MyUIntArraysLib { 111 | 112 | function removeValue(uint[] storage array, uint value) internal returns (bool) { 113 | for (uint i=0; i < array.length; i++) { 114 | if (array[i] == value) { 115 | array[i] = array[array.length-1]; 116 | array.length--; 117 | return true; 118 | } 119 | } 120 | return false; 121 | } 122 | } --------------------------------------------------------------------------------