From df55108bf8433241c067eacbbb181bc5a9100351 Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Tue, 6 Jan 2026 22:16:23 +0000 Subject: [PATCH 1/2] chore: Remove pool functionality This functionality was only used by the frontend and has been migrated there in entirety. This permits @uma/contracts-node to be dropped as a dependency. --- package.json | 1 - src/index.ts | 1 - src/pool/TransactionManager.ts | 73 -- src/pool/index.ts | 1 - src/pool/poolClient.ts | 849 ---------------------- src/pool/uma/across/constants.ts | 2 - src/pool/uma/across/index.ts | 2 - src/pool/uma/across/transactionManager.ts | 73 -- src/pool/uma/clients/erc20/README.md | 29 - src/pool/uma/clients/erc20/client.e2e.ts | 26 - src/pool/uma/clients/erc20/client.ts | 67 -- src/pool/uma/clients/erc20/index.ts | 1 - src/pool/uma/clients/index.ts | 1 - src/pool/uma/index.ts | 33 - src/pool/uma/oracle/index.ts | 2 - src/pool/uma/oracle/utils.ts | 38 - src/pool/uma/utils.test.ts | 24 - src/pool/uma/utils.ts | 53 -- yarn.lock | 5 - 19 files changed, 1281 deletions(-) delete mode 100644 src/pool/TransactionManager.ts delete mode 100644 src/pool/index.ts delete mode 100644 src/pool/poolClient.ts delete mode 100644 src/pool/uma/across/constants.ts delete mode 100644 src/pool/uma/across/index.ts delete mode 100644 src/pool/uma/across/transactionManager.ts delete mode 100644 src/pool/uma/clients/erc20/README.md delete mode 100644 src/pool/uma/clients/erc20/client.e2e.ts delete mode 100644 src/pool/uma/clients/erc20/client.ts delete mode 100644 src/pool/uma/clients/erc20/index.ts delete mode 100644 src/pool/uma/clients/index.ts delete mode 100644 src/pool/uma/index.ts delete mode 100644 src/pool/uma/oracle/index.ts delete mode 100644 src/pool/uma/oracle/utils.ts delete mode 100644 src/pool/uma/utils.test.ts delete mode 100644 src/pool/uma/utils.ts diff --git a/package.json b/package.json index e0cb587f2..fef64da8a 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,6 @@ "@solana/web3.js": "^1.31.0", "@types/mocha": "^10.0.1", "@types/node": "^24.3.0", - "@uma/contracts-node": "^0.4.26", "arweave": "^1.14.4", "async": "^3.2.5", "axios": "^0.27.2", diff --git a/src/index.ts b/src/index.ts index a3672fc88..fd0b395b1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,6 @@ export * as arch from "./arch"; export * as addressAggregator from "./addressAggregator"; export * as lpFeeCalculator from "./lpFeeCalculator"; -export * as pool from "./pool"; export * as relayFeeCalculator from "./relayFeeCalculator"; export * as utils from "./utils"; export * as contracts from "./contracts"; diff --git a/src/pool/TransactionManager.ts b/src/pool/TransactionManager.ts deleted file mode 100644 index c5f8a1883..000000000 --- a/src/pool/TransactionManager.ts +++ /dev/null @@ -1,73 +0,0 @@ -import assert from "assert"; -import { Signer } from "ethers"; -import { TransactionRequest, TransactionReceipt } from "@ethersproject/abstract-provider"; - -function makeKey(tx: TransactionRequest) { - return JSON.stringify( - Object.entries(tx).map(([key, value]) => { - return [key, (value || "").toString()]; - }) - ); -} - -type Config = { - confirmations?: number; -}; -export type Emit = (event: string, key: string, data: TransactionReceipt | string | TransactionRequest | Error) => void; -export default (config: Config, signer: Signer, emit: Emit = () => null) => { - assert(signer.provider, "signer requires a provider, use signer.connect(provider)"); - const { confirmations = 3 } = config; - const requests = new Map(); - const submissions = new Map(); - const mined = new Map(); - function request(unsignedTx: TransactionRequest) { - // this no longer calls signer.populateTransaction, to allow metamask to fill in missing details instead - // use overrides if you want to manually fill in other tx details, including the overrides.customData field. - const populated = unsignedTx; - const key = makeKey(populated); - assert(!requests.has(key), "Transaction already in progress"); - requests.set(key, populated); - return key; - } - async function processRequest(key: string) { - const request = requests.get(key); - assert(request, "invalid request"); - // always delete request, it should only be submitted once - requests.delete(key); - try { - const sent = await signer.sendTransaction(request); - submissions.set(key, sent.hash); - emit("submitted", key, sent.hash); - } catch (err) { - emit("error", key, err as Error); - } - } - async function processSubmission(key: string) { - const hash = submissions.get(key); - assert(hash, "invalid submission"); - assert(signer.provider, "signer requires a provider, use signer.connect(provider)"); - // we look for this transaction, but it may never find it if its sped up - const receipt = await signer.provider.getTransactionReceipt(hash).catch(() => undefined); - if (receipt == null) return; - if (receipt.confirmations < confirmations) return; - submissions.delete(key); - mined.set(key, receipt); - emit("mined", key, receipt); - } - function isMined(key: string) { - return mined.get(key); - } - async function update() { - for (const key of Object.keys(requests)) { - await processRequest(key); - } - for (const key of Object.keys(submissions)) { - await processSubmission(key); - } - } - return { - request, - isMined, - update, - }; -}; diff --git a/src/pool/index.ts b/src/pool/index.ts deleted file mode 100644 index 0de253091..000000000 --- a/src/pool/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./poolClient"; diff --git a/src/pool/poolClient.ts b/src/pool/poolClient.ts deleted file mode 100644 index c17ed491c..000000000 --- a/src/pool/poolClient.ts +++ /dev/null @@ -1,849 +0,0 @@ -import assert from "assert"; -import * as uma from "./uma"; -import { - bnZero, - toBNWei, - fixedPointAdjustment, - calcPeriodicCompoundInterest, - calcApr, - BigNumber, - BigNumberish, - fromWei, -} from "../utils"; -import { ethers, Signer } from "ethers"; -import type { Overrides } from "@ethersproject/contracts"; -import { TransactionRequest, TransactionReceipt, Log } from "@ethersproject/abstract-provider"; -import { Provider, Block } from "@ethersproject/providers"; -import set from "lodash/set"; -import get from "lodash/get"; -import has from "lodash/has"; -import { calculateInstantaneousRate, RateModel } from "../lpFeeCalculator"; -import { hubPool, acrossConfigStore } from "../contracts"; -import { - AcceleratingDistributor, - AcceleratingDistributor__factory, - MerkleDistributor, - MerkleDistributor__factory, - TypedEvent, -} from "../typechain"; - -const { erc20 } = uma.clients; -const { loop } = uma.utils; -const { TransactionManager } = uma.across; -const { SECONDS_PER_YEAR, DEFAULT_BLOCK_DELTA } = uma.across.constants; -const { AddressZero } = ethers.constants; - -export type { Provider }; - -export type Awaited = T extends PromiseLike ? U : T; - -export type Config = { - chainId?: number; - hubPoolAddress: string; - acceleratingDistributorAddress: string; - merkleDistributorAddress: string; - wethAddress: string; - configStoreAddress: string; - confirmations?: number; - blockDelta?: number; - hasArchive?: boolean; - hubPoolStartBlock?: number; -}; -export type Dependencies = { - provider: Provider; -}; -export type StakeData = { - cumulativeBalance: BigNumber; - amountAirdropped: BigNumber; -}; -export type Pool = { - address: string; - totalPoolSize: string; - l1Token: string; - lpToken: string; - liquidReserves: string; - exchangeRateCurrent: string; - exchangeRatePrevious: string; - estimatedApy: string; - estimatedApr: string; - blocksElapsed: number; - secondsElapsed: number; - utilizedReserves: string; - projectedApr: string; - liquidityUtilizationCurrent: string; -}; -export type User = { - address: string; - poolAddress: string; - lpTokens: string; - positionValue: string; - totalDeposited: string; - feesEarned: string; -}; -export type Transaction = { - id: string; - state: "requested" | "submitted" | "mined" | "error"; - toAddress: string; - fromAddress: string; - type: "Add Liquidity" | "Remove Liquidity"; - description: string; - request?: TransactionRequest; - hash?: string; - receipt?: TransactionReceipt; - error?: Error; -}; -export type Token = { - decimals: string; - symbol: string; - name: string; -}; -export type State = { - pools: Record; - users: Record>; - transactions: Record; - error?: Error; -}; -export type EmitState = (path: string[], data: Pool | User | Transaction | Error) => void; -export type PooledToken = { - // LP token given to LPs of a specific L1 token. - lpToken: string; - // True if accepting new LP's. - isEnabled: boolean; - // Timestamp of last LP fee update. - lastLpFeeUpdate: number; - // Number of LP funds sent via pool rebalances to SpokePools and are expected to be sent - // back later. - utilizedReserves: BigNumber; - // Number of LP funds held in contract less utilized reserves. - liquidReserves: BigNumber; - // Number of LP funds reserved to pay out to LPs as fees. - undistributedLpFees: BigNumber; -}; - -class PoolState { - constructor( - private contract: hubPool.Instance, - private address: string - ) {} - public async read(l1Token: string, latestBlock: number, previousBlock?: number) { - // typechain does not have complete types for call options, so we have to cast blockTag to any - const exchangeRatePrevious = await this.exchangeRateAtBlock(l1Token, previousBlock || latestBlock - 1); - - const exchangeRateCurrent = await this.contract.callStatic.exchangeRateCurrent(l1Token); - - const pooledToken: PooledToken = await this.contract.pooledTokens(l1Token); - const liquidityUtilizationCurrent: BigNumber = await this.contract.callStatic.liquidityUtilizationCurrent(l1Token); - - return { - address: this.address, - l1Token, - latestBlock, - previousBlock, - exchangeRatePrevious, - exchangeRateCurrent, - liquidityUtilizationCurrent, - ...pooledToken, - }; - } - public exchangeRateAtBlock(l1Token: string, blockTag: number) { - return this.contract.callStatic.exchangeRateCurrent(l1Token, { blockTag }); - } -} - -type EventIdParams = { blockNumber: number; transactionIndex: number; logIndex: number }; -export class PoolEventState { - private seen = new Set(); - private iface: ethers.utils.Interface; - // maintain ordered unique list of events so we can calculate state - private events: uma.SerializableEvent[] = []; - constructor( - private contract: hubPool.Instance, - private startBlock = 0 - ) { - this.iface = new ethers.utils.Interface(hubPool.Factory.abi); - } - private makeId = (params: EventIdParams): string => { - return uma.oracle.utils.eventKey(params); - }; - hasEvent(params: EventIdParams): boolean { - return this.seen.has(this.makeId(params)); - } - private addEvent(params: EventIdParams): void { - this.seen.add(this.makeId(params)); - } - private filterSeen = (params: EventIdParams): boolean => { - const seen = this.hasEvent(params); - if (!seen) this.addEvent(params); - return !seen; - }; - private processEvent = (event: uma.SerializableEvent): void => { - if (!this.filterSeen(event)) return; - this.events = uma.oracle.utils.insertOrderedAscending(this.events, event, this.makeId); - }; - private processEvents = (events: Array): void => { - events.forEach(this.processEvent); - }; - - public async read(endBlock: number, l1TokenAddress?: string, userAddress?: string): Promise { - const events = await Promise.all([ - ...(await this.contract.queryFilter( - this.contract.filters.LiquidityAdded(l1TokenAddress, undefined, undefined, userAddress), - this.startBlock, - endBlock - )), - ...(await this.contract.queryFilter( - this.contract.filters.LiquidityRemoved(l1TokenAddress, undefined, undefined, userAddress), - this.startBlock, - endBlock - )), - ]); - this.processEvents(events); - return hubPool.getEventState(this.events); - } - makeEventFromLog = (log: Log): uma.SerializableEvent => { - const description = this.iface.parseLog(log); - return { - ...log, - ...description, - event: description.name, - eventSignature: description.signature, - }; - }; - getL1TokenFromReceipt(receipt: TransactionReceipt): string { - const events = receipt.logs - .filter((log) => ethers.utils.getAddress(log.address) === ethers.utils.getAddress(this.contract.address)) - .map(this.makeEventFromLog); - - // save these events - this.processEvents(events); - // only process token receipt events, because we just want the l1 token involved with this transfer - const eventState = hubPool.getEventState(events); - // event state is keyed by l1token address - const l1Tokens = Object.keys(eventState); - assert(l1Tokens.length, "Token not found from events"); - assert(l1Tokens.length === 1, "Multiple tokens found from events"); - return l1Tokens[0]; - } - readTxReceipt(receipt: TransactionReceipt): hubPool.EventState { - const events = receipt.logs - .filter((log) => ethers.utils.getAddress(log.address) === ethers.utils.getAddress(this.contract.address)) - .map(this.makeEventFromLog); - this.processEvents(events); - return hubPool.getEventState(this.events); - } -} - -class UserState { - private seen = new Set(); - private events: uma.clients.erc20.Transfer[] = []; - constructor( - private contract: uma.clients.erc20.Instance, - private userAddress: string, - private startBlock = 0, - private acceleratingDistributorContractAddress = "" - ) {} - private makeId(params: EventIdParams): string { - return uma.oracle.utils.eventKey(params); - } - hasEvent(params: EventIdParams): boolean { - return this.seen.has(this.makeId(params)); - } - private addEvent(params: EventIdParams): void { - this.seen.add(this.makeId(params)); - } - private filterSeen = (params: EventIdParams): boolean => { - const seen = this.hasEvent(params); - if (!seen) this.addEvent(params); - return !seen; - }; - /** - * readEvents. Fetch and cache events for the user. - * - * @param {number} endBlock - */ - public async readEvents(endBlock: number): Promise { - if (endBlock <= this.startBlock) return []; - const { userAddress } = this; - const events: TypedEvent< - [string, string, BigNumber] & { - from: string; - to: string; - value: BigNumber; - } - >[] = ( - await Promise.all([ - ...(await this.contract.queryFilter( - this.contract.filters.Transfer(userAddress, undefined), - this.startBlock, - endBlock - )), - ...(await this.contract.queryFilter( - this.contract.filters.Transfer(undefined, userAddress), - this.startBlock, - endBlock - )), - ]) - ) - // filter out events we have seen - .filter(this.filterSeen) - // filter out mint/burn transfers - .filter( - (event: uma.clients.erc20.Transfer) => - // ignore mint events - event.args.from !== AddressZero && - // ignore burn events - event.args.to !== AddressZero && - // ignore AD transfer events in - event.args.to !== this.acceleratingDistributorContractAddress && - // ignore AD transfer events out - event.args.from !== this.acceleratingDistributorContractAddress && - // ignore self transfer events - event.args.from !== event.args.to - ) - .flat(); - - this.events = this.events.concat(events).sort((a, b) => { - if (a.blockNumber !== b.blockNumber) return a.blockNumber - b.blockNumber; - if (a.transactionIndex !== b.transactionIndex) return a.transactionIndex - b.transactionIndex; - if (a.logIndex !== b.logIndex) return a.logIndex - b.logIndex; - throw new Error("Duplicate events at tx hash: " + a.transactionHash); - }); - // ethers queries are inclusive [start,end] unless start === end, then exclusive (start,end). we increment to make sure we dont see same event twice - this.startBlock = endBlock + 1; - return this.events; - } - /** - * read. Reads the state for the user, building state from events as well as contract calls. - * - * @param {number} endBlock - */ - public async read(endBlock: number) { - const { userAddress } = this; - const transferEvents = await this.readEvents(endBlock); - const state = uma.clients.erc20.getEventState(transferEvents); - const balanceTransferred = state?.balances?.[userAddress] || "0"; - return { - transferEvents, - balanceTransferred, - address: userAddress, - balanceOf: await this.contract.balanceOf(userAddress), - }; - } -} - -export function calculateRemoval(amountWei: BigNumber, percentWei: BigNumber) { - const receive = amountWei.mul(percentWei).div(fixedPointAdjustment); - const remain = amountWei.sub(receive); - return { - receive: receive.toString(), - remain: remain.toString(), - }; -} -// params here mimic the user object type -export function previewRemoval( - values: { positionValue: BigNumberish; feesEarned: BigNumberish; totalDeposited: BigNumberish }, - percentFloat: number -) { - const percentWei = toBNWei(percentFloat); - return { - position: { - ...calculateRemoval(BigNumber.from(values.totalDeposited), percentWei), - }, - fees: { - ...calculateRemoval(BigNumber.from(values.feesEarned), percentWei), - }, - total: { - ...calculateRemoval(BigNumber.from(values.positionValue), percentWei), - }, - }; -} -function joinUserState( - poolState: Pool, - tokenEventState: hubPool.TokenEventState, - userState: Awaited>, - transferValue = bnZero, - cumulativeStakeBalance = bnZero, - cumulativeStakeClaimBalance = bnZero -): User { - const positionValue = BigNumber.from(poolState.exchangeRateCurrent) - .mul(userState.balanceOf.add(cumulativeStakeBalance)) - .div(fixedPointAdjustment); - const totalDeposited = BigNumber.from(tokenEventState?.tokenBalances[userState.address] || "0").add( - cumulativeStakeClaimBalance - ); - const feesEarned = positionValue.sub(totalDeposited.add(transferValue)); - return { - address: userState.address, - poolAddress: poolState.address, - lpTokens: userState.balanceOf.toString(), - positionValue: positionValue.toString(), - totalDeposited: totalDeposited.toString(), - feesEarned: feesEarned.toString(), - }; -} -function joinPoolState( - poolState: Awaited>, - latestBlock: Block, - previousBlock: Block, - rateModel?: RateModel -): Pool { - const totalPoolSize = poolState.liquidReserves.add(poolState.utilizedReserves); - const secondsElapsed = latestBlock.timestamp - previousBlock.timestamp; - const blocksElapsed = latestBlock.number - previousBlock.number; - const exchangeRatePrevious = poolState.exchangeRatePrevious.toString(); - const exchangeRateCurrent = poolState.exchangeRateCurrent.toString(); - const liquidityUtilizationCurrent = poolState.liquidityUtilizationCurrent.toString(); - - const estimatedApy = calcPeriodicCompoundInterest( - exchangeRatePrevious, - exchangeRateCurrent, - secondsElapsed, - SECONDS_PER_YEAR - ); - const estimatedApr = calcApr(exchangeRatePrevious, exchangeRateCurrent, secondsElapsed, SECONDS_PER_YEAR); - let projectedApr = ""; - - if (rateModel) { - projectedApr = fromWei( - calculateInstantaneousRate(rateModel, liquidityUtilizationCurrent) - .mul(liquidityUtilizationCurrent) - .div(fixedPointAdjustment) - ); - } - - return { - address: poolState.address, - totalPoolSize: totalPoolSize.toString(), - l1Token: poolState.l1Token, - lpToken: poolState.lpToken, - liquidReserves: poolState.liquidReserves.toString(), - exchangeRateCurrent: poolState.exchangeRateCurrent.toString(), - exchangeRatePrevious: poolState.exchangeRatePrevious.toString(), - estimatedApy, - estimatedApr, - blocksElapsed, - secondsElapsed, - projectedApr, - utilizedReserves: poolState.utilizedReserves.toString(), - liquidityUtilizationCurrent, - }; -} -export class ReadPoolClient { - private poolState: PoolState; - private contract: hubPool.Instance; - constructor( - private address: string, - private provider: Provider - ) { - this.contract = hubPool.connect(address, this.provider); - this.poolState = new PoolState(this.contract, this.address); - } - public read(tokenAddress: string, latestBlock: number) { - return this.poolState.read(tokenAddress, latestBlock); - } -} -export function validateWithdraw(pool: Pool, user: User, lpTokenAmount: BigNumberish) { - const l1TokensToReturn = BigNumber.from(lpTokenAmount).mul(pool.exchangeRateCurrent).div(fixedPointAdjustment); - assert(BigNumber.from(l1TokensToReturn).gt("0"), "Must withdraw amount greater than 0"); - assert(BigNumber.from(lpTokenAmount).lte(user.lpTokens), "You cannot withdraw more than you have"); - return { lpTokenAmount, l1TokensToReturn: l1TokensToReturn.toString() }; -} - -export class Client { - private transactionManagers: Record> = {}; - private hubPool: hubPool.Instance; - private acceleratingDistributor: AcceleratingDistributor; - private merkleDistributor: MerkleDistributor; - public readonly state: State = { pools: {}, users: {}, transactions: {} }; - private poolEvents: PoolEventState; - private erc20s: Record = {}; - private intervalStarted = false; - private configStoreClient: acrossConfigStore.Client; - private exchangeRateTable: Record> = {}; - private userServices: Record> = {}; - constructor( - public readonly config: Config, - public readonly deps: Dependencies, - private emit: EmitState - ) { - config.chainId = config.chainId || 1; - this.hubPool = this.createHubPoolContract(deps.provider); - this.acceleratingDistributor = this.createAcceleratingDistributorContract(deps.provider); - this.merkleDistributor = this.createMerkleDistributorContract(deps.provider); - this.poolEvents = new PoolEventState(this.hubPool, this.config.hubPoolStartBlock); - this.configStoreClient = new acrossConfigStore.Client(config.configStoreAddress, deps.provider); - } - public getOrCreateErc20Contract(address: string): uma.clients.erc20.Instance { - if (this.erc20s[address]) return this.erc20s[address]; - this.erc20s[address] = erc20.connect(address, this.deps.provider); - return this.erc20s[address]; - } - public getOrCreatePoolContract(): hubPool.Instance { - return this.hubPool; - } - public createHubPoolContract(signerOrProvider: Signer | Provider): hubPool.Instance { - return hubPool.connect(this.config.hubPoolAddress, signerOrProvider); - } - private getOrCreatePoolEvents() { - return this.poolEvents; - } - public createAcceleratingDistributorContract(signerOrProvider: Signer | Provider): AcceleratingDistributor { - return AcceleratingDistributor__factory.connect(this.config.acceleratingDistributorAddress, signerOrProvider); - } - public createMerkleDistributorContract(signerOrProvider: Signer | Provider): MerkleDistributor { - return MerkleDistributor__factory.connect(this.config.merkleDistributorAddress, signerOrProvider); - } - public getOrCreateAcceleratingDistributorContract(): AcceleratingDistributor { - return this.acceleratingDistributor; - } - public getOrCreateMerkleDistributorContract(): MerkleDistributor { - return this.merkleDistributor; - } - private getOrCreateUserService(userAddress: string, tokenAddress: string) { - if (has(this.userServices, [tokenAddress, userAddress])) return get(this.userServices, [tokenAddress, userAddress]); - const erc20Contract = this.getOrCreateErc20Contract(tokenAddress); - const userService = new UserState(erc20Contract, userAddress); - // this service is stateful now, so needs to be cached - set(this.userServices, [tokenAddress, userAddress], userService); - return userService; - } - private updateExchangeRateTable( - l1TokenAddress: string, - exchangeRateTable: Record - ): Record { - if (!this.exchangeRateTable[l1TokenAddress]) this.exchangeRateTable[l1TokenAddress] = {}; - this.exchangeRateTable[l1TokenAddress] = { ...this.exchangeRateTable[l1TokenAddress], ...exchangeRateTable }; - return this.exchangeRateTable[l1TokenAddress]; - } - async resolveStakingData( - lpToken: string, - l1TokenAddress: string, - userState: Awaited> - ): Promise { - assert(this.config.acceleratingDistributorAddress, "Must have the accelerating distributor address"); - assert(this.config.merkleDistributorAddress, "Must have the merkle distributor address"); - - // Define the contracts we need to interact with. - const acceleratingDistributorContract = this.getOrCreateAcceleratingDistributorContract(); - const merkleDistributorContract = this.getOrCreateMerkleDistributorContract(); - const poolContract = this.getOrCreatePoolContract(); - - // Get the list of all claims made by the user. - const claimList = await merkleDistributorContract.queryFilter( - merkleDistributorContract.filters.Claimed(undefined, undefined, userState.address, undefined, undefined, lpToken) - ); - - // Calculate the total amount of LP tokens claimed by the user from the merkle - // distributor contract with the exchange rate at the time of the claim. - const amountOfLPClaimed = ( - await Promise.all( - claimList.map(async (claim) => - claim.args.amount.mul( - await poolContract.callStatic.exchangeRateCurrent(l1TokenAddress, { blockTag: claim.blockNumber }) - ) - ) - ) - ).reduce((prev, acc) => acc.add(prev), bnZero); - - // Get the cumulative balance of the user from the accelerating distributor contract. - const { cumulativeBalance } = await acceleratingDistributorContract.getUserStake(lpToken, userState.address); - - return { - cumulativeBalance, - amountAirdropped: amountOfLPClaimed, - }; - } - - // calculates the value of each LP token transfer at the block it was sent. this only works if we have archive node - async calculateLpTransferValue(l1TokenAddress: string, userState: Awaited>) { - assert(this.config.hasArchive, "Can only calculate historical lp values with archive node"); - const contract = this.getOrCreatePoolContract(); - const pool = new PoolState(contract, this.config.hubPoolAddress); - const blockNumbers = userState.transferEvents - .map((x) => x.blockNumber) - // we are going to lookup exchange rates for block numbers only if we dont already have it - // its possible these values do not exist, so to prevent crashing do optional chaining - .filter((blockNumber) => !this.exchangeRateTable?.[l1TokenAddress]?.[blockNumber]); - - // new exchange rate lookups - const exchangeRateTable = this.updateExchangeRateTable( - l1TokenAddress, - Object.fromEntries( - await Promise.all( - blockNumbers.map(async (blockNumber) => { - return [blockNumber, await pool.exchangeRateAtBlock(l1TokenAddress, blockNumber)]; - }) - ) - ) - ); - - return userState.transferEvents.reduce((result, transfer) => { - const exchangeRate = exchangeRateTable[transfer.blockNumber]; - if (transfer.args.to === userState.address) { - return result.add(transfer.args.value.mul(exchangeRate).div(fixedPointAdjustment)); - } - if (transfer.args.from === userState.address) { - return result.sub(transfer.args.value.mul(exchangeRate).div(fixedPointAdjustment)); - } - // we make sure to filter out any transfers where to/from is the same user - return result; - }, bnZero); - } - private getOrCreateTransactionManager(signer: Signer, address: string) { - if (this.transactionManagers[address]) return this.transactionManagers[address]; - const txman = TransactionManager({ confirmations: this.config.confirmations }, signer, (event, id, data) => { - if (event === "submitted") { - this.state.transactions[id].state = event; - this.state.transactions[id].hash = data as string; - this.emit(["transactions", id], { ...this.state.transactions[id] }); - } - if (event === "mined") { - const txReceipt = data as TransactionReceipt; - this.state.transactions[id].state = event; - this.state.transactions[id].receipt = txReceipt; - this.emit(["transactions", id], { ...this.state.transactions[id] }); - // trigger pool and user update for a known mined transaction - const tx = this.state.transactions[id]; - this.updateUserWithTransaction(tx.fromAddress, txReceipt).catch((err) => { - this.emit(["error"], err); - }); - } - if (event === "error") { - this.state.transactions[id].state = event; - this.state.transactions[id].error = data as Error; - this.emit(["transactions", id], { ...this.state.transactions[id] }); - } - }); - this.transactionManagers[address] = txman; - return txman; - } - async addEthLiquidity(signer: Signer, l1TokenAmount: BigNumberish, overrides: Overrides = {}) { - const { hubPoolAddress, wethAddress: l1Token } = this.config; - const userAddress = await signer.getAddress(); - const contract = this.getOrCreatePoolContract(); - const txman = this.getOrCreateTransactionManager(signer, userAddress); - - // dont allow override value here - const request = await contract.populateTransaction.addLiquidity(l1Token, l1TokenAmount, { - ...overrides, - value: l1TokenAmount, - }); - const id = txman.request(request); - - this.state.transactions[id] = { - id, - state: "requested", - toAddress: hubPoolAddress, - fromAddress: userAddress, - type: "Add Liquidity", - description: "Adding ETH to pool", - request, - }; - this.emit(["transactions", id], { ...this.state.transactions[id] }); - await txman.update(); - return id; - } - async addTokenLiquidity(signer: Signer, l1Token: string, l1TokenAmount: BigNumberish, overrides: Overrides = {}) { - const { hubPoolAddress } = this.config; - const userAddress = await signer.getAddress(); - const contract = this.getOrCreatePoolContract(); - const txman = this.getOrCreateTransactionManager(signer, userAddress); - - const request = await contract.populateTransaction.addLiquidity(l1Token, l1TokenAmount, overrides); - const id = await txman.request(request); - - this.state.transactions[id] = { - id, - state: "requested", - toAddress: hubPoolAddress, - fromAddress: userAddress, - type: "Add Liquidity", - description: "Adding Tokens to pool", - request, - }; - - this.emit(["transactions", id], { ...this.state.transactions[id] }); - await txman.update(); - return id; - } - async validateWithdraw(l1Token: string, userAddress: string, lpAmount: BigNumberish) { - await this.updatePool(l1Token); - const poolState = this.getPoolState(l1Token); - if (!this.hasUserState(l1Token, userAddress)) { - await this.updateUser(l1Token, userAddress); - } - const userState = this.getUserState(poolState.l1Token, userAddress); - return validateWithdraw(poolState, userState, lpAmount); - } - async removeTokenLiquidity(signer: Signer, l1Token: string, lpTokenAmount: BigNumberish, overrides: Overrides = {}) { - const { hubPoolAddress } = this.config; - const userAddress = await signer.getAddress(); - await this.validateWithdraw(l1Token, userAddress, lpTokenAmount); - const contract = this.getOrCreatePoolContract(); - const txman = this.getOrCreateTransactionManager(signer, userAddress); - - const request = await contract.populateTransaction.removeLiquidity(l1Token, lpTokenAmount, false, overrides); - const id = await txman.request(request); - - this.state.transactions[id] = { - id, - state: "requested", - toAddress: hubPoolAddress, - fromAddress: userAddress, - type: "Remove Liquidity", - description: "Withdrawing Tokens from pool", - request, - }; - - this.emit(["transactions", id], { ...this.state.transactions[id] }); - await txman.update(); - return id; - } - async removeEthliquidity(signer: Signer, lpTokenAmount: BigNumberish, overrides: Overrides = {}) { - const { hubPoolAddress, wethAddress: l1Token } = this.config; - const userAddress = await signer.getAddress(); - await this.validateWithdraw(l1Token, userAddress, lpTokenAmount); - const contract = this.getOrCreatePoolContract(); - const txman = this.getOrCreateTransactionManager(signer, userAddress); - - const request = await contract.populateTransaction.removeLiquidity(l1Token, lpTokenAmount, true, overrides); - const id = await txman.request(request); - - this.state.transactions[id] = { - id, - state: "requested", - toAddress: hubPoolAddress, - fromAddress: userAddress, - type: "Remove Liquidity", - description: "Withdrawing Eth from pool", - request, - }; - this.emit(["transactions", id], { ...this.state.transactions[id] }); - await txman.update(); - return id; - } - getPoolState(l1TokenAddress: string): Pool { - return this.state.pools[l1TokenAddress]; - } - hasPoolState(l1TokenAddress: string): boolean { - return Boolean(this.state.pools[l1TokenAddress]); - } - setUserState(l1TokenAddress: string, userAddress: string, state: User): User { - set(this.state, ["users", userAddress, l1TokenAddress], state); - return state; - } - getUserState(l1TokenAddress: string, userAddress: string): User { - return get(this.state, ["users", userAddress, l1TokenAddress]); - } - hasUserState(l1TokenAddress: string, userAddress: string): boolean { - return has(this.state, ["users", userAddress, l1TokenAddress]); - } - hasTxState(id: string): boolean { - return has(this.state, ["transactions", id]); - } - getTxState(id: string): Transaction { - return get(this.state, ["transactions", id]); - } - private async updateAndEmitUser( - userState: Awaited>, - poolState: Pool, - poolEventState: hubPool.EventState - ): Promise { - const { l1Token: l1TokenAddress, lpToken } = poolState; - const { address: userAddress } = userState; - const transferValue = this.config.hasArchive - ? await this.calculateLpTransferValue(l1TokenAddress, userState) - : bnZero; - const stakeData = await this.resolveStakingData(lpToken, l1TokenAddress, userState); - const tokenEventState = poolEventState[l1TokenAddress]; - const newUserState = this.setUserState( - l1TokenAddress, - userAddress, - joinUserState( - poolState, - tokenEventState, - userState, - transferValue, - stakeData.cumulativeBalance, - stakeData.amountAirdropped - ) - ); - this.emit(["users", userAddress, l1TokenAddress], newUserState); - } - private async updateUserWithTransaction(userAddress: string, txReceipt: TransactionReceipt): Promise { - const latestBlock = await this.deps.provider.getBlock("latest"); - const getPoolEventState = this.getOrCreatePoolEvents(); - const l1TokenAddress = getPoolEventState.getL1TokenFromReceipt(txReceipt); - await this.updatePool(l1TokenAddress, latestBlock); - const poolState = this.getPoolState(l1TokenAddress); - const poolEventState = getPoolEventState.readTxReceipt(txReceipt); - - const lpToken = poolState.lpToken; - const getUserState = this.getOrCreateUserService(userAddress, lpToken); - const userState = await getUserState.read(latestBlock.number); - - await this.updateAndEmitUser(userState, poolState, poolEventState); - } - async updateUser(userAddress: string, l1TokenAddress: string): Promise { - const latestBlock = await this.deps.provider.getBlock("latest"); - await this.updatePool(l1TokenAddress, latestBlock); - - const poolState = this.getPoolState(l1TokenAddress); - const lpToken = poolState.lpToken; - const getPoolEventState = this.getOrCreatePoolEvents(); - const poolEventState = await getPoolEventState.read(latestBlock.number, l1TokenAddress, userAddress); - - const getUserState = this.getOrCreateUserService(userAddress, lpToken); - const userState = await getUserState.read(latestBlock.number); - - await this.updateAndEmitUser(userState, poolState, poolEventState); - } - async updatePool(l1TokenAddress: string, overrideLatestBlock?: Block): Promise { - // default to 100 block delta unless specified otherwise in config - const { blockDelta = DEFAULT_BLOCK_DELTA } = this.config; - const contract = this.getOrCreatePoolContract(); - const pool = new PoolState(contract, this.config.hubPoolAddress); - const latestBlock = overrideLatestBlock || (await this.deps.provider.getBlock("latest")); - const previousBlock = await this.deps.provider.getBlock(latestBlock.number - blockDelta); - const state = await pool.read(l1TokenAddress, latestBlock.number, previousBlock.number); - - let rateModel: RateModel | undefined = undefined; - try { - // Use the default rate model (i.e. not any of the routeRateModels to project the Pool's APR). This assumes - // that the default rate model is the most often used, but this may change in future if many different - // route rate models are set. - rateModel = await this.configStoreClient.getRateModel(l1TokenAddress); - } catch (err) { - // we could swallow this error or just log it since getting the rate model is optional, - // but we will just emit it to the caller and let them decide what to do with it. - this.emit(["error"], err as unknown as Error); - } - - this.state.pools[l1TokenAddress] = joinPoolState(state, latestBlock, previousBlock, rateModel); - this.emit(["pools", l1TokenAddress], this.state.pools[l1TokenAddress]); - } - async updateTransactions(): Promise { - for (const txMan of Object.values(this.transactionManagers)) { - try { - await txMan.update(); - } catch (err) { - this.emit(["error"], err as unknown as Error); - } - } - } - // starts transaction checking intervals, defaults to 30 seconds - startInterval(delayMs = 30000) { - assert(!this.intervalStarted, "Interval already started, try stopping first"); - this.intervalStarted = true; - loop(async () => { - assert(this.intervalStarted, "HubPool Interval Stopped"); - await this.updateTransactions(); - }, delayMs).catch((err) => { - this.emit(["error"], err); - }); - } - // starts transaction checking intervals - stopInterval() { - this.intervalStarted = false; - } -} diff --git a/src/pool/uma/across/constants.ts b/src/pool/uma/across/constants.ts deleted file mode 100644 index a67a8bad4..000000000 --- a/src/pool/uma/across/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const SECONDS_PER_YEAR = 31557600; // based on 365.25 days per year -export const DEFAULT_BLOCK_DELTA = 10; // look exchange rate up based on 10 block difference by default diff --git a/src/pool/uma/across/index.ts b/src/pool/uma/across/index.ts deleted file mode 100644 index 0a1263151..000000000 --- a/src/pool/uma/across/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as TransactionManager } from "./transactionManager"; -export * as constants from "./constants"; diff --git a/src/pool/uma/across/transactionManager.ts b/src/pool/uma/across/transactionManager.ts deleted file mode 100644 index 07a452404..000000000 --- a/src/pool/uma/across/transactionManager.ts +++ /dev/null @@ -1,73 +0,0 @@ -import assert from "assert"; -import { Signer } from "ethers"; -import { TransactionRequest, TransactionReceipt } from "@ethersproject/abstract-provider"; - -function makeKey(tx: TransactionRequest) { - return JSON.stringify( - Object.entries(tx).map(([key, value]) => { - return [key, (value || "").toString()]; - }) - ); -} - -type Config = { - confirmations?: number; -}; -export type Emit = (event: string, key: string, data: TransactionReceipt | string | TransactionRequest | Error) => void; -export default (config: Config, signer: Signer, emit: Emit = () => null) => { - assert(signer.provider, "signer requires a provider, use signer.connect(provider)"); - const { confirmations = 3 } = config; - const requests = new Map(); - const submissions = new Map(); - const mined = new Map(); - function request(unsignedTx: TransactionRequest) { - // this no longer calls signer.populateTransaction, to allow metamask to fill in missing details instead - // use overrides if you want to manually fill in other tx details, including the overrides.customData field. - const populated = unsignedTx; - const key = makeKey(populated); - assert(!requests.has(key), "Transaction already in progress"); - requests.set(key, populated); - return key; - } - async function processRequest(key: string) { - const request = requests.get(key); - assert(request, "invalid request"); - // always delete request, it should only be submitted once - requests.delete(key); - try { - const sent = await signer.sendTransaction(request); - submissions.set(key, sent.hash); - emit("submitted", key, sent.hash); - } catch (err) { - emit("error", key, err as Error); - } - } - async function processSubmission(key: string) { - const hash = submissions.get(key); - assert(hash, "invalid submission"); - assert(signer.provider, "signer requires a provider, use signer.connect(provider)"); - // we look for this transaction, but it may never find it if its sped up - const receipt = await signer.provider.getTransactionReceipt(hash).catch(() => undefined); - if (receipt == null) return; - if (receipt.confirmations < confirmations) return; - submissions.delete(key); - mined.set(key, receipt); - emit("mined", key, receipt); - } - function isMined(key: string) { - return Promise.resolve(mined.get(key)); - } - async function update() { - for (const key of Array.from(requests.keys())) { - await processRequest(key); - } - for (const key of Array.from(submissions.keys())) { - await processSubmission(key); - } - } - return { - request, - isMined, - update, - }; -}; diff --git a/src/pool/uma/clients/erc20/README.md b/src/pool/uma/clients/erc20/README.md deleted file mode 100644 index b37f2c358..000000000 --- a/src/pool/uma/clients/erc20/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# UMA SDK ERC20 Client - -Client to interface with ERC20 style tokens, based on Ethers and typechain. - -## Usage - -```js -import {ethers} from 'ethers' - -// assume you have a url injected from env -const provider = new ethers.providers.WebSocketProvider(env.CUSTOM_NODE_URL) - -// get the contract instance -const erc20Address:string = // assume you have an emp address you want to connect to -const erc20Instance:uma.clients.erc20.Instance = uma.clients.erc20.connect(erc20Address,provider) - -// gets all emp events, see ethers queryFilter for details on constructing the query. -const events = await erc20Instance.queryFilter({}) - -// returns EventState, defined in the emp client. This can contain user balances as well as approval limits. -const state:uma.clients.erc20.EventState = uma.clients.erc20.getEventState(events) - -// Types -types {Transfer,Approval, Instance, EventState} = uma.clients.erc20 -// Transfer and Approval are event types -// Instance is the contract instance once connected -// Event state describes what the state reconstruction based on contract events will look like - -``` diff --git a/src/pool/uma/clients/erc20/client.e2e.ts b/src/pool/uma/clients/erc20/client.e2e.ts deleted file mode 100644 index 3d70a9d03..000000000 --- a/src/pool/uma/clients/erc20/client.e2e.ts +++ /dev/null @@ -1,26 +0,0 @@ -import assert from "assert"; -import { ethers } from "ethers"; -import * as Client from "./client"; - -const address = "0xeca82185adCE47f39c684352B0439f030f860318"; -// these require integration testing, skip for ci -describe("erc20", function () { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let events: any; - let client: Client.Instance; - test("inits", function () { - const provider = ethers.providers.getDefaultProvider(process.env.CUSTOM_NODE_URL); - client = Client.connect(address, provider); - assert.ok(client); - }); - test("getEventState between", async function () { - events = await client.queryFilter({}, 12477952, 12477952 + 1000); - assert.ok(events.length); - }); - test("getEventState", async function () { - const state = await Client.getEventState(events); - assert.ok(state.balances); - assert.ok(state.approvalsByOwner); - assert.ok(state.approvalsBySpender); - }); -}); diff --git a/src/pool/uma/clients/erc20/client.ts b/src/pool/uma/clients/erc20/client.ts deleted file mode 100644 index 7e492df0a..000000000 --- a/src/pool/uma/clients/erc20/client.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { ERC20Ethers, ERC20Ethers__factory } from "@uma/contracts-node"; -// eslint-disable-next-line no-restricted-imports -import { Event } from "ethers"; -import { set } from "lodash"; -import { Balances } from "../../utils"; -import type { Provider, GetEventType } from "../.."; - -export type Instance = ERC20Ethers; -const Factory = ERC20Ethers__factory; - -export function connect(address: string, provider: Provider): Instance { - return Factory.connect(address, provider); -} - -export interface EventState { - // any address that created a position, regardless of if they have closed it - balances?: Balances; - // approvals are keyed both ways here for ease of lookup by either owner or spender - approvalsByOwner?: { - [owner: string]: { - [spender: string]: { - amount: string; - }; - }; - }; - approvalsBySpender?: { - [spender: string]: { - [owner: string]: { - amount: string; - }; - }; - }; -} - -export type Transfer = GetEventType; -export type Approval = GetEventType; - -// takes all events and returns user balances and approvals -function reduceEvents(state: EventState = {}, event: Event): EventState { - switch (event.event) { - case "Transfer": { - const typedEvent = event as Transfer; - const { from, to, value } = typedEvent.args; - const balances = Balances(state.balances || {}); - balances.sub(from, value); - balances.add(to, value); - return { - ...state, - balances: balances.balances, - }; - } - case "Approval": { - const typedEvent = event as Approval; - const { owner, spender, value } = typedEvent.args; - set(state, ["approvalsByOwner", owner, spender], value.toString()); - set(state, ["approvalsBySpender", spender, owner], value.toString()); - return { - ...state, - }; - } - } - return state; -} - -export function getEventState(events: Event[], initialState: EventState = {}): EventState { - return events.reduce(reduceEvents, initialState); -} diff --git a/src/pool/uma/clients/erc20/index.ts b/src/pool/uma/clients/erc20/index.ts deleted file mode 100644 index 5ec76921e..000000000 --- a/src/pool/uma/clients/erc20/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./client"; diff --git a/src/pool/uma/clients/index.ts b/src/pool/uma/clients/index.ts deleted file mode 100644 index c71a01e0d..000000000 --- a/src/pool/uma/clients/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * as erc20 from "./erc20"; diff --git a/src/pool/uma/index.ts b/src/pool/uma/index.ts deleted file mode 100644 index 74d3bcc47..000000000 --- a/src/pool/uma/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -// eslint-disable-next-line no-restricted-imports -import { Contract, ethers, Event } from "ethers"; -import type { TypedEventFilterEthers as TypedEventFilter, TypedEventEthers as TypedEvent } from "@uma/contracts-node"; - -export * as across from "./across"; -export * as clients from "./clients"; -export * as oracle from "./oracle"; -export * as utils from "./utils"; - -export { type Provider } from "@ethersproject/providers"; - -type Result = ethers.utils.Result; - -export interface Callable { - (...args: unknown[]): unknown; -} - -export type SerializableEvent = Omit< - Event, - "decode" | "removeListener" | "getBlock" | "getTransaction" | "getTransactionReceipt" ->; - -// this convoluted type is meant to cast events to the types you need based on the contract and event name -// example: type NewContractRegistered = GetEventType; -// TODO: the any below is a hacky solution because some typechain types fail to resolve due to -// incompatible TypedEventFilter and TypedEvent types. This will be fixed by upgrading typechain -// to a version where Ethers events are exported as first class types. -export type GetEventType = ReturnType< - ContractType["filters"][EventName] extends Callable ? ContractType["filters"][EventName] : never -> extends TypedEventFilter - ? // eslint-disable-next-line @typescript-eslint/no-explicit-any - TypedEvent - : never; diff --git a/src/pool/uma/oracle/index.ts b/src/pool/uma/oracle/index.ts deleted file mode 100644 index 169aacdc2..000000000 --- a/src/pool/uma/oracle/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Minimal oracle module - only export utils needed by poolClient -export * as utils from "./utils"; diff --git a/src/pool/uma/oracle/utils.ts b/src/pool/uma/oracle/utils.ts deleted file mode 100644 index a210035dc..000000000 --- a/src/pool/uma/oracle/utils.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { BigNumberish } from "../../../utils"; -import sortedLastIndexBy from "lodash/sortedLastIndexBy"; - -/** - * eventKey. Make a unique and sortable identifier string for an event - * Used by poolClient.ts lines 166, 246 - * - * @param {Event} event - * @returns {string} - the unique id - */ -export function eventKey(event: { - blockNumber: BigNumberish; - transactionIndex: BigNumberish; - logIndex: BigNumberish; -}): string { - return [ - // we pad these because numbers of varying lengths will not sort correctly, ie "10" will incorrectly sort before "9", but "09" will be correct. - event.blockNumber.toString().padStart(16, "0"), - event.transactionIndex.toString().padStart(16, "0"), - event.logIndex?.toString().padStart(16, "0"), - // ~ is the last printable ascii char, so it does not interfere with sorting - ].join("~"); -} - -/** - * insertOrdered. Inserts items in an array maintaining sorted order, in this case lowest to highest. Does not check duplicates. - * Mainly used for caching all known events, in order of oldest to newest. - * Used by poolClient.ts line 181 - * - * @param {T[]} array - * @param {T} element - * @param {Function} orderBy - */ -export function insertOrderedAscending(array: T[], element: T, orderBy: (element: T) => string | number): T[] { - const index = sortedLastIndexBy(array, element, orderBy); - array.splice(index, 0, element); - return array; -} diff --git a/src/pool/uma/utils.test.ts b/src/pool/uma/utils.test.ts deleted file mode 100644 index 73210d18a..000000000 --- a/src/pool/uma/utils.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as utils from "./utils"; -import assert from "assert"; -test("Balances", function () { - const balances = utils.Balances(); - assert.ok(balances); - balances.create("a", "100"); - balances.create("b", "99"); - let result = balances.get("a"); - assert.equal(result, "100"); - result = balances.get("b"); - assert.equal(result, "99"); - - result = balances.sub("a", 1); - assert.equal(result, "99"); - - result = balances.sub("b", 1); - assert.equal(result, "98"); - - result = balances.add("b", 2); - assert.equal(result, "100"); - - const bals = balances.balances; - assert.equal(Object.keys(bals).length, 2); -}); diff --git a/src/pool/uma/utils.ts b/src/pool/uma/utils.ts deleted file mode 100644 index e865b33bb..000000000 --- a/src/pool/uma/utils.ts +++ /dev/null @@ -1,53 +0,0 @@ -import assert from "assert"; -import { BigNumber, BigNumberish, delay as sleep } from "../../utils"; - -export { delay as sleep } from "../../utils"; - -// check if a value is not null or undefined, useful for numbers which could be 0. -// "is" syntax: https://stackoverflow.com/questions/40081332/what-does-the-is-keyword-do-in-typescript -/* eslint-disable-next-line @typescript-eslint/ban-types */ -export function exists(value: T | null | undefined): value is NonNullable { - return value !== null && value !== undefined; -} - -// useful for maintaining balances from events -export type Balances = { [key: string]: string }; -export function Balances(balances: Balances = {}) { - function create(id: string, amount = "0") { - assert(!has(id), "balance already exists"); - return set(id, amount); - } - function has(id: string) { - return exists(balances[id]); - } - function set(id: string, amount: string) { - balances[id] = amount; - return amount; - } - function add(id: string, amount: BigNumberish) { - return set(id, BigNumber.from(amount).add(getOrCreate(id)).toString()); - } - function sub(id: string, amount: BigNumberish) { - return set(id, BigNumber.from(getOrCreate(id)).sub(amount).toString()); - } - function get(id: string) { - assert(has(id), "balance does not exist"); - return balances[id]; - } - function getOrCreate(id: string) { - if (has(id)) return get(id); - return create(id); - } - return { create, add, sub, get, balances, set, has, getOrCreate }; -} - -// Loop forever but wait until execution is finished before starting next timer. Throw an error to break this -// or add another utility function if you need it to end on condition. -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function loop(fn: (...args: any[]) => any, delay: number, ...args: any[]) { - do { - await fn(...args); - await sleep(delay); - /* eslint-disable-next-line no-constant-condition */ - } while (true); -} diff --git a/yarn.lock b/yarn.lock index 09b56a906..5195c8cd2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4286,11 +4286,6 @@ resolved "https://registry.yarnpkg.com/@uma/contracts-node/-/contracts-node-0.4.25.tgz#d5c82f1f2c7e0dc2dec26fe876db73ba3f0689d7" integrity sha512-WaFojX4qyMmXpy5MBS7g0M0KnWESGusdSfTmlkZpCh65TksGaJwAyOM1YBRLL3xm3xSgxPoG+n6tTilSomUmOw== -"@uma/contracts-node@^0.4.26": - version "0.4.26" - resolved "https://registry.yarnpkg.com/@uma/contracts-node/-/contracts-node-0.4.26.tgz#2c967e10c41292eeaafeb6b458525138e06ea0d8" - integrity sha512-o+wzScJB9xVavQwQWkAA8ZXBT9tCqyrhJOBdcyxaKmTTZmPPjAW52V5IVKwcmEOykFOliDjvRtFZIfWjwzUwcA== - "@uma/core@^2.18.0": version "2.56.0" resolved "https://registry.yarnpkg.com/@uma/core/-/core-2.56.0.tgz#c19aa427f08691a85e99ec523d23abf359a6b0c3" From f78de6149fcae4fc51066a22500f8bc969e12eb2 Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Tue, 6 Jan 2026 22:34:53 +0000 Subject: [PATCH 2/2] Drop test --- test/poolClient.test.ts | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 test/poolClient.test.ts diff --git a/test/poolClient.test.ts b/test/poolClient.test.ts deleted file mode 100644 index 207b828ed..000000000 --- a/test/poolClient.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as poolClient from "../src/pool/poolClient"; -import { BigNumber } from "../src/utils"; -import { expect } from "./utils"; - -describe("poolClient", function () { - it("previewRemoval", function () { - const user = { - address: "0x9A8f92a830A5cB89a3816e3D267CB7791c16b04D", - lpTokens: "900000000000000000", - positionValue: "900000541941830509", - totalDeposited: "900000000000000000", - feesEarned: "541941830509", - }; - const result = poolClient.previewRemoval(user, 0.75); - - expect(BigNumber.from(result.total.receive).add(result.total.remain).toString()).to.be.eq(user.positionValue); - expect(BigNumber.from(result.position.receive).add(result.position.remain).toString()).to.be.eq( - user.totalDeposited - ); - expect(BigNumber.from(result.fees.receive).add(result.fees.remain).toString()).to.be.eq(user.feesEarned); - }); -});