Merge pull request 'fix: services/txnBot still has dead recenterAccess read infrastructure (#887)' (#917) from fix/issue-887 into master
This commit is contained in:
commit
5adf70518f
6 changed files with 5 additions and 155 deletions
|
|
@ -8,7 +8,7 @@
|
|||
"build": "tsc -p tsconfig.build.json",
|
||||
"start": "node dist/service.js",
|
||||
"dev": "tsx watch src/service.ts",
|
||||
"test": "node --test --import tsx src/recenterAccess.test.ts",
|
||||
"test": "node --test --import tsx",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
"lint:fix": "eslint --fix src/**/*.ts",
|
||||
"format": "prettier --write src/**/*.ts",
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { ethers } from 'ethers';
|
||||
import { hasRecenterAccess, readRecenterAccess, type RecenterAccessReader } from './recenterAccess.js';
|
||||
|
||||
const ZERO_ADDRESS = ethers.ZeroAddress;
|
||||
|
||||
class MockRecenterAccessReader implements RecenterAccessReader {
|
||||
constructor(
|
||||
private readonly value: string,
|
||||
private readonly shouldThrow = false
|
||||
) {}
|
||||
|
||||
async recenterAccess(): Promise<string> {
|
||||
if (this.shouldThrow) {
|
||||
throw new Error('read failed');
|
||||
}
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
test('readRecenterAccess returns zero address for empty or zero values', async () => {
|
||||
const reader = new MockRecenterAccessReader('0x0000000000000000000000000000000000000000');
|
||||
assert.equal(await readRecenterAccess(reader, ZERO_ADDRESS), ZERO_ADDRESS);
|
||||
|
||||
const emptyReader = new MockRecenterAccessReader('');
|
||||
assert.equal(await readRecenterAccess(emptyReader, ZERO_ADDRESS), ZERO_ADDRESS);
|
||||
});
|
||||
|
||||
test('readRecenterAccess normalises checksum addresses', async () => {
|
||||
const reader = new MockRecenterAccessReader('0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc');
|
||||
assert.equal(await readRecenterAccess(reader, ZERO_ADDRESS), '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC');
|
||||
});
|
||||
|
||||
test('readRecenterAccess throws when reader fails', async () => {
|
||||
const reader = new MockRecenterAccessReader('0x0', true);
|
||||
await assert.rejects(() => readRecenterAccess(reader, ZERO_ADDRESS), /read failed/);
|
||||
});
|
||||
|
||||
test('hasRecenterAccess acknowledges zero or wallet matches', () => {
|
||||
const wallet = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC';
|
||||
assert.equal(hasRecenterAccess(ZERO_ADDRESS, wallet, ZERO_ADDRESS), true);
|
||||
assert.equal(hasRecenterAccess(wallet, wallet, ZERO_ADDRESS), true);
|
||||
assert.equal(hasRecenterAccess('0x5cFB5CDd3E8723ba98312c90a43a4d6Ac6121240', wallet, ZERO_ADDRESS), false);
|
||||
});
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
import { ethers } from 'ethers';
|
||||
|
||||
export interface RecenterAccessReader {
|
||||
recenterAccess(): Promise<string>;
|
||||
}
|
||||
|
||||
export async function readRecenterAccess(reader: RecenterAccessReader, zeroAddress: string): Promise<string> {
|
||||
let raw: string;
|
||||
try {
|
||||
raw = await reader.recenterAccess();
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to read recenterAccess: ${(error as Error).message}`);
|
||||
}
|
||||
|
||||
if (typeof raw !== 'string' || raw.length === 0) {
|
||||
return zeroAddress;
|
||||
}
|
||||
|
||||
if (raw === zeroAddress) {
|
||||
return zeroAddress;
|
||||
}
|
||||
|
||||
try {
|
||||
return ethers.getAddress(raw);
|
||||
} catch (error) {
|
||||
throw new Error(`Invalid recenterAccess address: ${(error as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function hasRecenterAccess(recenterAddress: string, walletAddress: string, zeroAddress: string): boolean {
|
||||
if (recenterAddress === zeroAddress) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
return ethers.getAddress(recenterAddress) === ethers.getAddress(walletAddress);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import express, { NextFunction, Request, Response } from 'express';
|
||||
import { ethers } from 'ethers';
|
||||
import { decodePositionId } from 'kraiken-lib/ids';
|
||||
import { isPositionDelinquent } from 'kraiken-lib/staking';
|
||||
import { pathToFileURL } from 'url';
|
||||
|
|
@ -7,7 +6,6 @@ import { BotConfigService } from './services/BotConfigService.js';
|
|||
import { BlockchainService } from './services/BlockchainService.js';
|
||||
import { GraphQLService } from './services/GraphQLService.js';
|
||||
import { logger } from './logger.js';
|
||||
import { hasRecenterAccess, readRecenterAccess } from './recenterAccess.js';
|
||||
import { Position, RecenterAccessStatus, RecenterEligibility, RecenterResult } from './types.js';
|
||||
|
||||
const ACTIVE_POSITIONS_QUERY = `
|
||||
|
|
@ -24,8 +22,6 @@ const ACTIVE_POSITIONS_QUERY = `
|
|||
}
|
||||
`;
|
||||
|
||||
const ZERO_ADDRESS = ethers.ZeroAddress;
|
||||
|
||||
export interface TxnBotDependencies {
|
||||
configService: BotConfigService;
|
||||
blockchainService: BlockchainService;
|
||||
|
|
@ -91,8 +87,6 @@ function formatDuration(ms: number): string {
|
|||
export function createTxnBot(dependencies: TxnBotDependencies): TxnBotInstance {
|
||||
const { configService, blockchainService, graphQLService } = dependencies;
|
||||
const envConfig = configService.getConfig();
|
||||
const recenterAccessReader = blockchainService.getRecenterAccessReader();
|
||||
const walletAddress = blockchainService.getWalletAddress();
|
||||
|
||||
const startTime = new Date();
|
||||
let lastRecenterTime: Date | null = null;
|
||||
|
|
@ -125,28 +119,10 @@ export function createTxnBot(dependencies: TxnBotDependencies): TxnBotInstance {
|
|||
return lastRecenterAccessStatus;
|
||||
}
|
||||
|
||||
let recenterAddress: string | null = null;
|
||||
let hasAccess: boolean | null = null;
|
||||
let slotHex: string | null = null;
|
||||
let errorMessage: string | null = null;
|
||||
|
||||
try {
|
||||
const address = await readRecenterAccess(recenterAccessReader, ZERO_ADDRESS);
|
||||
recenterAddress = address;
|
||||
hasAccess = hasRecenterAccess(address, walletAddress, ZERO_ADDRESS);
|
||||
slotHex = 'recenterAccess()';
|
||||
} catch (error) {
|
||||
const err = error as { shortMessage?: string; message?: string };
|
||||
errorMessage = err?.shortMessage || err?.message || 'unknown error';
|
||||
recenterAddress = null;
|
||||
}
|
||||
|
||||
lastRecenterAccessStatus = {
|
||||
hasAccess,
|
||||
recenterAccessAddress: recenterAddress,
|
||||
slot: slotHex,
|
||||
hasAccess: true,
|
||||
checkedAtMs: now,
|
||||
error: errorMessage,
|
||||
error: null,
|
||||
};
|
||||
|
||||
return lastRecenterAccessStatus;
|
||||
|
|
@ -154,28 +130,6 @@ export function createTxnBot(dependencies: TxnBotDependencies): TxnBotInstance {
|
|||
|
||||
async function evaluateRecenterOpportunity(): Promise<RecenterEligibility> {
|
||||
const now = Date.now();
|
||||
const accessStatus = await getRecenterAccessStatus(true);
|
||||
|
||||
if (accessStatus.error && accessStatus.hasAccess === null) {
|
||||
lastRecenterEligibility = {
|
||||
checkedAtMs: now,
|
||||
canRecenter: false,
|
||||
reason: 'Failed to determine recenter access.',
|
||||
error: accessStatus.error,
|
||||
};
|
||||
return lastRecenterEligibility;
|
||||
}
|
||||
|
||||
if (accessStatus.hasAccess === false) {
|
||||
lastRecenterEligibility = {
|
||||
checkedAtMs: now,
|
||||
canRecenter: false,
|
||||
reason: 'txnBot is not the authorized recenter caller.',
|
||||
error: null,
|
||||
};
|
||||
return lastRecenterEligibility;
|
||||
}
|
||||
|
||||
try {
|
||||
await blockchainService.estimateRecenterGas();
|
||||
lastRecenterEligibility = {
|
||||
|
|
@ -287,9 +241,7 @@ export function createTxnBot(dependencies: TxnBotDependencies): TxnBotInstance {
|
|||
lastLiquidationTime: lastLiquidationTime ? lastLiquidationTime.toISOString() : 'Never',
|
||||
lastRecenterTx,
|
||||
recenterAccess: {
|
||||
hasAccess: recenterAccessStatus?.hasAccess ?? null,
|
||||
grantedTo: recenterAccessStatus?.recenterAccessAddress ?? null,
|
||||
slot: recenterAccessStatus?.slot ?? null,
|
||||
hasAccess: recenterAccessStatus?.hasAccess ?? true,
|
||||
checkedAt: recenterAccessStatus?.checkedAtMs ? new Date(recenterAccessStatus.checkedAtMs).toISOString() : null,
|
||||
error: recenterAccessStatus?.error ?? null,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { Contract, JsonRpcProvider, TransactionResponse, Wallet, ethers } from 'ethers';
|
||||
import { RecenterAccessReader } from '../recenterAccess.js';
|
||||
|
||||
export interface BlockchainConfig {
|
||||
providerUrl: string;
|
||||
|
|
@ -11,7 +10,6 @@ export interface BlockchainConfig {
|
|||
const LM_ABI = [
|
||||
{ type: 'function', name: 'recenter', inputs: [], outputs: [], stateMutability: 'nonpayable' },
|
||||
{ type: 'function', name: 'feeDestination', inputs: [], outputs: [{ type: 'address' }], stateMutability: 'view' },
|
||||
{ type: 'function', name: 'recenterAccess', inputs: [], outputs: [{ type: 'address' }], stateMutability: 'view' },
|
||||
];
|
||||
|
||||
const STAKE_ABI = [
|
||||
|
|
@ -37,19 +35,6 @@ export class BlockchainService {
|
|||
this.stakeContract = new ethers.Contract(config.stakeContractAddress, STAKE_ABI, this.wallet);
|
||||
}
|
||||
|
||||
getWalletAddress(): string {
|
||||
return ethers.getAddress(this.wallet.address);
|
||||
}
|
||||
|
||||
getRecenterAccessReader(): RecenterAccessReader {
|
||||
return {
|
||||
recenterAccess: async (): Promise<string> => {
|
||||
const method = this.liquidityManager.getFunction('recenterAccess');
|
||||
return (await method()) as string;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async checkFunds(): Promise<string> {
|
||||
const balance = await this.provider.getBalance(this.wallet.address);
|
||||
return ethers.formatEther(balance);
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@ export interface EnvConfig {
|
|||
}
|
||||
|
||||
export interface RecenterAccessStatus {
|
||||
hasAccess: boolean | null;
|
||||
recenterAccessAddress: string | null;
|
||||
slot: string | null;
|
||||
hasAccess: boolean;
|
||||
checkedAtMs: number;
|
||||
error: string | null;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue