From 322676eb3213b404db6316205940b4d86872a727 Mon Sep 17 00:00:00 2001 From: PintoPirate <189064953+PintoPirate@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:15:43 -0500 Subject: [PATCH 1/5] Add wsteth constants (actual values tbd) --- src/constants/raw/pinto-base.js | 2 ++ .../seeders/20241118003615-silo-tokens-base.js | 4 ++-- src/repository/subgraph/basin-subgraph.js | 3 +-- src/service/exchange-service.js | 10 ---------- src/service/price-service.js | 2 +- src/service/tractor/blueprints/blueprint-constants.js | 3 ++- test/service/exchange-service.test.js | 2 -- test/util/mock-constants.js | 9 ++++++++- 8 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/constants/raw/pinto-base.js b/src/constants/raw/pinto-base.js index cd83a28..4962914 100644 --- a/src/constants/raw/pinto-base.js +++ b/src/constants/raw/pinto-base.js @@ -22,12 +22,14 @@ const contracts = { PINTOCBBTC: ['0x3e11226fe3d85142B734ABCe6e58918d5828d1b4', 18, wellAbi], PINTOWSOL: ['0x3e11444c7650234c748D743D8d374fcE2eE5E6C9', 18, wellAbi], PINTOUSDC: ['0x3e1133aC082716DDC3114bbEFEeD8B1731eA9cb1', 18, wellAbi], + PINTOWSTETH: ['TODO(wsteth)', 18, wellAbi], SPINTO: ['0x00b174d66ada7d63789087f50a9b9e0e48446dc1', 18, wrappedDepositAbi], WETH: ['0x4200000000000000000000000000000000000006', 18, erc20Abi], CBETH: ['0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22', 18, erc20Abi], CBBTC: ['0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf', 8, erc20Abi], WSOL: ['0x1C61629598e4a901136a81BC138E5828dc150d67', 9, erc20Abi], USDC: ['0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', 6, erc20Abi], + WSTETH: ['TODO(wsteth)', 18, erc20Abi], CP2: ['0xBA510C289fD067EBbA41335afa11F0591940d6fe', null, wellFunctionAbi], STABLE2: ['0xBA51055a97b40d7f41f3F64b57469b5D45B67c87', null, wellFunctionAbi], SOW_V0: ['0xbb0a41927895F8ca2b4ECCc659ba158735fCF28B', null, sowBlueprintV0Abi], diff --git a/src/repository/postgres/seeders/20241118003615-silo-tokens-base.js b/src/repository/postgres/seeders/20241118003615-silo-tokens-base.js index e925bb1..0c667a0 100644 --- a/src/repository/postgres/seeders/20241118003615-silo-tokens-base.js +++ b/src/repository/postgres/seeders/20241118003615-silo-tokens-base.js @@ -16,7 +16,7 @@ module.exports = { return; } const c = C('base'); - const tokens = [c.BEAN, c.PINTOWETH, c.PINTOCBETH, c.PINTOCBBTC, c.PINTOWSOL, c.PINTOUSDC]; + const tokens = [c.BEAN, c.PINTOWETH, c.PINTOCBETH, c.PINTOCBBTC, c.PINTOWSOL, c.PINTOUSDC, c.PINTOWSTETH]; // Add base tokens await AlchemyUtil.ready(c.CHAIN); @@ -83,7 +83,7 @@ module.exports = { async down(queryInterface, Sequelize) { if (EnvUtil.isChainEnabled('base')) { const c = C('base'); - const tokens = [c.BEAN, c.PINTOWETH, c.PINTOCBETH, c.PINTOCBBTC, c.PINTOWSOL, c.PINTOUSDC]; + const tokens = [c.BEAN, c.PINTOWETH, c.PINTOCBETH, c.PINTOCBBTC, c.PINTOWSOL, c.PINTOUSDC, c.PINTOWSTETH]; // Delete pinto tokens await queryInterface.bulkDelete(TOKEN_TABLE.env, { address: { diff --git a/src/repository/subgraph/basin-subgraph.js b/src/repository/subgraph/basin-subgraph.js index 0285893..ef6cdf0 100644 --- a/src/repository/subgraph/basin-subgraph.js +++ b/src/repository/subgraph/basin-subgraph.js @@ -15,9 +15,8 @@ class BasinSubgraphRepository { } }`, `block: {number: ${blockNumber}}`, - // The exchange subgraph needs to update to indiate isBeanstalk or wasBeanstalk (for dewhitelisted) '', - // 'isBeanstalk: true', + 'isBeanstalk: true', { field: 'symbol', lastValue: ' ', diff --git a/src/service/exchange-service.js b/src/service/exchange-service.js index 70ad104..793b0ba 100644 --- a/src/service/exchange-service.js +++ b/src/service/exchange-service.js @@ -23,16 +23,6 @@ class ExchangeService { BasinSubgraphRepository.getAllWells(block.number), BasinSubgraphRepository.getAllTrades(block.timestamp - ONE_DAY, block.timestamp) ]); - // The exchange subgraph needs to update to indiate isBeanstalk or wasBeanstalk (for dewhitelisted) - // Until then allWells must manually filter out pools - if (C().PROJECT === 'pinto') { - const allWellAddresses = Object.keys(allWells); - for (const wellAddress of allWellAddresses) { - if (![C().PINTOWETH, C().PINTOCBETH, C().PINTOCBBTC, C().PINTOUSDC, C().PINTOWSOL].includes(wellAddress)) { - delete allWells[wellAddress]; - } - } - } const allPriceEvents = ExchangeService.priceEventsByWell(allWells, allTrades); // For each well in the subgraph, construct a formatted response diff --git a/src/service/price-service.js b/src/service/price-service.js index e9e8b3a..4ed5e2e 100644 --- a/src/service/price-service.js +++ b/src/service/price-service.js @@ -46,7 +46,7 @@ class PriceService { static #getPriceFunction(token) { if (token === C().BEAN) { return PriceService.getBeanPrice; - } else if ([C().WETH, C().CBETH, C().CBBTC, C().WSOL, C().USDC].includes(token)) { + } else if ([C().WETH, C().CBETH, C().CBBTC, C().WSOL, C().USDC, C().WSTETH].includes(token)) { return (options) => PriceService.getUsdOracleTokenPrice(token, options); } return () => ({ diff --git a/src/service/tractor/blueprints/blueprint-constants.js b/src/service/tractor/blueprints/blueprint-constants.js index 115156b..ade8bb3 100644 --- a/src/service/tractor/blueprints/blueprint-constants.js +++ b/src/service/tractor/blueprints/blueprint-constants.js @@ -8,7 +8,8 @@ class BlueprintConstants { [C().PINTOCBETH]: 2, [C().PINTOCBBTC]: 3, [C().PINTOUSDC]: 4, - [C().PINTOWSOL]: 5 + [C().PINTOWSOL]: 5, + [C().PINTOWSTETH]: 6 }; } diff --git a/test/service/exchange-service.test.js b/test/service/exchange-service.test.js index 3713cec..7cc1c74 100644 --- a/test/service/exchange-service.test.js +++ b/test/service/exchange-service.test.js @@ -13,8 +13,6 @@ const ERC20Info = require('../../src/datasources/erc20-info'); const BasinSubgraphRepository = require('../../src/repository/subgraph/basin-subgraph'); const TradeDto = require('../../src/repository/dto/TradeDto'); -const testTimestamp = 1715020584; - describe('ExchangeService', () => { beforeEach(() => { mockBeanstalkConstants(); diff --git a/test/util/mock-constants.js b/test/util/mock-constants.js index 4aeaab1..4bfa05e 100644 --- a/test/util/mock-constants.js +++ b/test/util/mock-constants.js @@ -46,12 +46,19 @@ function mockPintoERC20s() { symbol: 'U-PINTOWSOLCP2w', decimals: 18 }, + { + token: C().PINTOWSTETH, + name: 'PINTO:WSTETH Constant Product 2 Upgradeable Well', + symbol: 'U-PINTOWSTETHC2w', + decimals: 18 + }, { token: C().PINTOUSDC, name: 'PINTO:USDC Stable 2 Upgradeable Well', symbol: 'U-PINTOUSDCS2w', decimals: 18 }, { token: C().WETH, name: 'Wrapped Ether', symbol: 'WETH', decimals: 18 }, { token: C().CBETH, name: 'Coinbase Wrapped Staked ETH', symbol: 'cbETH', decimals: 18 }, { token: C().CBBTC, name: 'Coinbase Wrapped BTC', symbol: 'cbBTC', decimals: 8 }, { token: C().WSOL, name: 'Wrapped SOL', symbol: 'SOL', decimals: 9 }, - { token: C().USDC, name: 'USD Coin', symbol: 'USDC', decimals: 6 } + { token: C().USDC, name: 'USD Coin', symbol: 'USDC', decimals: 6 }, + { token: C().WSTETH, name: 'Wrapped Staked ETH', symbol: 'WSTETH', decimals: 18 } ]; jest.spyOn(ERC20Info, 'getTokenInfo').mockImplementation((token) => { From aac9c9f48995e62529df7f3a5e9e11a0241b7297 Mon Sep 17 00:00:00 2001 From: PintoPirate <189064953+PintoPirate@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:27:26 -0500 Subject: [PATCH 2/5] Fix tests --- src/repository/subgraph/basin-subgraph.js | 1 - test/service/exchange-service.test.js | 12 +++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/repository/subgraph/basin-subgraph.js b/src/repository/subgraph/basin-subgraph.js index ef6cdf0..e45f08e 100644 --- a/src/repository/subgraph/basin-subgraph.js +++ b/src/repository/subgraph/basin-subgraph.js @@ -15,7 +15,6 @@ class BasinSubgraphRepository { } }`, `block: {number: ${blockNumber}}`, - '', 'isBeanstalk: true', { field: 'symbol', diff --git a/test/service/exchange-service.test.js b/test/service/exchange-service.test.js index 7cc1c74..104fb95 100644 --- a/test/service/exchange-service.test.js +++ b/test/service/exchange-service.test.js @@ -23,12 +23,12 @@ describe('ExchangeService', () => { it('should return all Basin tickers in the expected format', async () => { const wellsResponse = require('../mock-responses/subgraph/basin/wells.json'); - jest.spyOn(mockBasinSG, 'request').mockResolvedValueOnce(wellsResponse); + jest.spyOn(mockBasinSG, 'request').mockResolvedValue(wellsResponse); // In practice these 2 values are not necessary since the subsequent getWellPriceRange is also mocked. - jest.spyOn(BasinSubgraphRepository, 'getAllTrades').mockResolvedValueOnce(undefined); + jest.spyOn(BasinSubgraphRepository, 'getAllTrades').mockResolvedValue(undefined); jest.spyOn(ExchangeService, 'priceEventsByWell').mockReturnValueOnce(undefined); - jest.spyOn(LiquidityUtil, 'calcWellLiquidityUSD').mockResolvedValueOnce(27491579.59267346); - jest.spyOn(LiquidityUtil, 'calcDepth').mockResolvedValueOnce({ + jest.spyOn(LiquidityUtil, 'calcWellLiquidityUSD').mockResolvedValue(27491579.59267346); + jest.spyOn(LiquidityUtil, 'calcDepth').mockResolvedValue({ buy: { float: [135736.220357, 52.83352694098683] }, @@ -79,9 +79,7 @@ describe('ExchangeService', () => { }); test('Returns swap history', async () => { - jest - .spyOn(mockBasinSG, 'request') - .mockResolvedValueOnce(require('../mock-responses/subgraph/basin/swapHistory.json')); + jest.spyOn(mockBasinSG, 'request').mockResolvedValue(require('../mock-responses/subgraph/basin/swapHistory.json')); const options = { ticker_id: `${BEAN}_${WETH}`, From 49f6b4d38d94dce51c3cd7255d10575a67ade383 Mon Sep 17 00:00:00 2001 From: PintoPirate <189064953+PintoPirate@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:36:58 -0500 Subject: [PATCH 3/5] pintowsteth token seeder --- .../20241118003615-silo-tokens-base.js | 4 +- .../20251110193438-whitelist-wsteth.js | 97 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 src/repository/postgres/seeders/20251110193438-whitelist-wsteth.js diff --git a/src/repository/postgres/seeders/20241118003615-silo-tokens-base.js b/src/repository/postgres/seeders/20241118003615-silo-tokens-base.js index 0c667a0..e925bb1 100644 --- a/src/repository/postgres/seeders/20241118003615-silo-tokens-base.js +++ b/src/repository/postgres/seeders/20241118003615-silo-tokens-base.js @@ -16,7 +16,7 @@ module.exports = { return; } const c = C('base'); - const tokens = [c.BEAN, c.PINTOWETH, c.PINTOCBETH, c.PINTOCBBTC, c.PINTOWSOL, c.PINTOUSDC, c.PINTOWSTETH]; + const tokens = [c.BEAN, c.PINTOWETH, c.PINTOCBETH, c.PINTOCBBTC, c.PINTOWSOL, c.PINTOUSDC]; // Add base tokens await AlchemyUtil.ready(c.CHAIN); @@ -83,7 +83,7 @@ module.exports = { async down(queryInterface, Sequelize) { if (EnvUtil.isChainEnabled('base')) { const c = C('base'); - const tokens = [c.BEAN, c.PINTOWETH, c.PINTOCBETH, c.PINTOCBBTC, c.PINTOWSOL, c.PINTOUSDC, c.PINTOWSTETH]; + const tokens = [c.BEAN, c.PINTOWETH, c.PINTOCBETH, c.PINTOCBBTC, c.PINTOWSOL, c.PINTOUSDC]; // Delete pinto tokens await queryInterface.bulkDelete(TOKEN_TABLE.env, { address: { diff --git a/src/repository/postgres/seeders/20251110193438-whitelist-wsteth.js b/src/repository/postgres/seeders/20251110193438-whitelist-wsteth.js new file mode 100644 index 0000000..5d45925 --- /dev/null +++ b/src/repository/postgres/seeders/20251110193438-whitelist-wsteth.js @@ -0,0 +1,97 @@ +'use strict'; + +const db = require('../models'); +const { C } = require('../../../constants/runtime-constants'); +const AlchemyUtil = require('../../../datasources/alchemy'); +const PromiseUtil = require('../../../utils/async/promise'); +const Contracts = require('../../../datasources/contracts/contracts'); +const EnvUtil = require('../../../utils/env'); +const { TOKEN_TABLE } = require('../../../constants/tables'); + +// This was copied from silo-tokens-base.js and modified to include only pintowsteth. + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + if (!EnvUtil.isChainEnabled('base')) { + console.log(`Skipping seeder: chain 'base' is not enabled.`); + return; + } + const c = C('base'); + const tokens = [c.PINTOWSTETH]; + + // Add base tokens + await AlchemyUtil.ready(c.CHAIN); + const beanstalk = Contracts.getBeanstalk(c); + + // Gets tokens that have already been populated + const existingTokens = await db.sequelize.models.Token.findAll({ + where: { + chain: c.CHAIN, + address: { + [Sequelize.Op.in]: tokens + } + }, + attributes: ['address'] + }); + + // Add new tokens only + const newTokens = tokens.filter((token) => !existingTokens.some((t) => t.address === token)); + if (newTokens.length > 0) { + const rows = []; + for (const token of newTokens) { + const erc20 = Contracts.get(token, c); + const [name, symbol, supply, decimals] = await Promise.all([ + erc20.name(), + erc20.symbol(), + erc20.totalSupply(), + (async () => Number(await erc20.decimals()))() + ]); + const [bdv, stalkEarnedPerSeason, stemTip, totalDeposited, totalDepositedBdv] = await Promise.all( + [ + PromiseUtil.defaultOnReject(1n)(beanstalk.bdv(token, BigInt(10 ** decimals))), + (async () => { + const tokenSettings = await beanstalk.tokenSettings(token); + return tokenSettings.stalkEarnedPerSeason; + })(), + beanstalk.stemTipForToken(token), + beanstalk.getTotalDeposited(token), + beanstalk.getTotalDepositedBdv(token) + // If any revert, they return null instead + ].map(PromiseUtil.defaultOnReject(null)) + ); + rows.push({ + address: token, + chain: c.CHAIN, + name, + symbol, + supply, + decimals, + isWhitelisted: true, + bdv, + stalkEarnedPerSeason, + stemTip, + totalDeposited, + totalDepositedBdv, + createdAt: new Date(), + updatedAt: new Date() + }); + } + + await queryInterface.bulkInsert(TOKEN_TABLE.env, rows); + } + }, + + async down(queryInterface, Sequelize) { + if (EnvUtil.isChainEnabled('base')) { + const c = C('base'); + const tokens = [c.PINTOWSTETH]; + // Delete pinto tokens + await queryInterface.bulkDelete(TOKEN_TABLE.env, { + address: { + [Sequelize.Op.in]: tokens + } + }); + } + } +}; From d94298b0f2f6e30bcc84e96e4c98b132ee2662c4 Mon Sep 17 00:00:00 2001 From: PintoPirate <189064953+PintoPirate@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:26:56 -0500 Subject: [PATCH 4/5] Add wsteth addrs --- src/constants/raw/pinto-base.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/constants/raw/pinto-base.js b/src/constants/raw/pinto-base.js index 4962914..ee7062f 100644 --- a/src/constants/raw/pinto-base.js +++ b/src/constants/raw/pinto-base.js @@ -22,14 +22,14 @@ const contracts = { PINTOCBBTC: ['0x3e11226fe3d85142B734ABCe6e58918d5828d1b4', 18, wellAbi], PINTOWSOL: ['0x3e11444c7650234c748D743D8d374fcE2eE5E6C9', 18, wellAbi], PINTOUSDC: ['0x3e1133aC082716DDC3114bbEFEeD8B1731eA9cb1', 18, wellAbi], - PINTOWSTETH: ['TODO(wsteth)', 18, wellAbi], + PINTOWSTETH: ['0x3e1155480Fce43686793dd18E43104A01abCBC92', 18, wellAbi], SPINTO: ['0x00b174d66ada7d63789087f50a9b9e0e48446dc1', 18, wrappedDepositAbi], WETH: ['0x4200000000000000000000000000000000000006', 18, erc20Abi], CBETH: ['0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22', 18, erc20Abi], CBBTC: ['0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf', 8, erc20Abi], WSOL: ['0x1C61629598e4a901136a81BC138E5828dc150d67', 9, erc20Abi], USDC: ['0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', 6, erc20Abi], - WSTETH: ['TODO(wsteth)', 18, erc20Abi], + WSTETH: ['0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452', 18, erc20Abi], CP2: ['0xBA510C289fD067EBbA41335afa11F0591940d6fe', null, wellFunctionAbi], STABLE2: ['0xBA51055a97b40d7f41f3F64b57469b5D45B67c87', null, wellFunctionAbi], SOW_V0: ['0xbb0a41927895F8ca2b4ECCc659ba158735fCF28B', null, sowBlueprintV0Abi], From a8b70ffb77e1e2413bfbd3ddf48899c9e5cd1f43 Mon Sep 17 00:00:00 2001 From: PintoPirate <189064953+PintoPirate@users.noreply.github.com> Date: Thu, 20 Nov 2025 12:20:17 -0500 Subject: [PATCH 5/5] Update pintowsteth address --- src/constants/raw/pinto-base.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constants/raw/pinto-base.js b/src/constants/raw/pinto-base.js index ee7062f..ad41dbc 100644 --- a/src/constants/raw/pinto-base.js +++ b/src/constants/raw/pinto-base.js @@ -22,7 +22,7 @@ const contracts = { PINTOCBBTC: ['0x3e11226fe3d85142B734ABCe6e58918d5828d1b4', 18, wellAbi], PINTOWSOL: ['0x3e11444c7650234c748D743D8d374fcE2eE5E6C9', 18, wellAbi], PINTOUSDC: ['0x3e1133aC082716DDC3114bbEFEeD8B1731eA9cb1', 18, wellAbi], - PINTOWSTETH: ['0x3e1155480Fce43686793dd18E43104A01abCBC92', 18, wellAbi], + PINTOWSTETH: ['0x3e1155245FF9a6a019Bc35827e801c6ED2CE91b9', 18, wellAbi], SPINTO: ['0x00b174d66ada7d63789087f50a9b9e0e48446dc1', 18, wrappedDepositAbi], WETH: ['0x4200000000000000000000000000000000000006', 18, erc20Abi], CBETH: ['0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22', 18, erc20Abi],