diff --git a/services/ponder/.env.local b/services/ponder/.env.local index 7839fed..78eb102 100644 --- a/services/ponder/.env.local +++ b/services/ponder/.env.local @@ -2,7 +2,7 @@ PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK KRAIKEN_ADDRESS=0x56186c1e64ca8043def78d06aff222212ea5df71 STAKE_ADDRESS=0x056e4a859558a3975761abd7385506bc4d8a8e60 -START_BLOCK=31441844 +START_BLOCK=31443298 # Use PostgreSQL connection DATABASE_URL=postgresql://ponder:ponder_local@localhost/ponder_local -DATABASE_SCHEMA=ponder_local_31441844 +DATABASE_SCHEMA=ponder_local_31443298 diff --git a/services/txnBot/service.js b/services/txnBot/service.js index 4d33e43..5325c69 100644 --- a/services/txnBot/service.js +++ b/services/txnBot/service.js @@ -61,6 +61,7 @@ const stakeContract = new ethers.Contract(STAKE_CONTRACT_ADDRESS, STAKE_ABI, wal let startTime = new Date(); let lastRecenterTime = null; let lastLiquidationTime = null; +let lastRecenterTx = null; async function fetchActivePositions() { const response = await fetch(GRAPHQL_ENDPOINT, { @@ -97,6 +98,25 @@ async function canCallFunction() { } } +async function attemptRecenter() { + if (!(await canCallFunction())) { + return { + executed: false, + message: 'Liquidity manager denied recenter (likely already centered).' + }; + } + + const tx = await liquidityManager.recenter(); + lastRecenterTx = tx.hash; + await tx.wait(); + lastRecenterTime = new Date(); + return { + executed: true, + txHash: tx.hash, + message: 'recenter transaction submitted' + }; +} + async function checkPosition(position) { const taxRate = Number(position.taxRate); const lastTaxTime = Number(position.lastTaxTime); @@ -127,12 +147,9 @@ function formatDuration(ms) { async function liquidityLoop() { try { - if (await canCallFunction()) { - console.log('Calling recenter...'); - const tx = await liquidityManager.recenter(); - await tx.wait(); - lastRecenterTime = new Date(); - console.log('recenter called successfully.'); + const result = await attemptRecenter(); + if (result.executed) { + console.log(`recenter called successfully. tx=${result.txHash}`); } else { console.log(`No liquidity can be moved at the moment. - ${(new Date()).toISOString()}`); } @@ -174,6 +191,16 @@ main().catch(async (error) => { const app = express(); const PORT = process.env.PORT || 3000; +app.use((req, res, next) => { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS'); + res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); + if (req.method === 'OPTIONS') { + return res.sendStatus(204); + } + return next(); +}); + app.get('/status', async (req, res) => { try { const balance = await checkFunds(); @@ -182,7 +209,8 @@ app.get('/status', async (req, res) => { balance: `${balance} ETH`, uptime: uptime, lastRecenterTime: lastRecenterTime ? lastRecenterTime.toISOString() : 'Never', - lastLiquidationTime: lastLiquidationTime ? lastLiquidationTime.toISOString() : 'Never' + lastLiquidationTime: lastLiquidationTime ? lastLiquidationTime.toISOString() : 'Never', + lastRecenterTx, }; if (parseFloat(balance) < 0.1) { @@ -195,6 +223,19 @@ app.get('/status', async (req, res) => { } }); +app.post('/recenter', async (req, res) => { + try { + const result = await attemptRecenter(); + if (!result.executed) { + return res.status(202).json(result); + } + return res.status(200).json(result); + } catch (error) { + console.error('Manual recenter failed:', error); + return res.status(500).json({ error: error.message || 'recenter failed' }); + } +}); + app.listen(PORT, () => { console.log(`HTTP server running on port ${PORT}`); }); diff --git a/web-app/src/config.ts b/web-app/src/config.ts index 0db846d..965b22f 100644 --- a/web-app/src/config.ts +++ b/web-app/src/config.ts @@ -1,4 +1,5 @@ const LOCAL_PONDER_URL = "http://127.0.0.1:42069/graphql"; +const LOCAL_TXNBOT_URL = "http://127.0.0.1:43069"; export const DEFAULT_CHAIN_ID = Number(import.meta.env.VITE_DEFAULT_CHAIN_ID ?? 84532); @@ -10,7 +11,8 @@ export const chainsData = [ path: "sepolia", stake: "0xCd21a41a137BCAf8743E47D048F57D92398f7Da9", harb: "0x087F256D11fe533b0c7d372e44Ee0F9e47C89dF9", - uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE" + uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE", + cheats: null }, { // base-sepolia (local dev default) id: 84532, @@ -18,7 +20,12 @@ export const chainsData = [ path: "sepoliabase", stake: "0xe28020BCdEeAf2779dd47c670A8eFC2973316EE2", harb: "0x22c264Ecf8D4E49D1E3CabD8DD39b7C4Ab51C1B8", - uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE" + uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE", + cheats: { + weth: "0x4200000000000000000000000000000000000006", + swapRouter: "0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4", + txnBot: import.meta.env.VITE_TXNBOT_BASE_SEPOLIA_LOCAL_FORK ?? LOCAL_TXNBOT_URL + } }, { // base mainnet @@ -27,7 +34,12 @@ export const chainsData = [ path: "base", stake: "0xed70707fab05d973ad41eae8d17e2bcd36192cfc", harb: "0x45caa5929f6ee038039984205bdecf968b954820", - uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE" + uniswap: "https://app.uniswap.org/swap?chain=mainnet&inputCurrency=NATIVE", + cheats: { + weth: "0x4200000000000000000000000000000000000006", + swapRouter: "", + txnBot: import.meta.env.VITE_TXNBOT_BASE ?? "" + } }, ]; diff --git a/web-app/src/router/index.ts b/web-app/src/router/index.ts index 5602c28..54b8c69 100644 --- a/web-app/src/router/index.ts +++ b/web-app/src/router/index.ts @@ -29,6 +29,15 @@ const router = createRouter({ , }, component: () => import("../views/LoginView.vue"), }, + { + path: "/cheats", + name: "cheats", + meta: { + title: "Cheats", + layout: 'NavbarLayout' + }, + component: () => import("../views/CheatsView.vue"), + }, ], }); diff --git a/web-app/src/views/CheatsView.vue b/web-app/src/views/CheatsView.vue new file mode 100644 index 0000000..e5b8a2e --- /dev/null +++ b/web-app/src/views/CheatsView.vue @@ -0,0 +1,429 @@ + + + + +