Skip to content

Commit 4816a35

Browse files
authored
Merge pull request #50 from pinto-org/pp/whitelist-wsteth
Support upcoming wsteth well
2 parents f77fe9b + 6f52cf4 commit 4816a35

8 files changed

Lines changed: 116 additions & 25 deletions

File tree

src/constants/raw/pinto-base.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ const contracts = {
2222
PINTOCBBTC: ['0x3e11226fe3d85142B734ABCe6e58918d5828d1b4', 18, wellAbi],
2323
PINTOWSOL: ['0x3e11444c7650234c748D743D8d374fcE2eE5E6C9', 18, wellAbi],
2424
PINTOUSDC: ['0x3e1133aC082716DDC3114bbEFEeD8B1731eA9cb1', 18, wellAbi],
25+
PINTOWSTETH: ['0x3e1155245FF9a6a019Bc35827e801c6ED2CE91b9', 18, wellAbi],
2526
SPINTO: ['0x00b174d66ada7d63789087f50a9b9e0e48446dc1', 18, wrappedDepositAbi],
2627
WETH: ['0x4200000000000000000000000000000000000006', 18, erc20Abi],
2728
CBETH: ['0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22', 18, erc20Abi],
2829
CBBTC: ['0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf', 8, erc20Abi],
2930
WSOL: ['0x1C61629598e4a901136a81BC138E5828dc150d67', 9, erc20Abi],
3031
USDC: ['0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', 6, erc20Abi],
32+
WSTETH: ['0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452', 18, erc20Abi],
3133
CP2: ['0xBA510C289fD067EBbA41335afa11F0591940d6fe', null, wellFunctionAbi],
3234
STABLE2: ['0xBA51055a97b40d7f41f3F64b57469b5D45B67c87', null, wellFunctionAbi],
3335
SOW_V0: ['0xbb0a41927895F8ca2b4ECCc659ba158735fCF28B', null, sowBlueprintV0Abi],
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
'use strict';
2+
3+
const db = require('../models');
4+
const { C } = require('../../../constants/runtime-constants');
5+
const AlchemyUtil = require('../../../datasources/alchemy');
6+
const PromiseUtil = require('../../../utils/async/promise');
7+
const Contracts = require('../../../datasources/contracts/contracts');
8+
const EnvUtil = require('../../../utils/env');
9+
const { TOKEN_TABLE } = require('../../../constants/tables');
10+
11+
// This was copied from silo-tokens-base.js and modified to include only pintowsteth.
12+
13+
/** @type {import('sequelize-cli').Migration} */
14+
module.exports = {
15+
async up(queryInterface, Sequelize) {
16+
if (!EnvUtil.isChainEnabled('base')) {
17+
console.log(`Skipping seeder: chain 'base' is not enabled.`);
18+
return;
19+
}
20+
const c = C('base');
21+
const tokens = [c.PINTOWSTETH];
22+
23+
// Add base tokens
24+
await AlchemyUtil.ready(c.CHAIN);
25+
const beanstalk = Contracts.getBeanstalk(c);
26+
27+
// Gets tokens that have already been populated
28+
const existingTokens = await db.sequelize.models.Token.findAll({
29+
where: {
30+
chain: c.CHAIN,
31+
address: {
32+
[Sequelize.Op.in]: tokens
33+
}
34+
},
35+
attributes: ['address']
36+
});
37+
38+
// Add new tokens only
39+
const newTokens = tokens.filter((token) => !existingTokens.some((t) => t.address === token));
40+
if (newTokens.length > 0) {
41+
const rows = [];
42+
for (const token of newTokens) {
43+
const erc20 = Contracts.get(token, c);
44+
const [name, symbol, supply, decimals] = await Promise.all([
45+
erc20.name(),
46+
erc20.symbol(),
47+
erc20.totalSupply(),
48+
(async () => Number(await erc20.decimals()))()
49+
]);
50+
const [bdv, stalkEarnedPerSeason, stemTip, totalDeposited, totalDepositedBdv] = await Promise.all(
51+
[
52+
PromiseUtil.defaultOnReject(1n)(beanstalk.bdv(token, BigInt(10 ** decimals))),
53+
(async () => {
54+
const tokenSettings = await beanstalk.tokenSettings(token);
55+
return tokenSettings.stalkEarnedPerSeason;
56+
})(),
57+
beanstalk.stemTipForToken(token),
58+
beanstalk.getTotalDeposited(token),
59+
beanstalk.getTotalDepositedBdv(token)
60+
// If any revert, they return null instead
61+
].map(PromiseUtil.defaultOnReject(null))
62+
);
63+
rows.push({
64+
address: token,
65+
chain: c.CHAIN,
66+
name,
67+
symbol,
68+
supply,
69+
decimals,
70+
isWhitelisted: true,
71+
bdv,
72+
stalkEarnedPerSeason,
73+
stemTip,
74+
totalDeposited,
75+
totalDepositedBdv,
76+
createdAt: new Date(),
77+
updatedAt: new Date()
78+
});
79+
}
80+
81+
await queryInterface.bulkInsert(TOKEN_TABLE.env, rows);
82+
}
83+
},
84+
85+
async down(queryInterface, Sequelize) {
86+
if (EnvUtil.isChainEnabled('base')) {
87+
const c = C('base');
88+
const tokens = [c.PINTOWSTETH];
89+
// Delete pinto tokens
90+
await queryInterface.bulkDelete(TOKEN_TABLE.env, {
91+
address: {
92+
[Sequelize.Op.in]: tokens
93+
}
94+
});
95+
}
96+
}
97+
};

src/repository/subgraph/basin-subgraph.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ class BasinSubgraphRepository {
1515
}
1616
}`,
1717
`block: {number: ${blockNumber}}`,
18-
// The exchange subgraph needs to update to indiate isBeanstalk or wasBeanstalk (for dewhitelisted)
19-
'',
20-
// 'isBeanstalk: true',
18+
'isBeanstalk: true',
2119
{
2220
field: 'symbol',
2321
lastValue: ' ',

src/service/exchange-service.js

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,6 @@ class ExchangeService {
2323
BasinSubgraphRepository.getAllWells(block.number),
2424
BasinSubgraphRepository.getAllTrades(block.timestamp - ONE_DAY, block.timestamp)
2525
]);
26-
// The exchange subgraph needs to update to indiate isBeanstalk or wasBeanstalk (for dewhitelisted)
27-
// Until then allWells must manually filter out pools
28-
if (C().PROJECT === 'pinto') {
29-
const allWellAddresses = Object.keys(allWells);
30-
for (const wellAddress of allWellAddresses) {
31-
if (![C().PINTOWETH, C().PINTOCBETH, C().PINTOCBBTC, C().PINTOUSDC, C().PINTOWSOL].includes(wellAddress)) {
32-
delete allWells[wellAddress];
33-
}
34-
}
35-
}
3626
const allPriceEvents = ExchangeService.priceEventsByWell(allWells, allTrades);
3727

3828
// For each well in the subgraph, construct a formatted response

src/service/price-service.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class PriceService {
4646
static #getPriceFunction(token) {
4747
if (token === C().BEAN) {
4848
return PriceService.getBeanPrice;
49-
} else if ([C().WETH, C().CBETH, C().CBBTC, C().WSOL, C().USDC].includes(token)) {
49+
} else if ([C().WETH, C().CBETH, C().CBBTC, C().WSOL, C().USDC, C().WSTETH].includes(token)) {
5050
return (options) => PriceService.getUsdOracleTokenPrice(token, options);
5151
}
5252
return () => ({

src/service/tractor/blueprints/blueprint-constants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ class BlueprintConstants {
88
[C().PINTOCBETH]: 2,
99
[C().PINTOCBBTC]: 3,
1010
[C().PINTOUSDC]: 4,
11-
[C().PINTOWSOL]: 5
11+
[C().PINTOWSOL]: 5,
12+
[C().PINTOWSTETH]: 6
1213
};
1314
}
1415

test/service/exchange-service.test.js

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ const ERC20Info = require('../../src/datasources/erc20-info');
1313
const BasinSubgraphRepository = require('../../src/repository/subgraph/basin-subgraph');
1414
const TradeDto = require('../../src/repository/dto/TradeDto');
1515

16-
const testTimestamp = 1715020584;
17-
1816
describe('ExchangeService', () => {
1917
beforeEach(() => {
2018
mockBeanstalkConstants();
@@ -25,12 +23,12 @@ describe('ExchangeService', () => {
2523

2624
it('should return all Basin tickers in the expected format', async () => {
2725
const wellsResponse = require('../mock-responses/subgraph/basin/wells.json');
28-
jest.spyOn(mockBasinSG, 'request').mockResolvedValueOnce(wellsResponse);
26+
jest.spyOn(mockBasinSG, 'request').mockResolvedValue(wellsResponse);
2927
// In practice these 2 values are not necessary since the subsequent getWellPriceRange is also mocked.
30-
jest.spyOn(BasinSubgraphRepository, 'getAllTrades').mockResolvedValueOnce(undefined);
28+
jest.spyOn(BasinSubgraphRepository, 'getAllTrades').mockResolvedValue(undefined);
3129
jest.spyOn(ExchangeService, 'priceEventsByWell').mockReturnValueOnce(undefined);
32-
jest.spyOn(LiquidityUtil, 'calcWellLiquidityUSD').mockResolvedValueOnce(27491579.59267346);
33-
jest.spyOn(LiquidityUtil, 'calcDepth').mockResolvedValueOnce({
30+
jest.spyOn(LiquidityUtil, 'calcWellLiquidityUSD').mockResolvedValue(27491579.59267346);
31+
jest.spyOn(LiquidityUtil, 'calcDepth').mockResolvedValue({
3432
buy: {
3533
float: [135736.220357, 52.83352694098683]
3634
},
@@ -81,9 +79,7 @@ describe('ExchangeService', () => {
8179
});
8280

8381
test('Returns swap history', async () => {
84-
jest
85-
.spyOn(mockBasinSG, 'request')
86-
.mockResolvedValueOnce(require('../mock-responses/subgraph/basin/swapHistory.json'));
82+
jest.spyOn(mockBasinSG, 'request').mockResolvedValue(require('../mock-responses/subgraph/basin/swapHistory.json'));
8783

8884
const options = {
8985
ticker_id: `${BEAN}_${WETH}`,

test/util/mock-constants.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,19 @@ function mockPintoERC20s() {
4646
symbol: 'U-PINTOWSOLCP2w',
4747
decimals: 18
4848
},
49+
{
50+
token: C().PINTOWSTETH,
51+
name: 'PINTO:WSTETH Constant Product 2 Upgradeable Well',
52+
symbol: 'U-PINTOWSTETHC2w',
53+
decimals: 18
54+
},
4955
{ token: C().PINTOUSDC, name: 'PINTO:USDC Stable 2 Upgradeable Well', symbol: 'U-PINTOUSDCS2w', decimals: 18 },
5056
{ token: C().WETH, name: 'Wrapped Ether', symbol: 'WETH', decimals: 18 },
5157
{ token: C().CBETH, name: 'Coinbase Wrapped Staked ETH', symbol: 'cbETH', decimals: 18 },
5258
{ token: C().CBBTC, name: 'Coinbase Wrapped BTC', symbol: 'cbBTC', decimals: 8 },
5359
{ token: C().WSOL, name: 'Wrapped SOL', symbol: 'SOL', decimals: 9 },
54-
{ token: C().USDC, name: 'USD Coin', symbol: 'USDC', decimals: 6 }
60+
{ token: C().USDC, name: 'USD Coin', symbol: 'USDC', decimals: 6 },
61+
{ token: C().WSTETH, name: 'Wrapped Staked ETH', symbol: 'WSTETH', decimals: 18 }
5562
];
5663

5764
jest.spyOn(ERC20Info, 'getTokenInfo').mockImplementation((token) => {

0 commit comments

Comments
 (0)