diff --git a/.vscode/settings.json b/.vscode/settings.json index 855df12..b5e2554 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,8 +6,6 @@ "editor.defaultFormatter": "tamasfe.even-better-toml" }, "solidity.formatter": "forge", - "solidity.packageDefaultDependenciesContractsDirectory": "contracts/src", - "solidity.packageDefaultDependenciesDirectory": "contracts/lib", "editor.formatOnSave": true, "solidity.compileUsingRemoteVersion": "v0.8.19", "solidity.enabledAsYouTypeCompilationErrorCheck": true, diff --git a/contracts/broadcast/StartGame.s.sol/5/run-1696540368.json b/contracts/broadcast/StartGame.s.sol/5/run-1696540368.json new file mode 100644 index 0000000..d12b90d --- /dev/null +++ b/contracts/broadcast/StartGame.s.sol/5/run-1696540368.json @@ -0,0 +1,34 @@ +{ + "transactions": [ + { + "hash": "0xd1477799ca5f9bdf56384c1f63701fd8232857e449d3ee89c0f8ee0a6b9d6045", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xD2605193cc30Be96F69DE74d4c5f5fD286f87650", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x3c5cd6c3a8f47443f739597a89a630868f805e2e", + "to": "0xd2605193cc30be96f69de74d4c5f5fd286f87650", + "gas": "0x18838", + "value": "0x0", + "data": "0xbe9a6555", + "nonce": "0x277", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [], + "libraries": [], + "pending": [ + "0xd1477799ca5f9bdf56384c1f63701fd8232857e449d3ee89c0f8ee0a6b9d6045" + ], + "returns": {}, + "timestamp": 1696540368, + "chain": 5, + "multi": false, + "commit": "18b1ba4" +} \ No newline at end of file diff --git a/contracts/broadcast/StartGame.s.sol/5/run-1696540399.json b/contracts/broadcast/StartGame.s.sol/5/run-1696540399.json new file mode 100644 index 0000000..f85bac0 --- /dev/null +++ b/contracts/broadcast/StartGame.s.sol/5/run-1696540399.json @@ -0,0 +1,63 @@ +{ + "transactions": [ + { + "hash": "0xd1477799ca5f9bdf56384c1f63701fd8232857e449d3ee89c0f8ee0a6b9d6045", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xD2605193cc30Be96F69DE74d4c5f5fD286f87650", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x3c5cd6c3a8f47443f739597a89a630868f805e2e", + "to": "0xd2605193cc30be96f69de74d4c5f5fd286f87650", + "gas": "0x18838", + "value": "0x0", + "data": "0xbe9a6555", + "nonce": "0x277", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0xd1477799ca5f9bdf56384c1f63701fd8232857e449d3ee89c0f8ee0a6b9d6045", + "transactionIndex": "0x1", + "blockHash": "0x0f9c292c09042f943f9db32c7796cb6a0ef0e16ba9e8f84bc059a3cc79eab893", + "blockNumber": "0x95c61c", + "from": "0x3c5CD6c3A8f47443f739597A89A630868F805e2e", + "to": "0xD2605193cc30Be96F69DE74d4c5f5fD286f87650", + "cumulativeGasUsed": "0x16dfe", + "gasUsed": "0x11bf6", + "contractAddress": null, + "logs": [ + { + "address": "0xD2605193cc30Be96F69DE74d4c5f5fD286f87650", + "topics": [ + "0x762f260439bb4be3ef6e4dc2786e2e7bd187d3d80b79057d7a424fe98563e335" + ], + "data": "0x", + "blockHash": "0x0f9c292c09042f943f9db32c7796cb6a0ef0e16ba9e8f84bc059a3cc79eab893", + "blockNumber": "0x95c61c", + "transactionHash": "0xd1477799ca5f9bdf56384c1f63701fd8232857e449d3ee89c0f8ee0a6b9d6045", + "transactionIndex": "0x1", + "logIndex": "0x0", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000100000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e08" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1696540399, + "chain": 5, + "multi": false, + "commit": "18b1ba4" +} \ No newline at end of file diff --git a/contracts/broadcast/StartGame.s.sol/5/run-latest.json b/contracts/broadcast/StartGame.s.sol/5/run-latest.json new file mode 100644 index 0000000..f85bac0 --- /dev/null +++ b/contracts/broadcast/StartGame.s.sol/5/run-latest.json @@ -0,0 +1,63 @@ +{ + "transactions": [ + { + "hash": "0xd1477799ca5f9bdf56384c1f63701fd8232857e449d3ee89c0f8ee0a6b9d6045", + "transactionType": "CALL", + "contractName": null, + "contractAddress": "0xD2605193cc30Be96F69DE74d4c5f5fD286f87650", + "function": null, + "arguments": null, + "transaction": { + "type": "0x02", + "from": "0x3c5cd6c3a8f47443f739597a89a630868f805e2e", + "to": "0xd2605193cc30be96f69de74d4c5f5fd286f87650", + "gas": "0x18838", + "value": "0x0", + "data": "0xbe9a6555", + "nonce": "0x277", + "accessList": [] + }, + "additionalContracts": [], + "isFixedGasLimit": false + } + ], + "receipts": [ + { + "transactionHash": "0xd1477799ca5f9bdf56384c1f63701fd8232857e449d3ee89c0f8ee0a6b9d6045", + "transactionIndex": "0x1", + "blockHash": "0x0f9c292c09042f943f9db32c7796cb6a0ef0e16ba9e8f84bc059a3cc79eab893", + "blockNumber": "0x95c61c", + "from": "0x3c5CD6c3A8f47443f739597A89A630868F805e2e", + "to": "0xD2605193cc30Be96F69DE74d4c5f5fD286f87650", + "cumulativeGasUsed": "0x16dfe", + "gasUsed": "0x11bf6", + "contractAddress": null, + "logs": [ + { + "address": "0xD2605193cc30Be96F69DE74d4c5f5fD286f87650", + "topics": [ + "0x762f260439bb4be3ef6e4dc2786e2e7bd187d3d80b79057d7a424fe98563e335" + ], + "data": "0x", + "blockHash": "0x0f9c292c09042f943f9db32c7796cb6a0ef0e16ba9e8f84bc059a3cc79eab893", + "blockNumber": "0x95c61c", + "transactionHash": "0xd1477799ca5f9bdf56384c1f63701fd8232857e449d3ee89c0f8ee0a6b9d6045", + "transactionIndex": "0x1", + "logIndex": "0x0", + "removed": false + } + ], + "status": "0x1", + "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000100000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000", + "type": "0x2", + "effectiveGasPrice": "0xb2d05e08" + } + ], + "libraries": [], + "pending": [], + "returns": {}, + "timestamp": 1696540399, + "chain": 5, + "multi": false, + "commit": "18b1ba4" +} \ No newline at end of file diff --git a/contracts/script/FactoryDeployer.s.sol b/contracts/script/FactoryDeployer.s.sol deleted file mode 100644 index 2013dad..0000000 --- a/contracts/script/FactoryDeployer.s.sol +++ /dev/null @@ -1,17 +0,0 @@ -// // SPDX-License-Identifier: MIT -// pragma solidity ^0.8.19; - -// import { Script, console } from "forge-std/Script.sol"; -// import { TankGameFactory } from "src/base/TankGameFactory.sol"; -// import { TankGame } from "src/base/TankGameV2.sol"; -// import { ITankGame } from "src/interfaces/ITankGame.sol"; -// import { GameView } from "src/view/GameView.sol"; - -// contract TankGameDeployerScript is Script { -// function run() public { -// vm.startBroadcast(); -// TankGameFactory factory = new TankGameFactory(); -// console.log("TankGameFactory at address: %s", address(factory)); -// vm.stopBroadcast(); -// } -// } diff --git a/contracts/script/StartGame.s.sol b/contracts/script/StartGame.s.sol new file mode 100644 index 0000000..beea4ae --- /dev/null +++ b/contracts/script/StartGame.s.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { Script, console } from "forge-std/Script.sol"; +// import { TankGameFactory } from "src/base/TankGameFactory.sol"; +import { TankGame } from "src/base/TankGameV2.sol"; + +contract StartGameScript is Script { + function run() public { + vm.startBroadcast(); + TankGame tankGame = TankGame(0xD2605193cc30Be96F69DE74d4c5f5fD286f87650); + + tankGame.start(); + vm.stopBroadcast(); + } +} diff --git a/contracts/script/TankGameDeployer.s.sol b/contracts/script/TankGameDeployer.s.sol index 1128cd2..b181c8d 100644 --- a/contracts/script/TankGameDeployer.s.sol +++ b/contracts/script/TankGameDeployer.s.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.19; import { Script, console } from "forge-std/Script.sol"; -// import { TankGameFactory } from "src/base/TankGameFactory.sol"; import { TankGame } from "src/base/TankGameV2.sol"; import { ITankGame } from "src/interfaces/ITankGame.sol"; import { GameView } from "src/view/GameView.sol"; diff --git a/contracts/script/TankGameDeployerSim.s.sol b/contracts/script/TankGameDeployerSim.s.sol new file mode 100644 index 0000000..23dd6b6 --- /dev/null +++ b/contracts/script/TankGameDeployerSim.s.sol @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import { Script, console } from "forge-std/Script.sol"; +// import { TankGameFactory } from "src/base/TankGameFactory.sol"; +import { TankGame } from "src/base/TankGameV2.sol"; +import { ITankGame } from "src/interfaces/ITankGame.sol"; +import { GameView } from "src/view/GameView.sol"; +import { NonAggression } from "src/hooks/NonAggression.sol"; +import { Bounty } from "src/hooks/Bounty.sol"; +import { HookFactory } from "src/base/HookFactory.sol"; +import { IHooks } from "src/interfaces/IHooks.sol"; + +contract TankGameDeployerScript is Script { + TankGame public tankGame; + + function run() public { + vm.startBroadcast(); + address adminAddress = vm.envAddress("ADMIN_ADDRESS"); + NamedPlayer[3] memory _staticAddresses = [ + NamedPlayer(0x1000000000000000000000000000000000000101, "bot1"), + NamedPlayer(0x1000000000000000000000000000000000000002, "bot2"), + NamedPlayer(adminAddress, "admin") + ]; + + // TankGameFactory factory = new TankGameFactory(); + ITankGame.GameSettings memory gs = ITankGame.GameSettings({ + playerCount: _staticAddresses.length, + boardSize: 30, + initAPs: 1000, + initHearts: 3, + initShootRange: 3, + epochSeconds: 1 seconds, + buyInMinimum: 0, + revealWaitBlocks: 45 seconds, + root: bytes32(0x0) + }); + tankGame = new TankGame(gs, msg.sender); + + GameView gameView = new GameView(tankGame); + HookFactory hookFactory = new HookFactory(); + console.log("TankGame at address: %s", address(tankGame)); + console.log("TankGameView at address: %s", address(gameView)); + console.log("HookFactory at address: %s", address(hookFactory)); + // join everyone. + for (uint256 i = 0; i < _staticAddresses.length; i++) { + NamedPlayer memory np = _staticAddresses[i]; + console.log("Joining %s at address %s", np.name, np.player); + tankGame.join(ITankGame.JoinParams(np.player, new bytes32[](0), np.name)); + // for every player give them a default hook for NonAggression and Bounties + // this wont be allowed because hooks only added by owner + // can get around this by allownig the admin to at the beginning + IHooks nonAggro = hookFactory.createHook(tankGame, i + 1, HookFactory.HookRegistry.NonAggression); + IHooks bounty = hookFactory.createHook(tankGame, i + 1, HookFactory.HookRegistry.Bounty); + + console.log("Adding bounty hook for %s at address %s", np.name, address(bounty)); + console.log("Adding nonaggression hook for %s at address %s", np.name, address(nonAggro)); + tankGame.forceAddDefaultHook(i + 1, nonAggro); + tankGame.forceAddDefaultHook(i + 1, bounty); + + // done with default hooks. this is temporary solution + + // finance this player + // payable(np.player).transfer(0.1 ether); // the piggies are full + } + + tankGame.setOwner(address(0)); + tankGame.start(); + vm.stopBroadcast(); + } + + struct NamedPlayer { + address player; + string name; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..07e66bb --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "", + "type": "module", + "devDependencies": { + "bun-types": "^1.0.4-canary.20231003T140149" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "scripts": { + "server": "bun run index.ts" + } +} diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..ab5afb2 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1,176 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +\*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +\*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +\*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +\*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.cache +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +.cache/ + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp +.cache + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.\* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store + diff --git a/server/README.md b/server/README.md new file mode 100644 index 0000000..91c41b7 --- /dev/null +++ b/server/README.md @@ -0,0 +1,21 @@ +# serve + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +for some endoints, you will need an authenticated RPC url + +```bash +export RPC_URL=blah +``` + +This project was created using `bun init` in bun v1.0.4. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/server/api/client.ts b/server/api/client.ts new file mode 100644 index 0000000..0d5614f --- /dev/null +++ b/server/api/client.ts @@ -0,0 +1,12 @@ +import { PublicClient, createPublicClient, http } from "viem"; +import { goerli, foundry } from "viem/chains"; + +const RPC_URL = process.env.RPC_URL!; +const CHAIN = process.env.CHAIN!; + +const publicClient: PublicClient = createPublicClient({ + chain: CHAIN == "5" ? goerli : foundry, + transport: http(RPC_URL), +}); + +export default publicClient; diff --git a/server/bun.lockb b/server/bun.lockb new file mode 100755 index 0000000..ed282f3 Binary files /dev/null and b/server/bun.lockb differ diff --git a/server/examples-requests/move.json b/server/examples-requests/move.json new file mode 100644 index 0000000..498c2ad --- /dev/null +++ b/server/examples-requests/move.json @@ -0,0 +1,5 @@ +{ + "from": "0x0", + "action": "move", + "params": { "tankId": "1", "to": { "x": 1, "y": 1, "z": 1 } } +} diff --git a/server/examples-requests/shoot.json b/server/examples-requests/shoot.json new file mode 100644 index 0000000..03f39dc --- /dev/null +++ b/server/examples-requests/shoot.json @@ -0,0 +1,5 @@ +{ + "from": "0x259A3AB4A06d647380B046249ef3b12dB212Dc3e", + "action": "shoot", + "params": [ "1", "1", "1"] +} diff --git a/server/index.ts b/server/index.ts new file mode 100644 index 0000000..acbea7a --- /dev/null +++ b/server/index.ts @@ -0,0 +1,35 @@ +import express from "express"; +import createError from "http-errors"; +import path from "path"; +import cookieParser from "cookie-parser"; +import logger from "morgan"; +import logsRouter from "./routes/logs"; +import txRouter from "./routes/tx"; +import testRouter from "./routes/test"; +import { Request, Response, NextFunction } from "express"; +const app = express(); +const port = 3000; + +// view engine setup +app.use(logger("dev")); +app.use(express.json()); +app.use(express.urlencoded({ extended: false })); +app.use(cookieParser()); +app.use(express.static(path.join(__dirname, "public"))); + +app.use("/logs", logsRouter); +app.use("/tx", txRouter); +app.use("/test", testRouter); + +// catch 404 and forward to error handler +app.use(function (req: Request, res: Response, next: NextFunction) { + next(createError(404)); +}); + +app.get("/logs", (req, res) => { + logsRouter; +}); + +app.listen(port, () => { + console.log(`Listening on port ${port}...`); +}); diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..5a2c674 --- /dev/null +++ b/server/package.json @@ -0,0 +1,28 @@ +{ + "name": "serve", + "module": "index.ts", + "type": "module", + "devDependencies": { + "bun-types": "^1.0.4-canary.20231003T140149" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "scripts": { + "start": "bun run index.ts", + "test": "bun run test/integ.ts" + }, + "dependencies": { + "@types/cookie-parser": "^1.4.4", + "@types/express": "^4.17.18", + "@types/morgan": "^1.9.6", + "axios": "^1.5.1", + "cookie-parser": "^1.4.6", + "debug": "^4.3.4", + "express": "^4.18.2", + "http-errors": "^2.0.0", + "jade": "^1.11.0", + "morgan": "^1.10.0", + "viem": "^1.15.1" + } +} diff --git a/server/routes/logs.ts b/server/routes/logs.ts new file mode 100644 index 0000000..886f0d8 --- /dev/null +++ b/server/routes/logs.ts @@ -0,0 +1,43 @@ +import { NextFunction, Request, Response, Router } from "express"; +import { tankGameABI } from "../../frontend/src/generated"; +import publicClient from "../api/client"; +var logsRouter = Router(); + +logsRouter.post("/", logHandler); + +function logHandler(req: Request, res: Response, next: NextFunction) { + if (!req.body.address) { + return res.status(400).send("address not provided"); + } + getLogs( + req.body.fromBlock !== undefined ? req.body.fromBlock : 0, + req.body.address + ) + .then((logs) => { + res.send( + // kinda hacky to handle the fact that BigInts are not JSON serializable + JSON.parse( + JSON.stringify(logs, (key, value) => + typeof value === "bigint" ? value.toString() : value + ) + ) + ); + }) + .catch((err) => { + return res.status(500).send(err.message); + }); +} + +async function getLogs(fromBlock: number, address: `0x${string}`) { + const filter = await publicClient.createContractEventFilter({ + abi: tankGameABI, + strict: true, + fromBlock: BigInt(fromBlock), + address: address, + }); + return await publicClient.getFilterLogs({ + filter, + }); +} + +export default logsRouter; diff --git a/server/routes/test.ts b/server/routes/test.ts new file mode 100644 index 0000000..1ef112b --- /dev/null +++ b/server/routes/test.ts @@ -0,0 +1,48 @@ +import { Request, Response, NextFunction, Router } from "express"; +import { exec } from "child_process"; + +var testRouter = Router(); + +testRouter.post("/init", initHandler); + +function initHandler(req: Request, res: Response, next: NextFunction) { + exec( + "pkill -f 'anvil' || true && cd ../contracts && make chain", + (error, stdout, stderr) => { + if (error) { + console.error(`Error: ${error.message}`); + return res.status(500).send(`Error: ${error.message}`); + } + console.log(`stdout: ${stdout}`); + console.log("anvil instance launched"); + } + ); + console.log("admin address " + req.body.adminAddress); + // deploy the contracts for sim + exec( + `cd ../contracts && export ADMIN_ADDRESS=${req.body.adminAddress} && forge script script/TankGameDeployerSim.s.sol --broadcast --rpc-url http://0.0.0.0:8545 --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`, + (error, stdout, stderr) => { + console.log("deploying contracts"); + if (error) { + console.error(`Error: ${error.message}`); + return res.status(500).send(`Error: ${error.message}`); + } + const result = parseAddresses(stdout); + return res.send(result); + } + ); +} + +function parseAddresses(stdout: string) { + let result: { [key: string]: string } = {}; + stdout + .split("\n") + .filter((x) => x.includes("at address:")) + .forEach((line) => { + const match = + line.match(/(\w+)\s+at\s+address:\s+(0x[a-fA-F0-9]+)/) || []; + result[match[1]] = match[2]; + }); + return result; +} +export default testRouter; diff --git a/server/routes/tx.ts b/server/routes/tx.ts new file mode 100644 index 0000000..82df7a0 --- /dev/null +++ b/server/routes/tx.ts @@ -0,0 +1,72 @@ +import { NextFunction, Request, Response, Router } from "express"; +import { tankGameABI, tankGameAddress } from "../../frontend/src/generated"; +import publicClient from "../api/client"; +import { foundry } from "viem/chains"; +import { serializeTransaction } from "viem"; +var txRouter = Router(); + +txRouter.post("/sim", simHandler); +txRouter.post("/send", sendHandler); + +async function simHandler(req: Request, res: Response, next: NextFunction) { + const { from, action, params, address } = req.body; + try { + let request = await publicClient.simulateContract({ + account: from, + address: address, + abi: tankGameABI, + functionName: action, + args: [params], + chain: foundry, + }); + + let prepRequest = await publicClient.prepareTransactionRequest( + request.request + ); + console.log("prep request:", { ...prepRequest, abi: "OMITTED" }); + // let ts = serializeTransaction({ + + // chainId: prepRequest.chain?.id, + // gas: prepRequest.gas, + // // maxFeePerGas: prepRequest.maxFeePerGas, + // // maxPriorityFeePerGas: prepRequest.maxPriorityFeePerGas, + // nonce: prepRequest.nonce, + // to: prepRequest.to, + // value: prepRequest.value, + // type: "0x2", + // data: prepRequest.data, + // gasPrice: prepRequest.gasPrice, + + // }); + // console.log("serialized tx:", ts); + const stringifiedPrepRequest = JSON.parse( + JSON.stringify({ ...prepRequest, chainId: foundry.id}, (_, value) => + typeof value === "bigint" ? value.toString() : value + ) + ); + return res.send(prepRequest); + // @ts-ignore + // let s = serializeTransaction({ ...prepRequest, chainId: foundry.id }); + // console.log(s); + // return res.send(s); + } catch (err: any) { + console.log(err); + return res.status(500).send(err.shortMessage); + } +} + +// the only purpose of passing this through the server is to parse the response +// nicely for the c++ client +function sendHandler(req: Request, res: Response, next: NextFunction) { + console.log("signed tx:", req.body.signedTx); + publicClient + .sendRawTransaction(req.body.signedTx) + .then((result) => { + return res.send(result); + }) + .catch((err) => { + return res.send(err.shortMessage); + }); +} + +export default txRouter; diff --git a/server/test/integ.ts b/server/test/integ.ts new file mode 100644 index 0000000..94673fb --- /dev/null +++ b/server/test/integ.ts @@ -0,0 +1,66 @@ +import axios from "axios"; +import { createWalletClient, http } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; + +// const ethers = require("ethers"); +// const signer = new ethers.Wallet( +// "0x0123456789012345678901234567890123456789012345678901234567890123" +// ); +// const provider = new ethers.providers.JsonRpcProvider("http://localhost:8545"); +// const wallet = signer.connect(provider); +const account = privateKeyToAccount( + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +); +console.log("account:", account.address); +// const walletClient = createWalletClient({ +// account, +// transport: http(), +// }); + +const URL = "http://localhost:3000"; +let initResponse = await axios.post(`${URL}/test/init`, { + adminAddress: account.address, +}); + +const { TankGame, TankGameView } = initResponse.data; +console.log("tank game:", TankGame); + +let logsResponse = await axios.post(`${URL}/logs`, { + address: TankGame, + fromBlock: 0, +}); +console.log("init logs:", logsResponse.data.length); + +let logsResponseNoFrom = await axios.post(`${URL}/logs`, { + address: TankGame, + fromBlock: 0, +}); +console.log("init logs no from:", logsResponseNoFrom.data.length); + +let { data: unsignedTx } = await axios.post(`${URL}/tx/sim`, { + from: account.address, + address: TankGame, + action: "move", + params: { + tankId: 3, + to: { + x: 30, + y: 30, + z: 30, + }, + }, +}); +// console.log("unsigned tx:", unsignedTx); +let signedTX = await account.signTransaction(unsignedTx); +// let signedTX = await account.signTransaction({ +// data: unsignedTx, +// to: TankGame, +// account: account.address, +// value: BigInt(0), +// type: "0x2", +// }); + +let { data: sendResponse } = await axios.post(`${URL}/tx/send`, { + signedTx: signedTX, +}); +console.log("send response:", sendResponse); diff --git a/server/tsconfig.json b/server/tsconfig.json new file mode 100644 index 0000000..182ef4b --- /dev/null +++ b/server/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + "allowImportingTsExtensions": true, + "noEmit": true, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "types": [ + "bun-types" // add Bun global + ] + }, + "include": ["**/*", "../frontend/**/*"] +}