Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/constants/raw/pinto-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ const contracts = {
PINTOCBBTC: ['0x3e11226fe3d85142B734ABCe6e58918d5828d1b4', 18, wellAbi],
PINTOWSOL: ['0x3e11444c7650234c748D743D8d374fcE2eE5E6C9', 18, wellAbi],
PINTOUSDC: ['0x3e1133aC082716DDC3114bbEFEeD8B1731eA9cb1', 18, wellAbi],
PINTOWSTETH: ['0x3e1155245FF9a6a019Bc35827e801c6ED2CE91b9', 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: ['0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452', 18, erc20Abi],
CP2: ['0xBA510C289fD067EBbA41335afa11F0591940d6fe', null, wellFunctionAbi],
STABLE2: ['0xBA51055a97b40d7f41f3F64b57469b5D45B67c87', null, wellFunctionAbi],
SOW_V0: ['0xbb0a41927895F8ca2b4ECCc659ba158735fCF28B', null, sowBlueprintV0Abi],
Expand Down
97 changes: 97 additions & 0 deletions src/repository/postgres/seeders/20251110193438-whitelist-wsteth.js
Original file line number Diff line number Diff line change
@@ -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
}
});
}
}
};
4 changes: 1 addition & 3 deletions src/repository/subgraph/basin-subgraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ 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: ' ',
Expand Down
10 changes: 0 additions & 10 deletions src/service/exchange-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/service/price-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => ({
Expand Down
3 changes: 2 additions & 1 deletion src/service/tractor/blueprints/blueprint-constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class BlueprintConstants {
[C().PINTOCBETH]: 2,
[C().PINTOCBBTC]: 3,
[C().PINTOUSDC]: 4,
[C().PINTOWSOL]: 5
[C().PINTOWSOL]: 5,
[C().PINTOWSTETH]: 6
};
}

Expand Down
14 changes: 5 additions & 9 deletions test/service/exchange-service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -25,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]
},
Expand Down Expand Up @@ -81,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}`,
Expand Down
9 changes: 8 additions & 1 deletion test/util/mock-constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down