From 3fea99f3dda0a79b5f297fd65937d2bc9334a826 Mon Sep 17 00:00:00 2001 From: Apeguru Date: Sat, 23 Mar 2024 01:17:12 -0300 Subject: [PATCH 1/3] lynex adapter --- adapters/lynex/package.json | 32 + adapters/lynex/src/index.ts | 110 ++ adapters/lynex/src/sdk/abis/PairAPIABI.json | 720 ++++++++++ adapters/lynex/src/sdk/abis/veLYNX.json | 1309 +++++++++++++++++++ adapters/lynex/src/sdk/config.ts | 22 + adapters/lynex/src/sdk/lensDetails.ts | 174 +++ adapters/lynex/src/sdk/subgraphDetails.ts | 82 ++ adapters/lynex/tsconfig.json | 109 ++ adapters/lynex/yarn.lock | 211 +++ 9 files changed, 2769 insertions(+) create mode 100644 adapters/lynex/package.json create mode 100644 adapters/lynex/src/index.ts create mode 100644 adapters/lynex/src/sdk/abis/PairAPIABI.json create mode 100644 adapters/lynex/src/sdk/abis/veLYNX.json create mode 100644 adapters/lynex/src/sdk/config.ts create mode 100644 adapters/lynex/src/sdk/lensDetails.ts create mode 100644 adapters/lynex/src/sdk/subgraphDetails.ts create mode 100644 adapters/lynex/tsconfig.json create mode 100644 adapters/lynex/yarn.lock diff --git a/adapters/lynex/package.json b/adapters/lynex/package.json new file mode 100644 index 00000000..6f39d663 --- /dev/null +++ b/adapters/lynex/package.json @@ -0,0 +1,32 @@ +{ + "name": "lynex-adapter", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node dist/index.js", + "compile": "tsc", + "watch": "tsc -w", + "clear": "rm -rf dist" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@types/big.js": "^6.2.2", + "big.js": "^6.2.1", + "bignumber.js": "^9.1.2", + "csv-parser": "^3.0.0", + "decimal.js-light": "^2.5.1", + "fast-csv": "^5.0.1", + "jsbi": "^4.3.0", + "tiny-invariant": "^1.3.1", + "toformat": "^2.0.0", + "viem": "^2.8.13" + }, + "devDependencies": { + "@types/node": "^20.11.17", + "typescript": "^5.3.3" + } +} diff --git a/adapters/lynex/src/index.ts b/adapters/lynex/src/index.ts new file mode 100644 index 00000000..d43de8fe --- /dev/null +++ b/adapters/lynex/src/index.ts @@ -0,0 +1,110 @@ +import fs from "fs"; +import { write } from "fast-csv"; +import { getTimestampAtBlock, getUserAddresses } from "./sdk/subgraphDetails"; +import { + VE_LYNX_ADDRESS, + fetchUserPools, + fetchUserVotes, +} from "./sdk/lensDetails"; +import BigNumber from "bignumber.js"; + +interface CSVRow { + block_number: string; + timestamp: string; + user_address: string; + token_address: string; + token_balance: string; +} + +const getData = async () => { + const snapshotBlocks = [2999728]; + + const csvRows: CSVRow[] = []; + + for (let block of snapshotBlocks) { + const [userAddresses] = await Promise.all([getUserAddresses(block)]); + console.log(`Block: ${block}`); + console.log("UserAddresses: ", userAddresses.length); + + const tokenBalanceMap = {} as { + [userAddress: string]: { [tokenAddress: string]: BigNumber }; + }; + + const userPoolFetch = []; + const userVotesFetch = []; + + for (const user of userAddresses) { + userPoolFetch.push(fetchUserPools(BigInt(block), user.id, user.pools)); + userVotesFetch.push(fetchUserVotes(BigInt(block), user.id)); + } + + const userFetchResult = await Promise.all(userPoolFetch); + const userVotesResult = await Promise.all(userVotesFetch); + const block_number = block.toString(); + const timestamp = new Date(await getTimestampAtBlock(block)).toISOString(); + + for (const userFetchedPools of userFetchResult) { + for (const userPool of userFetchedPools) { + const user_address = userPool.result.userAddress.toLowerCase(); + const totalLPBalance = + userPool.result.account_lp_balance + + userPool.result.account_gauge_balance; + const total0 = + (totalLPBalance * userPool.result.reserve0) / + userPool.result.total_supply; + const total1 = + (totalLPBalance * userPool.result.reserve1) / + userPool.result.total_supply; + const token0Address = userPool.result.token0.toLowerCase(); + const token1Address = userPool.result.token1.toLowerCase(); + + // Aggregate tokens + tokenBalanceMap[user_address] = tokenBalanceMap[user_address] ?? {}; + tokenBalanceMap[user_address][token0Address] = BigNumber( + tokenBalanceMap[user_address][token0Address] ?? 0 + ).plus(total0.toString()); + tokenBalanceMap[user_address] = tokenBalanceMap[user_address] ?? {}; + tokenBalanceMap[user_address][token1Address] = BigNumber( + tokenBalanceMap[user_address][token1Address] ?? 0 + ).plus(total1.toString()); + } + } + + for (const userFecthedVotes of userVotesResult) { + for (const userVote of userFecthedVotes) { + const user_address = userVote.result.userAddress.toLowerCase(); + const token0Address = VE_LYNX_ADDRESS.toLowerCase(); + tokenBalanceMap[user_address] = tokenBalanceMap[user_address] ?? {}; + tokenBalanceMap[user_address][token0Address] = BigNumber( + tokenBalanceMap[user_address][token0Address] ?? 0 + ).plus(userVote.result.amount.toString()); + } + } + + Object.entries(tokenBalanceMap).forEach(([user_address, balances]) => { + Object.entries(balances).forEach(([token_address, token_balance]) => { + if (token_balance.dp(0).lte(0)) { + return; + } + csvRows.push({ + block_number, + timestamp, + user_address, + token_address, + token_balance: token_balance.dp(0).toString(), + }); + }); + }); + + const ws = fs.createWriteStream("outputData.csv"); + write(csvRows, { headers: true }) + .pipe(ws) + .on("finish", () => { + console.log("CSV file has been written."); + }); + } +}; + +getData().then(() => { + console.log("Done"); +}); diff --git a/adapters/lynex/src/sdk/abis/PairAPIABI.json b/adapters/lynex/src/sdk/abis/PairAPIABI.json new file mode 100644 index 00000000..2ba927fc --- /dev/null +++ b/adapters/lynex/src/sdk/abis/PairAPIABI.json @@ -0,0 +1,720 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldOwner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "Owner", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldVoter", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newVoter", + "type": "address" + } + ], + "name": "Voter", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldWBF", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newWBF", + "type": "address" + } + ], + "name": "WBF", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_EPOCHS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_PAIRS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "MAX_REWARDS", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "WEEK", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "algebraFactory", + "outputs": [ + { + "internalType": "contract IAlgebraFactory", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "base", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amounts", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_offset", + "type": "uint256" + } + ], + "name": "getAllPair", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "pair_address", + "type": "address" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "uint256", + "name": "decimals", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "stable", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "total_supply", + "type": "uint256" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "string", + "name": "token0_symbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "token0_decimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserve0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimable0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "string", + "name": "token1_symbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "token1_decimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserve1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimable1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "gauge", + "type": "address" + }, + { + "internalType": "uint256", + "name": "gauge_total_supply", + "type": "uint256" + }, + { + "internalType": "address", + "name": "fee", + "type": "address" + }, + { + "internalType": "address", + "name": "bribe", + "type": "address" + }, + { + "internalType": "uint256", + "name": "emissions", + "type": "uint256" + }, + { + "internalType": "address", + "name": "emissions_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "emissions_token_decimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_lp_balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_token0_balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_token1_balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_gauge_balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_locked_gauge_balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_lock_end", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_gauge_earned", + "type": "uint256" + } + ], + "internalType": "struct PairAPI.pairInfo[]", + "name": "Pairs", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_pair", + "type": "address" + }, + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "getPair", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "pair_address", + "type": "address" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "uint256", + "name": "decimals", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "stable", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "total_supply", + "type": "uint256" + }, + { + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "internalType": "string", + "name": "token0_symbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "token0_decimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserve0", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimable0", + "type": "uint256" + }, + { + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "internalType": "string", + "name": "token1_symbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "token1_decimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserve1", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimable1", + "type": "uint256" + }, + { + "internalType": "address", + "name": "gauge", + "type": "address" + }, + { + "internalType": "uint256", + "name": "gauge_total_supply", + "type": "uint256" + }, + { + "internalType": "address", + "name": "fee", + "type": "address" + }, + { + "internalType": "address", + "name": "bribe", + "type": "address" + }, + { + "internalType": "uint256", + "name": "emissions", + "type": "uint256" + }, + { + "internalType": "address", + "name": "emissions_token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "emissions_token_decimals", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_lp_balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_token0_balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_token1_balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_gauge_balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_locked_gauge_balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_lock_end", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "account_gauge_earned", + "type": "uint256" + } + ], + "internalType": "struct PairAPI.pairInfo", + "name": "_pairInfo", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_amounts", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_offset", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_pair", + "type": "address" + } + ], + "name": "getPairBribe", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "epochTimestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalVotes", + "type": "uint256" + }, + { + "internalType": "address", + "name": "pair", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint8", + "name": "decimals", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "internalType": "struct PairAPI.tokenBribe[]", + "name": "bribes", + "type": "tuple[]" + } + ], + "internalType": "struct PairAPI.pairBribeEpoch[]", + "name": "_pairEpoch", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_voter", + "type": "address" + }, + { + "internalType": "contract IPairFactory", + "name": "_factory", + "type": "address" + }, + { + "internalType": "address", + "name": "_algebraFactory", + "type": "address" + }, + { + "internalType": "address", + "name": "_base", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_pair", + "type": "address" + }, + { + "internalType": "address", + "name": "_token", + "type": "address" + } + ], + "name": "left", + "outputs": [ + { + "internalType": "uint256", + "name": "_rewPerEpoch", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pairFactory", + "outputs": [ + { + "internalType": "contract IPairFactory", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "setOwner", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_voter", + "type": "address" + }, + { + "internalType": "contract IPairFactory", + "name": "_factory", + "type": "address" + } + ], + "name": "setVoter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "underlyingToken", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "voter", + "outputs": [ + { + "internalType": "contract IVoter", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } + ] \ No newline at end of file diff --git a/adapters/lynex/src/sdk/abis/veLYNX.json b/adapters/lynex/src/sdk/abis/veLYNX.json new file mode 100644 index 00000000..76ea3b51 --- /dev/null +++ b/adapters/lynex/src/sdk/abis/veLYNX.json @@ -0,0 +1,1309 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { "inputs": [], "name": "AlreadyVoted", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "ERC721IncorrectOwner", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "ERC721InsufficientApproval", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "approver", "type": "address" } + ], + "name": "ERC721InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" } + ], + "name": "ERC721InvalidOperator", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "ERC721InvalidOwner", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "receiver", "type": "address" } + ], + "name": "ERC721InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "sender", "type": "address" } + ], + "name": "ERC721InvalidSender", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "ERC721NonexistentToken", + "type": "error" + }, + { "inputs": [], "name": "InvalidDelegatee", "type": "error" }, + { "inputs": [], "name": "InvalidNonce", "type": "error" }, + { "inputs": [], "name": "InvalidSignature", "type": "error" }, + { "inputs": [], "name": "InvalidSignatureS", "type": "error" }, + { "inputs": [], "name": "InvalidWeights", "type": "error" }, + { "inputs": [], "name": "LockDurationNotInFuture", "type": "error" }, + { "inputs": [], "name": "LockDurationTooLong", "type": "error" }, + { "inputs": [], "name": "LockExpired", "type": "error" }, + { "inputs": [], "name": "LockHoldsValue", "type": "error" }, + { "inputs": [], "name": "LockNotExpired", "type": "error" }, + { "inputs": [], "name": "NoLockFound", "type": "error" }, + { "inputs": [], "name": "NotLockOwner", "type": "error" }, + { "inputs": [], "name": "NotPermanentLock", "type": "error" }, + { "inputs": [], "name": "PermanentLock", "type": "error" }, + { "inputs": [], "name": "PermanentLockMismatch", "type": "error" }, + { "inputs": [], "name": "SafeCastOverflow", "type": "error" }, + { "inputs": [], "name": "SameNFT", "type": "error" }, + { "inputs": [], "name": "SignatureExpired", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "expiry", "type": "uint256" } + ], + "name": "VotesExpiredSignature", + "type": "error" + }, + { "inputs": [], "name": "ZeroAmount", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ClaimApproval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ClaimApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "fromDelegate", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "toDelegate", + "type": "address" + } + ], + "name": "DelegateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegate", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "previousBalance", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newBalance", + "type": "uint256" + } + ], + "name": "DelegateVotesChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [], + "name": "EIP712DomainChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "LockAmountIncreased", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "unlockTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isPermanent", + "type": "bool" + } + ], + "name": "LockCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "fromDelegate", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "toDelegate", + "type": "address" + } + ], + "name": "LockDelegateChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newUnlockTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isPermanent", + "type": "bool" + } + ], + "name": "LockDurationExtended", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "fromTokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "toTokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "unlockTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isPermanent", + "type": "bool" + } + ], + "name": "LockMerged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256[]", + "name": "splitWeights", + "type": "uint256[]" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "_tokenId", + "type": "uint256" + } + ], + "name": "LockSplit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "unlockTime", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isPermanent", + "type": "bool" + } + ], + "name": "LockUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "claimAmount", + "type": "uint256" + } + ], + "name": "PayoutClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "oldSupply", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newSupply", + "type": "uint256" + } + ], + "name": "SupplyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "unlockTime", + "type": "uint256" + } + ], + "name": "UnlockPermanent", + "type": "event" + }, + { + "inputs": [], + "name": "DELEGATION_TYPEHASH", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "_lockDetails", + "outputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "uint256", "name": "startTime", "type": "uint256" }, + { "internalType": "uint256", "name": "endTime", "type": "uint256" }, + { "internalType": "bool", "name": "isPermanent", "type": "bool" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "_token", + "outputs": [ + { + "internalType": "contract IERC20Upgradeable", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "approve", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "artProxy", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "balanceOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" } + ], + "name": "balanceOfNFT", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }, + { "internalType": "uint256", "name": "_timestamp", "type": "uint256" } + ], + "name": "balanceOfNFTAt", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "checkpoint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_delegateeAddress", + "type": "address" + } + ], + "name": "checkpointDelegatee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" } + ], + "name": "claim", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "claimablePayout", + "outputs": [ + { "internalType": "uint256", "name": "payout", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "claimedPayout", + "outputs": [ + { "internalType": "uint256", "name": "payout", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_value", "type": "uint256" }, + { "internalType": "uint256", "name": "_lockDuration", "type": "uint256" }, + { "internalType": "address", "name": "_to", "type": "address" }, + { "internalType": "address", "name": "_delegatee", "type": "address" }, + { "internalType": "bool", "name": "_permanent", "type": "bool" } + ], + "name": "createDelegatedLockFor", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_value", "type": "uint256" }, + { "internalType": "uint256", "name": "_lockDuration", "type": "uint256" }, + { "internalType": "bool", "name": "_permanent", "type": "bool" } + ], + "name": "createLock", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_value", "type": "uint256" }, + { "internalType": "uint256", "name": "_lockDuration", "type": "uint256" }, + { "internalType": "address", "name": "_to", "type": "address" }, + { "internalType": "bool", "name": "_permanent", "type": "bool" } + ], + "name": "createLockFor", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }, + { "internalType": "address", "name": "delegatee", "type": "address" } + ], + "name": "delegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "delegatee", "type": "address" } + ], + "name": "delegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "delegatee", "type": "address" }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "uint256", "name": "expiry", "type": "uint256" }, + { "internalType": "uint8", "name": "v", "type": "uint8" }, + { "internalType": "bytes32", "name": "r", "type": "bytes32" }, + { "internalType": "bytes32", "name": "s", "type": "bytes32" } + ], + "name": "delegateBySig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_delegatee", "type": "address" }, + { "internalType": "uint256", "name": "_timestamp", "type": "uint256" } + ], + "name": "delegateeSlopeChanges", + "outputs": [{ "internalType": "int128", "name": "", "type": "int128" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "uint48", "name": "timestamp", "type": "uint48" } + ], + "name": "delegates", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "delegates", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "eip712Domain", + "outputs": [ + { "internalType": "bytes1", "name": "fields", "type": "bytes1" }, + { "internalType": "string", "name": "name", "type": "string" }, + { "internalType": "string", "name": "version", "type": "string" }, + { "internalType": "uint256", "name": "chainId", "type": "uint256" }, + { + "internalType": "address", + "name": "verifyingContract", + "type": "address" + }, + { "internalType": "bytes32", "name": "salt", "type": "bytes32" }, + { "internalType": "uint256[]", "name": "extensions", "type": "uint256[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "getAccountDelegates", + "outputs": [ + { "internalType": "address[]", "name": "", "type": "address[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "getApproved", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "getClaimApproved", + "outputs": [ + { "internalType": "address", "name": "operator", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" } + ], + "name": "getFirstEscrowPoint", + "outputs": [ + { + "components": [ + { "internalType": "int128", "name": "bias", "type": "int128" }, + { "internalType": "int128", "name": "slope", "type": "int128" }, + { "internalType": "int128", "name": "permanent", "type": "int128" } + ], + "internalType": "struct Checkpoints.Point", + "name": "", + "type": "tuple" + }, + { "internalType": "uint48", "name": "", "type": "uint48" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "getLockDelegatee", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }, + { "internalType": "uint256", "name": "_timestamp", "type": "uint256" } + ], + "name": "getPastEscrowPoint", + "outputs": [ + { + "components": [ + { "internalType": "int128", "name": "bias", "type": "int128" }, + { "internalType": "int128", "name": "slope", "type": "int128" }, + { "internalType": "int128", "name": "permanent", "type": "int128" } + ], + "internalType": "struct Checkpoints.Point", + "name": "", + "type": "tuple" + }, + { "internalType": "uint48", "name": "", "type": "uint48" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_timePoint", "type": "uint256" } + ], + "name": "getPastTotalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "uint256", "name": "timepoint", "type": "uint256" } + ], + "name": "getPastVotes", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "getVotes", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "globalCheckpoint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_timestamp", "type": "uint256" } + ], + "name": "globalSlopeChanges", + "outputs": [{ "internalType": "int128", "name": "", "type": "int128" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }, + { "internalType": "uint256", "name": "_value", "type": "uint256" } + ], + "name": "increaseAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" }, + { "internalType": "uint256", "name": "_lockDuration", "type": "uint256" }, + { "internalType": "bool", "name": "_permanent", "type": "bool" } + ], + "name": "increaseUnlockTime", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "_name", "type": "string" }, + { "internalType": "string", "name": "_symbol", "type": "string" }, + { "internalType": "string", "name": "version", "type": "string" }, + { + "internalType": "contract IERC20Upgradeable", + "name": "mainToken", + "type": "address" + }, + { "internalType": "address", "name": "_artProxy", "type": "address" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "isApprovedClaimOrOwner", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "operator", "type": "address" } + ], + "name": "isApprovedForAll", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "user", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "isApprovedOrOwner", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address", "name": "operator", "type": "address" } + ], + "name": "isClaimApprovedForAll", + "outputs": [ + { "internalType": "bool", "name": "isClaimApproved", "type": "bool" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" } + ], + "name": "lockDetails", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "uint256", "name": "startTime", "type": "uint256" }, + { "internalType": "uint256", "name": "endTime", "type": "uint256" }, + { "internalType": "bool", "name": "isPermanent", "type": "bool" } + ], + "internalType": "struct IVotingEscrowV2Upgradeable.LockDetails", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_from", "type": "uint256" }, + { "internalType": "uint256", "name": "_to", "type": "uint256" } + ], + "name": "merge", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "nonces", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "ownerOf", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "payoutToken", + "outputs": [ + { "internalType": "address", "name": "token", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" }, + { "internalType": "bool", "name": "approved", "type": "bool" } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" }, + { "internalType": "bool", "name": "approved", "type": "bool" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "setClaimApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" }, + { "internalType": "bool", "name": "approved", "type": "bool" } + ], + "name": "setClaimApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256[]", "name": "_weights", "type": "uint256[]" }, + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" } + ], + "name": "split", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "supply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" } + ], + "name": "supportsInterface", + "outputs": [ + { "internalType": "bool", "name": "supported", "type": "bool" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_timestamp", "type": "uint256" } + ], + "name": "toGlobalClock", + "outputs": [{ "internalType": "uint48", "name": "", "type": "uint48" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "token", + "outputs": [ + { + "internalType": "contract IERC20Upgradeable", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "tokenByIndex", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "uint256", "name": "index", "type": "uint256" } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" } + ], + "name": "tokenURI", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalNftsMinted", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "from", "type": "address" }, + { "internalType": "address", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_tokenId", "type": "uint256" } + ], + "name": "unlockPermanent", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "vestedPayout", + "outputs": [ + { "internalType": "uint256", "name": "payout", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" }, + { "internalType": "uint256", "name": "timestamp", "type": "uint256" } + ], + "name": "vestedPayoutAtTime", + "outputs": [ + { "internalType": "uint256", "name": "payout", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "vestingPayout", + "outputs": [ + { "internalType": "uint256", "name": "payout", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "tokenId", "type": "uint256" } + ], + "name": "vestingPeriod", + "outputs": [ + { "internalType": "uint256", "name": "vestingStart", "type": "uint256" }, + { "internalType": "uint256", "name": "vestingEnd", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/adapters/lynex/src/sdk/config.ts b/adapters/lynex/src/sdk/config.ts new file mode 100644 index 00000000..418afb85 --- /dev/null +++ b/adapters/lynex/src/sdk/config.ts @@ -0,0 +1,22 @@ +export const enum CHAINS { + L2_CHAIN_ID = 59144, +} +export const enum PROTOCOLS { + LYNEX = 0, +} + +export const enum AMM_TYPES { + LYNEX = 0, +} + +export const SUBGRAPH_URLS = { + [CHAINS.L2_CHAIN_ID]: { + [PROTOCOLS.LYNEX]: { + [AMM_TYPES.LYNEX]: + "https://api.goldsky.com/api/public/project_cltyhthusbmxp01s95k9l8a1u/subgraphs/lynex-gauges/1.1.0/gn", + }, + }, +}; +export const RPC_URLS = { + [CHAINS.L2_CHAIN_ID]: "https://rpc.linea.build", +}; diff --git a/adapters/lynex/src/sdk/lensDetails.ts b/adapters/lynex/src/sdk/lensDetails.ts new file mode 100644 index 00000000..005569f0 --- /dev/null +++ b/adapters/lynex/src/sdk/lensDetails.ts @@ -0,0 +1,174 @@ +import { + Abi, + Address, + createPublicClient, + extractChain, + http, + MulticallParameters, + PublicClient, +} from "viem"; +import { linea } from "viem/chains"; +import { CHAINS, RPC_URLS } from "./config"; +import lensABI from "./abis/PairAPIABI.json"; +import veLYNXAbi from "./abis/veLYNX.json"; + +export const LENS_ADDRESS = "0x6c84329CC8c37376eb32db50a17F3bFc917c3665"; // PairAPI +export const VE_LYNX_ADDRESS = "0x8d95f56b0bac46e8ac1d3a3f12fb1e5bc39b4c0c"; // veLYNX + +export interface LensResponse { + pair_address: string; + symbol: string; + name: string; + decimals: bigint; + stable: boolean; + total_supply: bigint; + token0: string; + token0_symbol: string; + token0_decimals: bigint; + reserve0: bigint; + claimable0: string; + token1: string; + token1_symbol: string; + token1_decimals: bigint; + reserve1: bigint; + claimable1: bigint; + gauge: string; + gauge_total_supply: bigint; + fee: string; + bribe: string; + emissions: bigint; + emissions_token: string; + emissions_token_decimals: bigint; + account_lp_balance: bigint; + account_token0_balance: bigint; + account_token1_balance: bigint; + account_gauge_balance: bigint; + account_locked_gauge_balance: bigint; + account_lock_end: bigint; + account_gauge_earned: bigint; + userAddress: string; +} + +export interface LensResponseWithBlock { + result: LensResponse; +} + +export interface VoteRequest { + userAddress: string; + amount: bigint; +} + +export interface VoteResponse { + result: VoteRequest; +} + +export const fetchUserPools = async ( + blockNumber: bigint, + userAddress: string, + userPools: string[] +): Promise => { + const publicClient = createPublicClient({ + chain: extractChain({ chains: [linea], id: CHAINS.L2_CHAIN_ID }), + transport: http(RPC_URLS[CHAINS.L2_CHAIN_ID]), + }); + + const calls = userPools.map((pool: string) => { + return { + address: LENS_ADDRESS, + name: "getPair", + params: [pool, userAddress], + }; + }); + + const res = (await multicall( + publicClient, + lensABI as Abi, + calls, + blockNumber + )) as any; + return res.map((r: any) => { + return { result: { ...r.result, userAddress } }; + }) as LensResponseWithBlock[]; +}; + +export const fetchUserVotes = async ( + blockNumber: bigint, + userAddress: string +): Promise => { + const publicClient = createPublicClient({ + chain: extractChain({ chains: [linea], id: CHAINS.L2_CHAIN_ID }), + transport: http(RPC_URLS[CHAINS.L2_CHAIN_ID]), + }); + + const userBalanceCall = await multicall( + publicClient, + veLYNXAbi as Abi, + [ + { + address: VE_LYNX_ADDRESS, + name: "balanceOf", + params: [userAddress], + }, + ], + blockNumber + ); + + const userBalance = userBalanceCall[0].result as number; + + if (userBalance === 0) return []; + + const calls = []; + for (let i = 0; i < userBalance; i++) { + calls.push({ + address: VE_LYNX_ADDRESS, + name: "tokenOfOwnerByIndex", + params: [userAddress, i], + }); + } + + const userTokensCalls = await multicall( + publicClient, + veLYNXAbi as Abi, + calls, + blockNumber + ); + + const detailsCall = userTokensCalls.map((call) => { + return { + address: VE_LYNX_ADDRESS, + name: "lockDetails", + params: [call.result], + }; + }); + + const res = (await multicall( + publicClient, + veLYNXAbi as Abi, + detailsCall, + blockNumber + )) as any; + return res.map((r: any) => { + return { result: { ...r.result, userAddress } }; + }) as VoteResponse[]; +}; + +function multicall( + publicClient: PublicClient, + abi: Abi, + calls: any[], + blockNumber: bigint +) { + const call: MulticallParameters = { + contracts: calls.map((call) => { + return { + address: call.address as Address, + abi, + functionName: call.name, + args: call.params, + }; + }), + blockNumber, + }; + + return publicClient.multicall(call); +} diff --git a/adapters/lynex/src/sdk/subgraphDetails.ts b/adapters/lynex/src/sdk/subgraphDetails.ts new file mode 100644 index 00000000..0ea70c75 --- /dev/null +++ b/adapters/lynex/src/sdk/subgraphDetails.ts @@ -0,0 +1,82 @@ +import { createPublicClient, extractChain, http } from "viem"; +import { linea } from "viem/chains"; +import { CHAINS, PROTOCOLS, RPC_URLS, SUBGRAPH_URLS } from "./config"; + +interface UserStake { + id: string; + pools: any[]; +} + +export const getUserAddresses = async ( + blockNumber: number +): Promise => { + let subgraphUrl = + SUBGRAPH_URLS[CHAINS.L2_CHAIN_ID][PROTOCOLS.LYNEX][PROTOCOLS.LYNEX]; + let blockQuery = blockNumber !== 0 ? ` block: {number: ${blockNumber}}` : ``; + + let skip = 0; + let fetchNext = true; + let result: UserStake[] = []; + while (fetchNext) { + let query = ` + query UserQuery { + users(${blockQuery} first:1000,skip:${skip}) { + id + liquidityPositions { + id + gauge { + id + token0 { + symbol + id + } + token1 { + symbol + id + } + pool + } + amount + userToken0 + userToken1 + userToken0Decimals + userToken1Decimals + } + } + }`; + + let response = await fetch(subgraphUrl, { + method: "POST", + body: JSON.stringify({ query }), + headers: { "Content-Type": "application/json" }, + }); + let data = await response.json(); + let userStakes = data.data.users; + for (let i = 0; i < userStakes.length; i++) { + let userStake = userStakes[i]; + let transformedUserStake: UserStake = { + id: userStake.id, + pools: userStake.liquidityPositions.map((lp: any) => lp.gauge.pool), + }; + result.push(transformedUserStake); + } + if (userStakes.length < 1000) { + fetchNext = false; + } else { + skip += 1000; + } + } + return result; +}; + +export const getTimestampAtBlock = async (blockNumber: number) => { + const publicClient = createPublicClient({ + chain: extractChain({ chains: [linea], id: CHAINS.L2_CHAIN_ID }), + transport: http(RPC_URLS[CHAINS.L2_CHAIN_ID]), + }); + + const block = await publicClient.getBlock({ + blockNumber: BigInt(blockNumber), + }); + return Number(block.timestamp * 1000n); +}; diff --git a/adapters/lynex/tsconfig.json b/adapters/lynex/tsconfig.json new file mode 100644 index 00000000..9852ff27 --- /dev/null +++ b/adapters/lynex/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs" /* Specify what module code is generated. */, + "rootDir": "src/" /* Specify the root folder within your source files. */, + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + "resolveJsonModule": true /* Enable importing .json files. */, + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "dist/" /* Specify an output folder for all emitted files. */, + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + + /* Type Checking */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/adapters/lynex/yarn.lock b/adapters/lynex/yarn.lock new file mode 100644 index 00000000..c3536119 --- /dev/null +++ b/adapters/lynex/yarn.lock @@ -0,0 +1,211 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@adraffy/ens-normalize@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" + integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== + +"@fast-csv/format@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@fast-csv/format/-/format-5.0.0.tgz#f2e557fdd4370360b418cc78636684c07b12d0ba" + integrity sha512-IyMpHwYIOGa2f0BJi6Wk55UF0oBA5urdIydoEDYxPo88LFbeb3Yr4rgpu98OAO1glUWheSnNtUgS80LE+/dqmw== + dependencies: + lodash.escaperegexp "^4.1.2" + lodash.isboolean "^3.0.3" + lodash.isequal "^4.5.0" + lodash.isfunction "^3.0.9" + lodash.isnil "^4.0.0" + +"@fast-csv/parse@5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@fast-csv/parse/-/parse-5.0.0.tgz#091665753f9e58f0dda6a55ae30f582526042903" + integrity sha512-ecF8tCm3jVxeRjEB6VPzmA+1wGaJ5JgaUX2uesOXdXD6qQp0B3EdshOIed4yT1Xlj/F2f8v4zHSo0Oi31L697g== + dependencies: + lodash.escaperegexp "^4.1.2" + lodash.groupby "^4.6.0" + lodash.isfunction "^3.0.9" + lodash.isnil "^4.0.0" + lodash.isundefined "^3.0.1" + lodash.uniq "^4.5.0" + +"@noble/curves@1.2.0", "@noble/curves@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + +"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + +"@scure/base@~1.1.0", "@scure/base@~1.1.2": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d" + integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g== + +"@scure/bip32@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.2.tgz#90e78c027d5e30f0b22c1f8d50ff12f3fb7559f8" + integrity sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA== + dependencies: + "@noble/curves" "~1.2.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.2" + +"@scure/bip39@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" + integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@types/big.js@^6.2.2": + version "6.2.2" + resolved "https://registry.yarnpkg.com/@types/big.js/-/big.js-6.2.2.tgz#69422ec9ef59df1330ccfde2106d9e1159a083c3" + integrity sha512-e2cOW9YlVzFY2iScnGBBkplKsrn2CsObHQ2Hiw4V1sSyiGbgWL8IyqE3zFi1Pt5o1pdAtYkDAIsF3KKUPjdzaA== + +"@types/node@^20.11.17": + version "20.11.30" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.30.tgz#9c33467fc23167a347e73834f788f4b9f399d66f" + integrity sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw== + dependencies: + undici-types "~5.26.4" + +abitype@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.0.tgz#237176dace81d90d018bebf3a45cb42f2a2d9e97" + integrity sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ== + +big.js@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.2.1.tgz#7205ce763efb17c2e41f26f121c420c6a7c2744f" + integrity sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ== + +bignumber.js@^9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== + +csv-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/csv-parser/-/csv-parser-3.0.0.tgz#b88a6256d79e090a97a1b56451f9327b01d710e7" + integrity sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ== + dependencies: + minimist "^1.2.0" + +decimal.js-light@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934" + integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg== + +fast-csv@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/fast-csv/-/fast-csv-5.0.1.tgz#2beaa2437ea83bd335cc3a39a41e69d9936f3987" + integrity sha512-Q43zC4NdQD5MAWOVQOF8KA+D6ddvTJjX2ib8zqysm74jZhtk6+dc8C75/OqRV6Y9CLc4kgvbC3PLG8YL4YZfgw== + dependencies: + "@fast-csv/format" "5.0.0" + "@fast-csv/parse" "5.0.0" + +isows@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" + integrity sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg== + +jsbi@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-4.3.0.tgz#b54ee074fb6fcbc00619559305c8f7e912b04741" + integrity sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g== + +lodash.escaperegexp@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw== + +lodash.groupby@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1" + integrity sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.isfunction@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== + +lodash.isnil@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/lodash.isnil/-/lodash.isnil-4.0.0.tgz#49e28cd559013458c814c5479d3c663a21bfaa6c" + integrity sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng== + +lodash.isundefined@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48" + integrity sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA== + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +minimist@^1.2.0: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +tiny-invariant@^1.3.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + +toformat@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8" + integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ== + +typescript@^5.3.3: + version "5.4.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff" + integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +viem@^2.8.13: + version "2.8.16" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.8.16.tgz#30370390a6f109657b57c1a01b2793c4056b4e95" + integrity sha512-J8tu1aP7TfI2HT/IEmyJ+n+WInrA/cuMuJtfgvYhYgHBobxhYGc2SojHm5lZBWcWgErN1Ld7VcKUwTmPh4ToQA== + dependencies: + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@scure/bip32" "1.3.2" + "@scure/bip39" "1.2.1" + abitype "1.0.0" + isows "1.0.3" + ws "8.13.0" + +ws@8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== From 05ed18649f66123686df16bbe51f3c5aeacd7075 Mon Sep 17 00:00:00 2001 From: Apeguru Date: Sat, 6 Apr 2024 14:21:46 -0300 Subject: [PATCH 2/3] feat: Add user active unstaked liquidity TVL --- adapters/lynex/src/index.ts | 245 ++++++++++++------ adapters/lynex/src/sdk/config.ts | 32 +-- adapters/lynex/src/sdk/lensDetails.ts | 13 +- adapters/lynex/src/sdk/pools.ts | 286 ++++++++++++++++++++++ adapters/lynex/src/sdk/subgraphDetails.ts | 10 +- adapters/lynex/src/sdk/types.ts | 26 ++ 6 files changed, 498 insertions(+), 114 deletions(-) create mode 100644 adapters/lynex/src/sdk/pools.ts create mode 100644 adapters/lynex/src/sdk/types.ts diff --git a/adapters/lynex/src/index.ts b/adapters/lynex/src/index.ts index d43de8fe..c3e3873f 100644 --- a/adapters/lynex/src/index.ts +++ b/adapters/lynex/src/index.ts @@ -7,102 +7,193 @@ import { fetchUserVotes, } from "./sdk/lensDetails"; import BigNumber from "bignumber.js"; - -interface CSVRow { - block_number: string; - timestamp: string; - user_address: string; - token_address: string; - token_balance: string; -} +import { BlockData, OutputSchemaRow } from "./sdk/types"; +import { + getV2UserPositionsAtBlock, + getV3UserPositionsAtBlock, +} from "./sdk/pools"; const getData = async () => { - const snapshotBlocks = [2999728]; + const snapshotBlocks = [3460121]; - const csvRows: CSVRow[] = []; + const csvRows: OutputSchemaRow[] = []; for (let block of snapshotBlocks) { - const [userAddresses] = await Promise.all([getUserAddresses(block)]); - console.log(`Block: ${block}`); - console.log("UserAddresses: ", userAddresses.length); + const timestamp = await getTimestampAtBlock(block); + csvRows.push( + ...(await getUserTVLByBlock({ + blockNumber: block, + blockTimestamp: timestamp, + })) + ); + } - const tokenBalanceMap = {} as { - [userAddress: string]: { [tokenAddress: string]: BigNumber }; - }; + const ws = fs.createWriteStream("outputData.csv"); + write(csvRows, { headers: true }) + .pipe(ws) + .on("finish", () => { + console.log("CSV file has been written."); + }); +}; - const userPoolFetch = []; - const userVotesFetch = []; +export const getUserTVLByBlock = async ({ + blockNumber, + blockTimestamp, +}: BlockData): Promise => { + const result: OutputSchemaRow[] = []; + + const [stakedTvl, liquidityTvl] = await Promise.all([ + getUserStakedTVLByBlock({ blockNumber, blockTimestamp }), + getUserLiquidityTVLByBlock({ blockNumber, blockTimestamp }), + ]); + + // combine staked & unstaked + const combinedPositions = [...stakedTvl, ...liquidityTvl]; + const balances: Record> = {}; + for (const position of combinedPositions) { + balances[position.user_address] = balances[position.user_address] || {}; + + if (position.token_balance > 0n) + balances[position.user_address][position.token_address] = + (balances?.[position.user_address]?.[position.token_address] ?? 0n) + + position.token_balance; + } - for (const user of userAddresses) { - userPoolFetch.push(fetchUserPools(BigInt(block), user.id, user.pools)); - userVotesFetch.push(fetchUserVotes(BigInt(block), user.id)); + for (const [user, tokenBalances] of Object.entries(balances)) { + for (const [token, balance] of Object.entries(tokenBalances)) { + result.push({ + block_number: blockNumber, + timestamp: blockTimestamp, + user_address: user, + token_address: token, + token_balance: balance, + }); } + } - const userFetchResult = await Promise.all(userPoolFetch); - const userVotesResult = await Promise.all(userVotesFetch); - const block_number = block.toString(); - const timestamp = new Date(await getTimestampAtBlock(block)).toISOString(); - - for (const userFetchedPools of userFetchResult) { - for (const userPool of userFetchedPools) { - const user_address = userPool.result.userAddress.toLowerCase(); - const totalLPBalance = - userPool.result.account_lp_balance + - userPool.result.account_gauge_balance; - const total0 = - (totalLPBalance * userPool.result.reserve0) / - userPool.result.total_supply; - const total1 = - (totalLPBalance * userPool.result.reserve1) / - userPool.result.total_supply; - const token0Address = userPool.result.token0.toLowerCase(); - const token1Address = userPool.result.token1.toLowerCase(); - - // Aggregate tokens - tokenBalanceMap[user_address] = tokenBalanceMap[user_address] ?? {}; - tokenBalanceMap[user_address][token0Address] = BigNumber( - tokenBalanceMap[user_address][token0Address] ?? 0 - ).plus(total0.toString()); - tokenBalanceMap[user_address] = tokenBalanceMap[user_address] ?? {}; - tokenBalanceMap[user_address][token1Address] = BigNumber( - tokenBalanceMap[user_address][token1Address] ?? 0 - ).plus(total1.toString()); - } + return result; +}; + +export const getUserStakedTVLByBlock = async ({ + blockNumber, + blockTimestamp, +}: BlockData): Promise => { + const result: OutputSchemaRow[] = []; + const [userAddresses] = await Promise.all([getUserAddresses(blockNumber)]); + console.log(`Block: ${blockNumber}`); + console.log("UserAddresses: ", userAddresses.length); + + const tokenBalanceMap = {} as { + [userAddress: string]: { [tokenAddress: string]: BigNumber }; + }; + + const userPoolFetch = []; + const userVotesFetch = []; + + for (const user of userAddresses) { + userPoolFetch.push( + fetchUserPools(BigInt(blockNumber), user.id, user.pools) + ); + userVotesFetch.push(fetchUserVotes(BigInt(blockNumber), user.id)); + } + + const userFetchResult = await Promise.all(userPoolFetch); + const userVotesResult = await Promise.all(userVotesFetch); + + for (const userFetchedPools of userFetchResult) { + for (const userPool of userFetchedPools) { + const user_address = userPool.result.userAddress.toLowerCase(); + const totalLPBalance = userPool.result.account_gauge_balance; + const total0 = + (totalLPBalance * userPool.result.reserve0) / + userPool.result.total_supply; + const total1 = + (totalLPBalance * userPool.result.reserve1) / + userPool.result.total_supply; + const token0Address = userPool.result.token0.toLowerCase(); + const token1Address = userPool.result.token1.toLowerCase(); + + // Aggregate tokens + tokenBalanceMap[user_address] = tokenBalanceMap[user_address] ?? {}; + tokenBalanceMap[user_address][token0Address] = BigNumber( + tokenBalanceMap[user_address][token0Address] ?? 0 + ).plus(total0.toString()); + tokenBalanceMap[user_address] = tokenBalanceMap[user_address] ?? {}; + tokenBalanceMap[user_address][token1Address] = BigNumber( + tokenBalanceMap[user_address][token1Address] ?? 0 + ).plus(total1.toString()); } + } - for (const userFecthedVotes of userVotesResult) { - for (const userVote of userFecthedVotes) { - const user_address = userVote.result.userAddress.toLowerCase(); - const token0Address = VE_LYNX_ADDRESS.toLowerCase(); - tokenBalanceMap[user_address] = tokenBalanceMap[user_address] ?? {}; - tokenBalanceMap[user_address][token0Address] = BigNumber( - tokenBalanceMap[user_address][token0Address] ?? 0 - ).plus(userVote.result.amount.toString()); - } + for (const userFecthedVotes of userVotesResult) { + for (const userVote of userFecthedVotes) { + const user_address = userVote.result.userAddress.toLowerCase(); + const token0Address = VE_LYNX_ADDRESS.toLowerCase(); + tokenBalanceMap[user_address] = tokenBalanceMap[user_address] ?? {}; + tokenBalanceMap[user_address][token0Address] = BigNumber( + tokenBalanceMap[user_address][token0Address] ?? 0 + ).plus(userVote.result.amount.toString()); } + } - Object.entries(tokenBalanceMap).forEach(([user_address, balances]) => { - Object.entries(balances).forEach(([token_address, token_balance]) => { - if (token_balance.dp(0).lte(0)) { - return; - } - csvRows.push({ - block_number, - timestamp, - user_address, - token_address, - token_balance: token_balance.dp(0).toString(), - }); + Object.entries(tokenBalanceMap).forEach(([user_address, balances]) => { + Object.entries(balances).forEach(([token_address, token_balance]) => { + if (token_balance.dp(0).lte(0)) { + return; + } + result.push({ + block_number: blockNumber, + timestamp: blockTimestamp, + user_address, + token_address, + token_balance: BigInt(token_balance.dp(0).toNumber()), }); }); + }); + return result; +}; - const ws = fs.createWriteStream("outputData.csv"); - write(csvRows, { headers: true }) - .pipe(ws) - .on("finish", () => { - console.log("CSV file has been written."); +export const getUserLiquidityTVLByBlock = async ({ + blockNumber, + blockTimestamp, +}: BlockData): Promise => { + const result: OutputSchemaRow[] = []; + + const [v2Positions, v3Positions] = await Promise.all([ + getV2UserPositionsAtBlock(blockNumber), + getV3UserPositionsAtBlock(blockNumber), + ]); + + // combine v2 & v3 + const combinedPositions = [...v2Positions, ...v3Positions]; + const balances: Record> = {}; + for (const position of combinedPositions) { + balances[position.user] = balances[position.user] || {}; + + if (position.token0.balance > 0n) + balances[position.user][position.token0.address] = + (balances?.[position.user]?.[position.token0.address] ?? 0n) + + position.token0.balance; + + if (position.token1.balance > 0n) + balances[position.user][position.token1.address] = + (balances?.[position.user]?.[position.token1.address] ?? 0n) + + position.token1.balance; + } + + for (const [user, tokenBalances] of Object.entries(balances)) { + for (const [token, balance] of Object.entries(tokenBalances)) { + result.push({ + block_number: blockNumber, + timestamp: blockTimestamp, + user_address: user, + token_address: token, + token_balance: balance, }); + } } + + return result; }; getData().then(() => { diff --git a/adapters/lynex/src/sdk/config.ts b/adapters/lynex/src/sdk/config.ts index 418afb85..9232aef5 100644 --- a/adapters/lynex/src/sdk/config.ts +++ b/adapters/lynex/src/sdk/config.ts @@ -1,22 +1,14 @@ -export const enum CHAINS { - L2_CHAIN_ID = 59144, -} -export const enum PROTOCOLS { - LYNEX = 0, -} +import { createPublicClient, http } from "viem"; +import { linea } from "viem/chains"; -export const enum AMM_TYPES { - LYNEX = 0, -} +export const V2_SUBGRAPH_URL = + "https://api.studio.thegraph.com/query/59052/lynex-v1/version/latest"; +export const GAUGE_SUBGRAPH_URL = + "https://api.goldsky.com/api/public/project_cltyhthusbmxp01s95k9l8a1u/subgraphs/lynex-gauges/1.1.0/gn"; +export const V3_SUBGRAPH_URL = + "https://api.studio.thegraph.com/query/59052/lynex-cl/v0.0.1"; -export const SUBGRAPH_URLS = { - [CHAINS.L2_CHAIN_ID]: { - [PROTOCOLS.LYNEX]: { - [AMM_TYPES.LYNEX]: - "https://api.goldsky.com/api/public/project_cltyhthusbmxp01s95k9l8a1u/subgraphs/lynex-gauges/1.1.0/gn", - }, - }, -}; -export const RPC_URLS = { - [CHAINS.L2_CHAIN_ID]: "https://rpc.linea.build", -}; +export const client = createPublicClient({ + chain: linea, + transport: http("https://rpc.linea.build"), +}); diff --git a/adapters/lynex/src/sdk/lensDetails.ts b/adapters/lynex/src/sdk/lensDetails.ts index 005569f0..3cdf1096 100644 --- a/adapters/lynex/src/sdk/lensDetails.ts +++ b/adapters/lynex/src/sdk/lensDetails.ts @@ -7,8 +7,7 @@ import { MulticallParameters, PublicClient, } from "viem"; -import { linea } from "viem/chains"; -import { CHAINS, RPC_URLS } from "./config"; +import { client } from "./config"; import lensABI from "./abis/PairAPIABI.json"; import veLYNXAbi from "./abis/veLYNX.json"; @@ -67,10 +66,7 @@ export const fetchUserPools = async ( userAddress: string, userPools: string[] ): Promise => { - const publicClient = createPublicClient({ - chain: extractChain({ chains: [linea], id: CHAINS.L2_CHAIN_ID }), - transport: http(RPC_URLS[CHAINS.L2_CHAIN_ID]), - }); + const publicClient = client; const calls = userPools.map((pool: string) => { return { @@ -95,10 +91,7 @@ export const fetchUserVotes = async ( blockNumber: bigint, userAddress: string ): Promise => { - const publicClient = createPublicClient({ - chain: extractChain({ chains: [linea], id: CHAINS.L2_CHAIN_ID }), - transport: http(RPC_URLS[CHAINS.L2_CHAIN_ID]), - }); + const publicClient = client; const userBalanceCall = await multicall( publicClient, diff --git a/adapters/lynex/src/sdk/pools.ts b/adapters/lynex/src/sdk/pools.ts new file mode 100644 index 00000000..60d3fd31 --- /dev/null +++ b/adapters/lynex/src/sdk/pools.ts @@ -0,0 +1,286 @@ +import BigNumber from "bignumber.js"; +import { V2_SUBGRAPH_URL, V3_SUBGRAPH_URL, client } from "./config"; +import { UserPosition } from "./types"; + +type V2Position = { + liquidityTokenBalance: string; + user: { + id: string; + }; + pair: { + totalSupply: string; + reserve0: string; + reserve1: string; + token0: { + id: string; + symbol: string; + decimals: number; + }; + token1: { + id: string; + symbol: string; + decimals: number; + }; + token0Price: string; + token1Price: string; + }; +}; + +export const fromWei = (number: number | string, decimals = 18) => + new BigNumber(number).div(new BigNumber(10).pow(decimals)); +export const toWei = (number: number | string, decimals = 18) => + new BigNumber(number).times(new BigNumber(10).pow(decimals)); + +const getV2PositionReserves = (position: V2Position) => { + return { + reserve0: BigInt( + toWei(position.pair.reserve0, position.pair.token0.decimals) + .times(toWei(position.liquidityTokenBalance)) + .div(toWei(position.pair.totalSupply)) + .dp(0) + .toNumber() + ), + reserve1: BigInt( + toWei(position.pair.reserve1, position.pair.token1.decimals) + .times(toWei(position.liquidityTokenBalance)) + .div(toWei(position.pair.totalSupply)) + .dp(0) + .toNumber() + ), + }; +}; + +export const getV2UserPositionsAtBlock = async ( + blockNumber: number +): Promise => { + const result: UserPosition[] = []; + + let skip = 0; + let fetchNext = true; + while (fetchNext) { + const query = `query { + liquidityPositions( + first: 1000, + skip: ${skip}, + where: { liquidityTokenBalance_gt: 0 }, + block: { number: ${blockNumber} } + ) { + liquidityTokenBalance + user { + id + } + pair { + totalSupply + reserve0 + reserve1 + token0 { + id + symbol + decimals + } + token1 { + id + symbol + decimals + } + token0Price + token1Price + } + } + }`; + + const response = await fetch(V2_SUBGRAPH_URL, { + method: "POST", + body: JSON.stringify({ query }), + headers: { "Content-Type": "application/json" }, + }); + const { + data: { liquidityPositions }, + } = await response.json(); + + result.push( + ...liquidityPositions.map((position: V2Position) => { + const { reserve0, reserve1 } = getV2PositionReserves(position); + return { + user: position.user.id, + token0: { + address: position.pair.token0.id, + balance: reserve0, + symbol: position.pair.token0.symbol, + }, + token1: { + address: position.pair.token1.id, + balance: reserve1, + symbol: position.pair.token1.symbol, + }, + }; + }) + ); + + if (liquidityPositions.length < 1000) { + fetchNext = false; + } else { + skip += 1000; + } + } + + return result; +}; + +type V3Position = { + liquidity: string; + owner: string; + pool: { + sqrtPrice: string; + tick: string; + token0: { + id: string; + symbol: string; + }; + token1: { + id: string; + symbol: string; + }; + token0Price: string; + token1Price: string; + }; + tickLower: { + tickIdx: string; + }; + tickUpper: { + tickIdx: string; + }; +}; + +const getV3PositionReserves = (position: V3Position) => { + const liquidity = +position.liquidity; + const _sqrtPrice = +position.pool.sqrtPrice; + const currentTick = +position.pool.tick; + const tickLower = +position.tickLower.tickIdx; + const tickUpper = +position.tickUpper.tickIdx; + + let reserve0 = 0n; + let reserve1 = 0n; + + if (liquidity === 0) { + return { + reserve0, + reserve1, + }; + } + + const sqrtRatioA = Math.sqrt(1.0001 ** tickLower); + const sqrtRatioB = Math.sqrt(1.0001 ** tickUpper); + const sqrtPrice = _sqrtPrice / 2 ** 96; + + // Onlye return active TVL + if (currentTick < tickLower) { + reserve0 = BigInt( + Math.floor( + liquidity * ((sqrtRatioB - sqrtRatioA) / (sqrtRatioA * sqrtRatioB)) + ) + ); + } else if (currentTick >= tickUpper) { + reserve1 = BigInt(Math.floor(liquidity * (sqrtRatioB - sqrtRatioA))); + } else if (currentTick >= tickLower && currentTick < tickUpper) { + reserve0 = BigInt( + Math.floor( + liquidity * ((sqrtRatioB - sqrtPrice) / (sqrtPrice * sqrtRatioB)) + ) + ); + reserve1 = BigInt(Math.floor(liquidity * (sqrtPrice - sqrtRatioA))); + } + + return { + reserve0, + reserve1, + }; +}; + +export const getV3UserPositionsAtBlock = async ( + blockNumber: number +): Promise => { + const result: UserPosition[] = []; + + let skip = 0; + let fetchNext = true; + while (fetchNext) { + const query = `query { + positions( + first: 1000, + skip: ${skip}, + where: { liquidity_gt: 0 }, + block: { number: ${blockNumber} } + ) { + liquidity + owner + pool { + sqrtPrice + tick + token0 { + id + symbol + } + token1 { + id + symbol + } + token0Price + token1Price + } + tickLower { + tickIdx + } + tickUpper { + tickIdx + } + } + }`; + + const response = await fetch(V3_SUBGRAPH_URL, { + method: "POST", + body: JSON.stringify({ query }), + headers: { "Content-Type": "application/json" }, + }); + + const { + data: { positions }, + } = await response.json(); + + result.push( + ...positions.map((position: V3Position) => { + const { reserve0, reserve1 } = getV3PositionReserves(position); + return { + user: position.owner, + token0: { + address: position.pool.token0.id, + balance: reserve0, + symbol: position.pool.token0.symbol, + usdPrice: +position.pool.token0Price, + }, + token1: { + address: position.pool.token1.id, + balance: reserve1, + symbol: position.pool.token1.symbol, + usdPrice: +position.pool.token1Price, + }, + }; + }) + ); + + if (positions.length < 1000) { + fetchNext = false; + } else { + skip += 1000; + } + } + + return result; +}; + +export const getTimestampAtBlock = async (blockNumber: number) => { + const block = await client.getBlock({ + blockNumber: BigInt(blockNumber), + }); + return Number(block.timestamp * 1000n); +}; diff --git a/adapters/lynex/src/sdk/subgraphDetails.ts b/adapters/lynex/src/sdk/subgraphDetails.ts index 0ea70c75..a0e76c95 100644 --- a/adapters/lynex/src/sdk/subgraphDetails.ts +++ b/adapters/lynex/src/sdk/subgraphDetails.ts @@ -1,6 +1,6 @@ import { createPublicClient, extractChain, http } from "viem"; import { linea } from "viem/chains"; -import { CHAINS, PROTOCOLS, RPC_URLS, SUBGRAPH_URLS } from "./config"; +import { GAUGE_SUBGRAPH_URL, client } from "./config"; interface UserStake { id: string; @@ -10,8 +10,7 @@ interface UserStake { export const getUserAddresses = async ( blockNumber: number ): Promise => { - let subgraphUrl = - SUBGRAPH_URLS[CHAINS.L2_CHAIN_ID][PROTOCOLS.LYNEX][PROTOCOLS.LYNEX]; + let subgraphUrl = GAUGE_SUBGRAPH_URL; let blockQuery = blockNumber !== 0 ? ` block: {number: ${blockNumber}}` : ``; let skip = 0; @@ -70,10 +69,7 @@ export const getUserAddresses = async ( }; export const getTimestampAtBlock = async (blockNumber: number) => { - const publicClient = createPublicClient({ - chain: extractChain({ chains: [linea], id: CHAINS.L2_CHAIN_ID }), - transport: http(RPC_URLS[CHAINS.L2_CHAIN_ID]), - }); + const publicClient = client; const block = await publicClient.getBlock({ blockNumber: BigInt(blockNumber), diff --git a/adapters/lynex/src/sdk/types.ts b/adapters/lynex/src/sdk/types.ts new file mode 100644 index 00000000..2d822b65 --- /dev/null +++ b/adapters/lynex/src/sdk/types.ts @@ -0,0 +1,26 @@ +export interface BlockData { + blockNumber: number; + blockTimestamp: number; +} + +export type OutputSchemaRow = { + block_number: number; + timestamp: number; + user_address: string; + token_address: string; + token_balance: bigint; + token_symbol?: string; + usd_price?: number; +}; + +export type UserPosition = { + user: string; + token0: { + address: string; + balance: bigint; + }; + token1: { + address: string; + balance: bigint; + }; +}; From ed7e2e970d86786ee250cf38e2020ad143a992b9 Mon Sep 17 00:00:00 2001 From: Apeguru Date: Wed, 10 Apr 2024 18:10:22 -0300 Subject: [PATCH 3/3] refactor: Only return active TVL --- adapters/lynex/src/sdk/pools.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/adapters/lynex/src/sdk/pools.ts b/adapters/lynex/src/sdk/pools.ts index 60d3fd31..79c1c880 100644 --- a/adapters/lynex/src/sdk/pools.ts +++ b/adapters/lynex/src/sdk/pools.ts @@ -173,16 +173,8 @@ const getV3PositionReserves = (position: V3Position) => { const sqrtRatioB = Math.sqrt(1.0001 ** tickUpper); const sqrtPrice = _sqrtPrice / 2 ** 96; - // Onlye return active TVL - if (currentTick < tickLower) { - reserve0 = BigInt( - Math.floor( - liquidity * ((sqrtRatioB - sqrtRatioA) / (sqrtRatioA * sqrtRatioB)) - ) - ); - } else if (currentTick >= tickUpper) { - reserve1 = BigInt(Math.floor(liquidity * (sqrtRatioB - sqrtRatioA))); - } else if (currentTick >= tickLower && currentTick < tickUpper) { + // Only return active TVL + if (currentTick >= tickLower && currentTick < tickUpper) { reserve0 = BigInt( Math.floor( liquidity * ((sqrtRatioB - sqrtPrice) / (sqrtPrice * sqrtRatioB))