From 776dd56414fd61f68190ab2b28549959a83fed2b Mon Sep 17 00:00:00 2001 From: Christian Felder Date: Sun, 11 Dec 2022 09:52:55 +0100 Subject: [PATCH 01/23] correct npm homepage url homepage link on npm was kaputt --- balancer-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/package.json b/balancer-js/package.json index ec2f7d82f..86e9737bf 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -3,7 +3,7 @@ "version": "0.1.40", "description": "JavaScript SDK for interacting with the Balancer Protocol V2", "license": "GPL-3.0-only", - "homepage": "https://github.com/balancer-labs/balancer-sdk/balancer-js#readme", + "homepage": "https://github.com/balancer-labs/balancer-sdk#readme", "repository": { "type": "git", "url": "https://github.com/balancer-labs/balancer-sdk", From 6b2b3e3c7392568e149e3f6f6c57f3cf9dd65a59 Mon Sep 17 00:00:00 2001 From: bronco Date: Tue, 18 Oct 2022 03:13:12 +0200 Subject: [PATCH 02/23] fix: docs typo --- .../modules/pools/pool-types/concerns/weighted/join.concern.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/src/modules/pools/pool-types/concerns/weighted/join.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/weighted/join.concern.ts index 2b5eacef7..6a21d7368 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/weighted/join.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/weighted/join.concern.ts @@ -23,7 +23,7 @@ export class WeightedPoolJoin implements JoinConcern { * @param {string[]} params.tokensIn - Token addresses provided for joining pool (same length and order as amountsIn) * @param {string[]} params.amountsIn - - Token amounts provided for joining pool in EVM amounts * @param {string} params.slippage - Maximum slippage tolerance in bps i.e. 50 = 0.5% - * @param {string} wrappedNativeAsset - Address of wrapped native asset for specific network config. Required for joining with ETH. + * @param {string} params.wrappedNativeAsset - Address of wrapped native asset for specific network config. Required for joining with ETH. * @returns transaction request ready to send with signer.sendTransaction */ buildJoin = ({ From 6d49723f54d820b6639aece497c97fd48441e842 Mon Sep 17 00:00:00 2001 From: bronco Date: Tue, 18 Oct 2022 03:06:45 +0200 Subject: [PATCH 03/23] adding balancerHelpers contract --- balancer-js/src/lib/constants/config.ts | 8 ++++++++ balancer-js/src/modules/contracts/contracts.module.ts | 9 +++++++++ balancer-js/src/types.ts | 1 + 3 files changed, 18 insertions(+) diff --git a/balancer-js/src/lib/constants/config.ts b/balancer-js/src/lib/constants/config.ts index 3599191f9..d875025f2 100644 --- a/balancer-js/src/lib/constants/config.ts +++ b/balancer-js/src/lib/constants/config.ts @@ -10,6 +10,7 @@ export const BALANCER_NETWORK_CONFIG: Record = { contracts: { vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', multicall: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441', + balancerHelpers: '0x5aDDCCa35b7A0D07C74063c48700C8590E87864E', lidoRelayer: '0xdcdbf71A870cc60C6F9B621E28a7D3Ffd6Dd4965', relayerV3: '0x886A3Ec7bcC508B8795990B60Fa21f85F9dB7948', relayerV4: '0x2536dfeeCB7A0397CF98eDaDA8486254533b1aFA', @@ -57,6 +58,7 @@ export const BALANCER_NETWORK_CONFIG: Record = { multicall: '0xa1B2b503959aedD81512C37e9dce48164ec6a94d', relayerV3: '0xcf6a66E32dCa0e26AcC3426b851FD8aCbF12Dac7', relayerV4: '0x28A224d9d398a1eBB7BA69BCA515898966Bb1B6b', + balancerHelpers: '0x239e55F427D44C3cc793f49bFB507ebe76638a2b', }, tokens: { bal: '0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3', @@ -81,6 +83,7 @@ export const BALANCER_NETWORK_CONFIG: Record = { multicall: '0x269ff446d9892c9e19082564df3f5e8741e190a1', relayerV3: '0x42E49B48573c725ee32d2579060Ed06894f97002', relayerV4: '0x5bf3B7c14b10f16939d63Bd679264A1Aa951B4D5', + balancerHelpers: '0x77d46184d22CA6a3726a2F500c776767b6A3d6Ab', }, tokens: { bal: '0x040d1edc9569d4bab2d15287dc5a4f10f56a56b8', @@ -105,6 +108,7 @@ export const BALANCER_NETWORK_CONFIG: Record = { multicall: '0x2cc8688C5f75E365aaEEb4ea8D6a480405A48D2A', veBal: '0x16ba924752EF283C7946db8A122a6742AA35C1DC', veBalProxy: '0x98D0d0a65cBeCCaa647a5a95cf27Cf2f00E1231C', + balancerHelpers: '0x94905e703fEAd7f0fD0eEe355D267eE909784e6d', }, tokens: { wrappedNativeAsset: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', @@ -124,6 +128,7 @@ export const BALANCER_NETWORK_CONFIG: Record = { contracts: { vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', multicall: '0x53c43764255c17bd724f74c4ef150724ac50a3ed', + balancerHelpers: '', }, tokens: { wrappedNativeAsset: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', @@ -142,6 +147,7 @@ export const BALANCER_NETWORK_CONFIG: Record = { contracts: { vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', multicall: '0x42ad527de7d4e9d9d011ac45b31d8551f8fe9821', + balancerHelpers: '0x5aDDCCa35b7A0D07C74063c48700C8590E87864E', }, tokens: { wrappedNativeAsset: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', @@ -166,6 +172,7 @@ export const BALANCER_NETWORK_CONFIG: Record = { gaugeController: '0xBB1CE49b16d55A1f2c6e88102f32144C7334B116', veBal: '0x33A99Dcc4C85C014cf12626959111D5898bbCAbF', veBalProxy: '0xA1F107D1cD709514AE8A914eCB757E95f9cedB31', + balancerHelpers: '0x5aDDCCa35b7A0D07C74063c48700C8590E87864E', }, tokens: { wrappedNativeAsset: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', @@ -189,6 +196,7 @@ export const BALANCER_NETWORK_CONFIG: Record = { multicall: '0x2dc0e2aa608532da689e89e237df582b783e552c', relayerV3: '0x195CcCBE464EF9073d1f7A1ba1C9Bf0f56dfFFff', relayerV4: '0x1a58897Ab366082028ced3740900ecBD765Af738', + balancerHelpers: '0x8E9aa87E45e92bad84D5F8DD1bff34Fb92637dE9', }, tokens: { wrappedNativeAsset: '0x4200000000000000000000000000000000000006', diff --git a/balancer-js/src/modules/contracts/contracts.module.ts b/balancer-js/src/modules/contracts/contracts.module.ts index 2240e7389..b83fc3443 100644 --- a/balancer-js/src/modules/contracts/contracts.module.ts +++ b/balancer-js/src/modules/contracts/contracts.module.ts @@ -9,6 +9,8 @@ import { Vault, LidoRelayer__factory, LidoRelayer, + BalancerHelpers, + BalancerHelpers__factory, } from '@balancer-labs/typechain'; import { Multicall } from './implementations/multicall'; import { ERC20 } from './implementations/ERC20'; @@ -24,6 +26,7 @@ type ContractFactory = ( export interface ContractInstances { vault: Vault; + balancerHelpers: BalancerHelpers; lidoRelayer?: LidoRelayer; multicall: Contract; relayerV3?: Contract; @@ -37,6 +40,7 @@ export interface ContractInstances { export class Contracts { contractAddresses: ContractAddresses; vault: Vault; + balancerHelpers: BalancerHelpers; lidoRelayer?: LidoRelayer; multicall: Contract; relayerV3?: Contract; @@ -62,6 +66,10 @@ export class Contracts { } this.vault = Vault__factory.connect(this.contractAddresses.vault, provider); + this.balancerHelpers = BalancerHelpers__factory.connect( + this.contractAddresses.balancerHelpers, + provider + ); if (this.contractAddresses.lidoRelayer) this.lidoRelayer = LidoRelayer__factory.connect( @@ -92,6 +100,7 @@ export class Contracts { get contracts(): ContractInstances { return { vault: this.vault, + balancerHelpers: this.balancerHelpers, lidoRelayer: this.lidoRelayer, multicall: this.multicall, relayerV3: this.relayerV3, diff --git a/balancer-js/src/types.ts b/balancer-js/src/types.ts index 4b4eaf6ef..bc8c0d147 100644 --- a/balancer-js/src/types.ts +++ b/balancer-js/src/types.ts @@ -61,6 +61,7 @@ export interface BalancerSdkSorConfig { export interface ContractAddresses { vault: string; multicall: string; + balancerHelpers: string; lidoRelayer?: string; relayerV3?: string; relayerV4?: string; From 046ead55c0ee7863aa79a1e3714a49366075ef62 Mon Sep 17 00:00:00 2001 From: bronco Date: Tue, 18 Oct 2022 03:07:07 +0200 Subject: [PATCH 04/23] adding bn helper utils --- balancer-js/src/lib/utils/index.ts | 1 + balancer-js/src/lib/utils/math.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/balancer-js/src/lib/utils/index.ts b/balancer-js/src/lib/utils/index.ts index f0c1b91fb..8569022f8 100644 --- a/balancer-js/src/lib/utils/index.ts +++ b/balancer-js/src/lib/utils/index.ts @@ -8,6 +8,7 @@ export * from './aaveHelpers'; export * from './poolHelper'; export * from './tokens'; export * from './debouncer'; +export * from './math'; export const isSameAddress = (address1: string, address2: string): boolean => getAddress(address1) === getAddress(address2); diff --git a/balancer-js/src/lib/utils/math.ts b/balancer-js/src/lib/utils/math.ts index 33497896d..2b35449ff 100644 --- a/balancer-js/src/lib/utils/math.ts +++ b/balancer-js/src/lib/utils/math.ts @@ -28,3 +28,6 @@ export function parseToBigInt18(value: string): bigint { export function formatFromBigInt18(value: bigint): string { return _formatFixed(BigNumber.from(value), 18); } + +export const bn = (value: number): BigNumber => _parseFixed(`${value}`, 18); +export const fp = (value: number): string => bn(value).toString(); From 41d6480128a49badaa66623266f078708d251059 Mon Sep 17 00:00:00 2001 From: bronco Date: Tue, 18 Oct 2022 03:07:25 +0200 Subject: [PATCH 05/23] sdk top level provider --- balancer-js/src/modules/sdk.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/balancer-js/src/modules/sdk.module.ts b/balancer-js/src/modules/sdk.module.ts index 07288810c..7b01f0a12 100644 --- a/balancer-js/src/modules/sdk.module.ts +++ b/balancer-js/src/modules/sdk.module.ts @@ -32,6 +32,7 @@ export class BalancerSDK implements BalancerSDKRoot { balancerContracts: Contracts; zaps: Zaps; readonly networkConfig: BalancerNetworkConfig; + readonly provider: Provider; constructor( public config: BalancerSdkConfig, @@ -39,6 +40,7 @@ export class BalancerSDK implements BalancerSDKRoot { public subgraph = new Subgraph(config) ) { this.networkConfig = getNetworkConfig(config); + this.provider = sor.provider; this.data = new Data(this.networkConfig, sor.provider); this.swaps = new Swaps(this.config); From 00a8637ea1c0c34febd08090f5904cc0dfb58db5 Mon Sep 17 00:00:00 2001 From: bronco Date: Tue, 18 Oct 2022 03:07:54 +0200 Subject: [PATCH 06/23] pools module controller --- balancer-js/src/modules/pools/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/balancer-js/src/modules/pools/index.ts b/balancer-js/src/modules/pools/index.ts index 24a87cf07..c2e6cf32a 100644 --- a/balancer-js/src/modules/pools/index.ts +++ b/balancer-js/src/modules/pools/index.ts @@ -185,6 +185,10 @@ export class Pools implements Findable { return this.volumeService.last24h(pool); } + controller(pool: Pool): PoolWithMethods { + return Pools.wrap(pool, this.networkConfig); + } + static wrap( pool: Pool, networkConfig: BalancerNetworkConfig From 119cd06b0bccf9a0c44eb643f48e56aaa1fe51f6 Mon Sep 17 00:00:00 2001 From: bronco Date: Tue, 15 Nov 2022 16:29:59 +0100 Subject: [PATCH 07/23] queries --- .../src/modules/pools/queries/get_encoder.ts | 33 +++ .../src/modules/pools/queries/index.ts | 1 + .../modules/pools/queries/params_builder.ts | 210 ++++++++++++++++++ .../pools/queries/queries.integration.spec.ts | 113 ++++++++++ .../src/modules/pools/queries/types.ts | 109 +++++++++ 5 files changed, 466 insertions(+) create mode 100644 balancer-js/src/modules/pools/queries/get_encoder.ts create mode 100644 balancer-js/src/modules/pools/queries/index.ts create mode 100644 balancer-js/src/modules/pools/queries/params_builder.ts create mode 100644 balancer-js/src/modules/pools/queries/queries.integration.spec.ts create mode 100644 balancer-js/src/modules/pools/queries/types.ts diff --git a/balancer-js/src/modules/pools/queries/get_encoder.ts b/balancer-js/src/modules/pools/queries/get_encoder.ts new file mode 100644 index 000000000..93be26ea1 --- /dev/null +++ b/balancer-js/src/modules/pools/queries/get_encoder.ts @@ -0,0 +1,33 @@ +import { WeightedPoolEncoder } from '@/pool-weighted/encoder'; +import { StablePoolEncoder } from '@/pool-stable/encoder'; +import { ComposableStablePoolEncoder } from '@/pool-composable-stable'; +import { PoolType } from '@/types'; + +export const getEncoder = ( + poolType: PoolType +): + | typeof WeightedPoolEncoder + | typeof StablePoolEncoder + | typeof ComposableStablePoolEncoder + | undefined => { + switch (poolType) { + case PoolType.Weighted: + return WeightedPoolEncoder; + + case PoolType.Stable: + case PoolType.MetaStable: + case PoolType.StablePhantom: + case PoolType.AaveLinear: + case PoolType.ERC4626Linear: + case PoolType.Element: + case PoolType.Gyro2: + case PoolType.Gyro3: + return StablePoolEncoder; + + case PoolType.ComposableStable: + return ComposableStablePoolEncoder; + + default: + break; + } +}; diff --git a/balancer-js/src/modules/pools/queries/index.ts b/balancer-js/src/modules/pools/queries/index.ts new file mode 100644 index 000000000..52e553a50 --- /dev/null +++ b/balancer-js/src/modules/pools/queries/index.ts @@ -0,0 +1 @@ +export * from './params_builder'; diff --git a/balancer-js/src/modules/pools/queries/params_builder.ts b/balancer-js/src/modules/pools/queries/params_builder.ts new file mode 100644 index 000000000..f1654444e --- /dev/null +++ b/balancer-js/src/modules/pools/queries/params_builder.ts @@ -0,0 +1,210 @@ +import * as PoolQueries from './types'; +import { AddressZero, Zero, MaxUint256 } from '@ethersproject/constants'; +import { getEncoder } from './get_encoder'; + +/** + * Builds parameters quering join / exit liquidity functions in the Balancer Helpers contract. + */ +export class ParamsBuilder implements PoolQueries.ParamsBuilder { + private encoder: PoolQueries.Encoder; + + constructor(private pool: PoolQueries.Pool) { + const encoder = getEncoder(pool.poolType); + if (encoder) { + this.encoder = encoder; + } else { + throw 'Pool type not supported'; + } + } + + /** + * Encodes the query to get expected amount of BPT when joining a Pool with exact token inputs + * + * @param maxAmountsIn - the amounts each of token to deposit in the pool as liquidity, order needs to match pool.tokensList + * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens + */ + buildQueryJoinExactIn({ + sender = AddressZero, + recipient = sender, + maxAmountsIn, + minimumBPT = Zero, + fromInternalBalance = false, + }: PoolQueries.JoinExactInParams): PoolQueries.queryJoinParams { + const bptIndex = this.pool.tokensList.findIndex((token) => + this.pool.id.includes(token) + ); + const assets = [...this.pool.tokensList]; + + // Remove BPT token from amounts + if (bptIndex && bptIndex > -1) { + maxAmountsIn.splice(bptIndex, 1); + } + + const userData = this.encoder.joinExactTokensInForBPTOut( + maxAmountsIn, + minimumBPT + ); + + const params = [ + this.pool.id, + sender, + recipient, + { + assets, + maxAmountsIn, + userData, + fromInternalBalance, + }, + ] as PoolQueries.queryJoinParams; + + return params; + } + + /** + * Encodes the query to get expected token amount when joining a Pool specifying fixed BPT out. + * + * @param maxAmountsIn - max limits of amounts provided as liquidity, can be set to zero, ordered same as pool.tokensList + * @param bptOut - the expected BPT for providing liquidity + * @param tokenIn - address of a token joining the pool + */ + buildQueryJoinExactOut({ + sender = AddressZero, + recipient = sender, + maxAmountsIn = [], + bptOut, + tokenIn, + fromInternalBalance = false, + }: PoolQueries.JoinExactOutParams): PoolQueries.queryJoinParams { + const tokenIndex = this.pool.tokensList.indexOf(tokenIn); + + const userData = this.encoder.joinTokenInForExactBPTOut(bptOut, tokenIndex); + + const params = [ + this.pool.id, + sender, + recipient, + { + assets: this.pool.tokensList, + maxAmountsIn, + userData, + fromInternalBalance, + }, + ] as PoolQueries.queryJoinParams; + + return params; + } + + /** + * Encodes the query for exiting the pool to a single token + * + * @param minAmountsOut - minimum expected amounts, can be set to zero for a query, ordered same as pool.tokensList + * @param bptIn - BPT, shares of the pool liquidity + * @param tokenOut - address of an exit liquidity token + */ + buildQueryExitToSingleToken({ + sender = AddressZero, + recipient = sender, + minAmountsOut = [], + bptIn, + tokenOut, + toInternalBalance = false, + }: PoolQueries.ExitToSingleTokenParams): PoolQueries.queryExitParams { + const tokenIndex = this.pool.tokensList.indexOf(tokenOut); + + const userData = this.encoder.exitExactBPTInForOneTokenOut( + bptIn, + tokenIndex + ); + + const params = [ + this.pool.id, + sender, + recipient, + { + assets: this.pool.tokensList, + minAmountsOut, + userData, + toInternalBalance, + }, + ] as PoolQueries.queryExitParams; + + return params; + } + + /** + * Encodes the query for exiting the pool with all underlying tokens proportionally. + * Not supported by ComposableStable + * + * @param minAmountsOut - minimum expected amounts, can be set to zero for a query, ordered same as pool.tokensList + * @param bptIn - BPT, shares of the pool liquidity + */ + buildQueryExitProportionally({ + sender = AddressZero, + recipient = sender, + minAmountsOut = [], + bptIn, + toInternalBalance = false, + }: PoolQueries.ExitProportionallyParams): PoolQueries.queryExitParams { + if (!this.encoder.exitExactBPTInForTokensOut) { + throw 'Proportional exit not implemented'; + } + + const userData = this.encoder.exitExactBPTInForTokensOut(bptIn); + + const params = [ + this.pool.id, + sender, + recipient, + { + assets: this.pool.tokensList, + minAmountsOut, + userData, + toInternalBalance, + }, + ] as PoolQueries.queryExitParams; + + return params; + } + + /** + * Encodes calldata to query expected BPT for known amounts out. + * + * @param minAmountsOut - minimum expected amounts, ordered same as pool.tokensList + * @param maxBptIn - BPT, shares of the pool liquidity, can be set to zero for a query + */ + buildQueryExitExactOut({ + sender = AddressZero, + recipient = sender, + minAmountsOut, + maxBptIn = MaxUint256, + toInternalBalance = false, + }: PoolQueries.ExitExactOutParams): PoolQueries.queryExitParams { + const bptIndex = this.pool.tokensList.findIndex((token) => + this.pool.id.includes(token) + ); + + // Remove BPT token from amounts + if (bptIndex && bptIndex > -1) { + minAmountsOut.splice(bptIndex, 1); + } + + const userData = this.encoder.exitBPTInForExactTokensOut( + minAmountsOut, + maxBptIn + ); + + const params = [ + this.pool.id, + sender, + recipient, + { + assets: this.pool.tokensList, + minAmountsOut, + userData, + toInternalBalance, + }, + ] as PoolQueries.queryExitParams; + + return params; + } +} diff --git a/balancer-js/src/modules/pools/queries/queries.integration.spec.ts b/balancer-js/src/modules/pools/queries/queries.integration.spec.ts new file mode 100644 index 000000000..92ed346cd --- /dev/null +++ b/balancer-js/src/modules/pools/queries/queries.integration.spec.ts @@ -0,0 +1,113 @@ +import dotenv from 'dotenv'; +import { expect } from 'chai'; +import { BalancerSDK, Network, PoolType } from '@/.'; +import { bn } from '@/lib/utils'; +import { ParamsBuilder } from '.'; + +dotenv.config(); + +const rpcUrl = process.env.ALCHEMY_URL || 'http://127.0.0.1:8545'; +const network = Network.MAINNET; +const sdk = new BalancerSDK({ network, rpcUrl }); +const { contracts } = sdk; + +const stETHPool = { + id: '0x32296969ef14eb0c6d29669c550d4a0449130230000200000000000000000080', + poolType: PoolType.MetaStable, + tokensList: [ + '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + ], +}; + +const balPool = { + id: '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014', + poolType: PoolType.Weighted, + tokensList: [ + '0xba100000625a3754423978a60c9317c58a424e3d', + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + ], +}; + +const composableStablePool = { + id: '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d', + poolType: PoolType.ComposableStable, + tokensList: [ + '0x2f4eb100552ef93840d5adc30560e5513dfffacb', + '0x82698aecc9e28e9bb27608bd52cf57f704bd1b83', + '0xa13a9247ea42d743238089903570127dda72fe44', + '0xae37d54ae477268b9997d4161b96b8200755935c', + ], +}; + +const pools = [stETHPool, balPool, composableStablePool]; + +let queryParams: ParamsBuilder; +const { balancerHelpers } = contracts; + +describe('join and exit queries', () => { + // for each poolType test outputs + pools.forEach((pool) => { + context(`${pool.poolType} pool`, () => { + before(async () => { + queryParams = new ParamsBuilder(pool); + }); + + it('should joinExactIn', async () => { + const maxAmountsIn = [ + bn(1), + ...Array(pool.tokensList.length - 1).fill(bn(0)), + ]; + + const params = queryParams.buildQueryJoinExactIn({ + maxAmountsIn, + }); + const join = await balancerHelpers.queryJoin(...params); + expect(Number(join.bptOut)).to.be.gt(0); + }); + + it('should joinExactOut', async () => { + const params = queryParams.buildQueryJoinExactOut({ + bptOut: bn(1), + tokenIn: pool.tokensList[0], + }); + const join = await balancerHelpers.queryJoin(...params); + expect(Number(join.amountsIn[0])).to.be.gt(0); + expect(Number(join.amountsIn[1])).to.eq(0); + }); + + it('should exitToSingleToken', async () => { + const params = queryParams.buildQueryExitToSingleToken({ + bptIn: bn(10), + tokenOut: pool.tokensList[0], + }); + const exit = await balancerHelpers.queryExit(...params); + expect(Number(exit.amountsOut[0])).to.be.gt(0); + expect(Number(exit.amountsOut[1])).to.eq(0); + }); + + it('should exitProportionally', async function () { + if (pool.poolType == PoolType.ComposableStable) { + this.skip(); + } + const params = queryParams.buildQueryExitProportionally({ + bptIn: bn(10), + }); + const exit = await balancerHelpers.queryExit(...params); + expect(Number(exit.amountsOut[0])).to.be.gt(0); + expect(Number(exit.amountsOut[1])).to.be.gt(0); + }); + + it('should exitExactOut', async () => { + const minAmountsOut = Array(pool.tokensList.length).fill(bn(1)); + + const params = queryParams.buildQueryExitExactOut({ + minAmountsOut, + }); + const exit = await balancerHelpers.queryExit(...params); + expect(Number(exit.amountsOut[0])).to.be.gt(0); + expect(Number(exit.amountsOut[1])).to.be.gt(0); + }); + }); + }); +}); diff --git a/balancer-js/src/modules/pools/queries/types.ts b/balancer-js/src/modules/pools/queries/types.ts new file mode 100644 index 000000000..a87abd9b1 --- /dev/null +++ b/balancer-js/src/modules/pools/queries/types.ts @@ -0,0 +1,109 @@ +import type { BigNumber } from '@ethersproject/bignumber'; +import type { PoolType } from '@/.'; + +export interface Encoder { + joinExactTokensInForBPTOut( + amountsIn: BigNumber[], + minimumBPT: BigNumber + ): string; + joinTokenInForExactBPTOut( + bptAmountOut: BigNumber, + enterTokenIndex: number + ): string; + exitExactBPTInForOneTokenOut( + bptAmountIn: BigNumber, + exitTokenIndex: number + ): string; + exitExactBPTInForTokensOut?(bptAmountIn: BigNumber): string; + exitBPTInForExactTokensOut( + amountsOut: BigNumber[], + maxBPTAmountIn: BigNumber + ): string; +} + +export interface ParamsBuilder { + buildQueryJoinExactIn(params: JoinExactInParams): queryJoinParams; + buildQueryJoinExactOut(params: JoinExactOutParams): queryJoinParams; + buildQueryExitToSingleToken(params: ExitToSingleTokenParams): queryExitParams; + buildQueryExitProportionally( + params: ExitProportionallyParams + ): queryExitParams; + buildQueryExitExactOut(params: ExitExactOutParams): queryExitParams; +} + +/** + * Pool model used to build queries + * + * @param tokensList is expected to be sorted in ascending order to match ordering used by the Vault. + */ +export interface Pool { + id: string; + poolType: PoolType; + tokensList: string[]; +} + +export interface JoinExactInParams { + sender?: string; + recipient?: string; + maxAmountsIn: BigNumber[]; + minimumBPT?: BigNumber; + fromInternalBalance?: boolean; +} + +export interface JoinExactOutParams { + sender?: string; + recipient?: string; + maxAmountsIn?: BigNumber[]; + bptOut: BigNumber; + tokenIn: string; + fromInternalBalance?: boolean; +} + +export interface ExitToSingleTokenParams { + sender?: string; + recipient?: string; + minAmountsOut?: BigNumber[]; + bptIn: BigNumber; + tokenOut: string; + toInternalBalance?: boolean; +} + +export interface ExitProportionallyParams { + sender?: string; + recipient?: string; + minAmountsOut?: BigNumber[]; + bptIn: BigNumber; + toInternalBalance?: boolean; +} + +export interface ExitExactOutParams { + sender?: string; + recipient?: string; + minAmountsOut: BigNumber[]; + maxBptIn?: BigNumber; + toInternalBalance?: boolean; +} + +export type queryJoinParams = [ + poolId: string, + sender: string, + recipient: string, + request: { + assets: string[]; + maxAmountsIn: BigNumber[]; + userData: string; + fromInternalBalance: boolean; + } +]; + +export type queryExitParams = [ + poolId: string, + sender: string, + recipient: string, + request: { + assets: string[]; + minAmountsOut: BigNumber[]; + userData: string; + toInternalBalance: boolean; + } +]; From ddc5901ab5b56720420be568609bd3a4cf2aa5a2 Mon Sep 17 00:00:00 2001 From: bronco Date: Thu, 17 Nov 2022 12:16:16 +0100 Subject: [PATCH 08/23] queries in pools module --- balancer-js/src/modules/pools/index.ts | 9 +++++ .../modules/pools/pools.integration.spec.ts | 34 +++++++++++++++++++ balancer-js/src/types.ts | 3 +- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 balancer-js/src/modules/pools/pools.integration.spec.ts diff --git a/balancer-js/src/modules/pools/index.ts b/balancer-js/src/modules/pools/index.ts index c2e6cf32a..a4353ebaa 100644 --- a/balancer-js/src/modules/pools/index.ts +++ b/balancer-js/src/modules/pools/index.ts @@ -17,6 +17,7 @@ import { Join } from '../joins/joins.module'; import { Exit } from '../exits/exits.module'; import { PoolVolume } from './volume/volume'; import { PoolFees } from './fees/fees'; +import * as Queries from './queries'; /** * Controller / use-case layer for interacting with pools data. @@ -194,10 +195,18 @@ export class Pools implements Findable { networkConfig: BalancerNetworkConfig ): PoolWithMethods { const methods = PoolTypeConcerns.from(pool.poolType); + const queries = new Queries.ParamsBuilder(pool); const wrappedNativeAsset = networkConfig.addresses.tokens.wrappedNativeAsset.toLowerCase(); return { ...pool, + buildQueryJoinExactIn: queries.buildQueryJoinExactIn.bind(queries), + buildQueryJoinExactOut: queries.buildQueryJoinExactOut.bind(queries), + buildQueryExitExactOut: queries.buildQueryExitExactOut.bind(queries), + buildQueryExitToSingleToken: + queries.buildQueryExitToSingleToken.bind(queries), + buildQueryExitProportionally: + queries.buildQueryExitProportionally.bind(queries), buildJoin: ( joiner: string, tokensIn: string[], diff --git a/balancer-js/src/modules/pools/pools.integration.spec.ts b/balancer-js/src/modules/pools/pools.integration.spec.ts new file mode 100644 index 000000000..25332fc83 --- /dev/null +++ b/balancer-js/src/modules/pools/pools.integration.spec.ts @@ -0,0 +1,34 @@ +import { expect } from 'chai'; +import { BalancerSDK, Network, PoolWithMethods } from '@/.'; +import { AddressZero, Zero } from '@ethersproject/constants'; +import { bn } from '@/lib/utils'; + +const rpcUrl = 'http://127.0.0.1:8545'; +const network = Network.MAINNET; +const sdk = new BalancerSDK({ network, rpcUrl }); +const { pools, contracts } = sdk; +const { balancerHelpers } = contracts; + +const ethStEth = + '0x32296969ef14eb0c6d29669c550d4a0449130230000200000000000000000080'; + +describe('pools module', () => { + describe('methods', () => { + let pool: PoolWithMethods | undefined; + before(async () => { + pool = await pools.find(ethStEth); + }); + + it('gets query function params', async () => { + if (!pool) { + return false; + } + const params = pool.buildQueryJoinExactIn({ + sender: AddressZero, + maxAmountsIn: [bn(1), Zero], + }); + const join = await balancerHelpers.queryJoin(...params); + expect(Number(join.bptOut)).to.be.gt(0); + }); + }); +}); diff --git a/balancer-js/src/types.ts b/balancer-js/src/types.ts index bc8c0d147..2dddff2c8 100644 --- a/balancer-js/src/types.ts +++ b/balancer-js/src/types.ts @@ -23,6 +23,7 @@ import type { } from './modules/data'; import type { GraphQLArgs } from './lib/graphql'; import type { AprBreakdown } from '@/modules/pools/apr/apr'; +import * as Queries from '@/modules/pools/queries/types'; export * from '@/modules/data/types'; export { Network, AprBreakdown }; @@ -309,7 +310,7 @@ export interface PriceRateProvider { /** * Pool use-cases / controller layer */ -export interface PoolWithMethods extends Pool { +export interface PoolWithMethods extends Pool, Queries.ParamsBuilder { buildJoin: ( joiner: string, tokensIn: string[], From 831b00ba68b7073879e106310868343317e621f3 Mon Sep 17 00:00:00 2001 From: bronco Date: Thu, 8 Dec 2022 19:45:30 +0100 Subject: [PATCH 09/23] query examples --- balancer-js/README.md | 51 ++++++++++++++++++++++ balancer-js/examples/pools/queries.ts | 62 +++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 balancer-js/examples/pools/queries.ts diff --git a/balancer-js/README.md b/balancer-js/README.md index 21bfc880a..736a53013 100644 --- a/balancer-js/README.md +++ b/balancer-js/README.md @@ -289,6 +289,57 @@ async getSpotPrice( [Example](./examples/spotPrice.ts) +## Simulating pool joins and exists + +The Balancer Vault provides a [method to simulate join or exit calls to a pool](https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/standalone-utils/contracts/BalancerQueries.sol#L91). +These function allows you to perform a dry run before sending an actual transaction, without checking the sender / recipient or token balances / approvals. Note that this function is not 'view' (due to implementation details): the client code must explicitly execute `eth_call` instead of `eth_sendTransaction`. + +### Simulating joins + +There are two ways to join a pool: + +1. `joinExactIn`: Joining the pool with known token amounts. This is the most commonly used method. +2. `joinExactOut`: Asking the pool for the expected liquidity when we know how much BPT we want back. + +In this documentation, we will focus on the first method (`joinExactIn`) for joining a pool with known token amounts. + +```js +const pool = await sdk.pools.find(poolId) +const maxAmountsIn = pool.tokenList.map((t) => forEachTokenSpecifyAmountYouWantToJoinWith) +const queryParams = pool.buildQueryJoinExactIn({ maxAmountsIn }) +const response = await balancerContracts.balancerHelpers.queryJoin(...queryParams) +const { bptOut, amountsIn } = response +``` + +`response` will return: + +* `bptOut`: The expected pool token amount returned by the pool. +* `amountsIn`: The same as maxAmountsIn + +### Simulating exits + +There are three ways to join a pool: + +1. `exitToSingleToken`: Exiting liquidity to a single underlying token is the simplest method. However, if the amount of liquidity being exited is a significant portion of the pool's total liquidity, it may result in price slippage. +2. `exitProportionally`: Exiting liquidity proportionally to all pool tokens. This is the most commonly used method. However `ComposableStable` pool type doesn't support it. +3. `exitExactOut`: Asking the pool for the expected pool token amount when we know how much token amounts we want back. + +In this example, we will focus on the first method (`exitProportionally`). + +```js +const pool = await sdk.pools.find(poolId) +const queryParams = pool.buildQueryJoinExactIn({ bptIn }) +const response = await balancerContracts.balancerHelpers.queryJoin(...queryParams) +const { bptIn, amountsOut } = response +``` + +`response` will return: + +* `amountsOut`: Token amounts returned by the pool. +* `bptIn`: The same as intput bptIn + +More examples: https://github.com/balancer-labs/balancer-sdk/blob/master/balancer-js/examples/pools/queries.ts + ## Joining Pools ### Joining with pool tokens diff --git a/balancer-js/examples/pools/queries.ts b/balancer-js/examples/pools/queries.ts new file mode 100644 index 000000000..e62fa8dc3 --- /dev/null +++ b/balancer-js/examples/pools/queries.ts @@ -0,0 +1,62 @@ +/** + * Shows how to query balancer helper contracts for + * expected amounts when providing or exiting liquidity from pools + * + * yarn examples:run ./examples/pools/queries.ts + */ + +import { parseEther, formatEther } from '@ethersproject/units' +import { BalancerSDK, PoolWithMethods } from '@/.' + +const sdk = new BalancerSDK({ + network: 1, + rpcUrl: 'https://eth-rpc.gateway.pokt.network', +}) + +const { pools, balancerContracts: contracts } = sdk; + +// Joining with a single token +const queryJoin = async (pool: PoolWithMethods) => { + const token = pool.tokensList[0] + const joinExactInQuery = pool.buildQueryJoinExactIn({ + maxAmountsIn: pool.tokensList.map((t) => parseEther((t === token) ? '1' : '0')) + }) + + const response = await contracts.balancerHelpers.queryJoin(...joinExactInQuery) + + console.log(`Joining ${pool.poolType}`) + console.table({ + tokens: pool.tokensList.map((t) => `${t.slice(0, 6)}...${t.slice(38, 42)}`), + amountsIn: response.amountsIn.map(formatEther), + bptOut: formatEther(response.bptOut), + }); +} + +// Exiting to single token +const queryExit = async (pool: PoolWithMethods) => { + const exitToSingleToken = pool.buildQueryExitToSingleToken({ + bptIn: parseEther('1'), + tokenOut: pool.tokensList[0] + }) + + const response = await contracts.balancerHelpers.queryExit(...exitToSingleToken) + + console.log(`Exiting ${pool.poolType}`) + console.table({ + tokens: pool.tokensList.map((t) => `${t.slice(0, 6)}...${t.slice(38, 42)}`), + amountsOut: response.amountsOut.map(formatEther), + bptIn: formatEther(response.bptIn), + }) +} + + +(async () => { + const composableStable = await pools.find('0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d') + const weighted = await pools.find('0x25accb7943fd73dda5e23ba6329085a3c24bfb6a000200000000000000000387') + const metaStable = await pools.find('0x32296969ef14eb0c6d29669c550d4a0449130230000200000000000000000080') + + for (const pool of [composableStable, weighted, metaStable]) { + await queryJoin(pool!) + await queryExit(pool!) + } +})() From fa0f1a280f75704c3c1af440355526844a5b6fde Mon Sep 17 00:00:00 2001 From: bronco Date: Mon, 12 Dec 2022 15:22:15 +0100 Subject: [PATCH 10/23] fix: examples TS --- balancer-js/examples/pools/calculateLiquidity.ts | 2 +- balancer-js/examples/priceImpact.ts | 6 ++++-- balancer-js/examples/swapSor.ts | 4 ++-- balancer-js/tsconfig.json | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/balancer-js/examples/pools/calculateLiquidity.ts b/balancer-js/examples/pools/calculateLiquidity.ts index 4e7068657..eb272815d 100644 --- a/balancer-js/examples/pools/calculateLiquidity.ts +++ b/balancer-js/examples/pools/calculateLiquidity.ts @@ -28,7 +28,7 @@ TOKENS.forEach((token) => { }); const pools = new Map(); -POOLS.forEach((pool) => pools.set(pool.id, pool as Pool)); +POOLS.forEach((pool) => pools.set(pool.id, {...pool, poolTypeVersion: 1, protocolYieldFeeCache: '0'} as Pool)); const poolProvider = findable(pools); const tokenPriceProvider = new StaticTokenPriceProvider(tokenPrices); diff --git a/balancer-js/examples/priceImpact.ts b/balancer-js/examples/priceImpact.ts index 7ae1d9cf8..111af101e 100644 --- a/balancer-js/examples/priceImpact.ts +++ b/balancer-js/examples/priceImpact.ts @@ -33,7 +33,8 @@ async function getPriceImpact() { const priceImpactWBTCWETH = await pool.calcPriceImpact( ['100000000000000000000', '100000000'], - '99430576622436571714692' + '99430576622436571714692', + true ); console.log(priceImpactWBTCWETH); @@ -43,7 +44,8 @@ async function getPriceImpact() { const priceImpactStaBal3 = await pool2.calcPriceImpact( ['100000000000000000000', '100000000', '190000000'], - '376972471880969684010' + '376972471880969684010', + true ); console.log(priceImpactStaBal3); } diff --git a/balancer-js/examples/swapSor.ts b/balancer-js/examples/swapSor.ts index 204a34531..4f3511f3f 100644 --- a/balancer-js/examples/swapSor.ts +++ b/balancer-js/examples/swapSor.ts @@ -61,7 +61,7 @@ async function getAndProcessSwaps( swapInfo, pools, wallet.address, - balancer.contracts.relayer!.address, + balancer.contracts.relayerV3!.address, balancer.networkConfig.addresses.tokens.wrappedNativeAsset, slippage, undefined @@ -71,7 +71,7 @@ async function getAndProcessSwaps( // console.log(wallet.address); // console.log(await balancer.sor.provider.getBlockNumber()); // console.log(relayerCallData.data); - const result = await balancer.contracts.relayer + const result = await balancer.contracts.relayerV3 ?.connect(wallet) .callStatic.multicall(relayerCallData.rawCalls); console.log(result); diff --git a/balancer-js/tsconfig.json b/balancer-js/tsconfig.json index 80eb45feb..875f176fa 100644 --- a/balancer-js/tsconfig.json +++ b/balancer-js/tsconfig.json @@ -15,7 +15,7 @@ "@/*": ["src/*"] } }, - "include": ["./src", "src/abi/*.json"], + "include": ["./src", "./examples", "src/abi/*.json"], "exclude": ["node_modules"], "files": ["hardhat.config.ts", "hardhat.config.goerli.ts"] } From cd02fdb56f01cdb66cfe59f46e20fd2f8d3edec6 Mon Sep 17 00:00:00 2001 From: bronco Date: Mon, 12 Dec 2022 19:29:24 +0100 Subject: [PATCH 11/23] cleanup: removing unused math helper --- balancer-js/src/lib/utils/math.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/balancer-js/src/lib/utils/math.ts b/balancer-js/src/lib/utils/math.ts index 2b35449ff..475119f2c 100644 --- a/balancer-js/src/lib/utils/math.ts +++ b/balancer-js/src/lib/utils/math.ts @@ -29,5 +29,7 @@ export function formatFromBigInt18(value: bigint): string { return _formatFixed(BigNumber.from(value), 18); } +/** + * Like parseEther but for numbers. Converts floating point to BigNumber using 18 decimals + */ export const bn = (value: number): BigNumber => _parseFixed(`${value}`, 18); -export const fp = (value: number): string => bn(value).toString(); From c72a1c9e84cca02f045ef4cdffdc0b4691776b2a Mon Sep 17 00:00:00 2001 From: bronco Date: Mon, 12 Dec 2022 20:42:15 +0100 Subject: [PATCH 12/23] Update beta-release.yaml --- .github/workflows/beta-release.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/beta-release.yaml b/.github/workflows/beta-release.yaml index 651ba9294..a7dc22001 100644 --- a/.github/workflows/beta-release.yaml +++ b/.github/workflows/beta-release.yaml @@ -38,15 +38,15 @@ jobs: git config --global user.email "johngrantuk+bot@users.noreply.github.com" git config user.signingkey AFFC8986D78B522F2999BAE053C748C476381000 git config gpg.program /usr/bin/gpg - git checkout develop yarn version --prerelease --preid beta --no-git-tag-version export NEW_VERSION=$(jq -r '.version' package.json) git commit -S -am "chore: version bump v$NEW_VERSION" git tag "v$NEW_VERSION" + git push yarn build yarn publish --non-interactive --tag beta - git push env: CI: true - NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file + NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }} From cdf3b2f69d1c5118300cbfa12eb5574751be4730 Mon Sep 17 00:00:00 2001 From: bronco Date: Tue, 13 Dec 2022 09:29:27 +0100 Subject: [PATCH 13/23] fix: debouncer should reject a promise on errors --- balancer-js/src/lib/utils/debouncer.spec.ts | 10 ++++++++++ balancer-js/src/lib/utils/debouncer.ts | 1 + 2 files changed, 11 insertions(+) diff --git a/balancer-js/src/lib/utils/debouncer.spec.ts b/balancer-js/src/lib/utils/debouncer.spec.ts index 4b8e3badc..41d1fbc03 100644 --- a/balancer-js/src/lib/utils/debouncer.spec.ts +++ b/balancer-js/src/lib/utils/debouncer.spec.ts @@ -83,4 +83,14 @@ describe('Debouncer', () => { expect(await p1).to.eql(['first']); expect(await p2).to.eql(['second']); }); + + it('rejects the promise when debounced function fails', async () => { + const asyncFunc = async (asyncAttrs: string[]) => + Promise.reject(asyncAttrs[0]); + const subject = new Debouncer(asyncFunc, 0); + + return subject.fetch('anything').catch((error) => { + expect(error).to.eql('anything'); + }); + }); }); diff --git a/balancer-js/src/lib/utils/debouncer.ts b/balancer-js/src/lib/utils/debouncer.ts index 5fb49bfb9..d9f2cc1bc 100644 --- a/balancer-js/src/lib/utils/debouncer.ts +++ b/balancer-js/src/lib/utils/debouncer.ts @@ -64,6 +64,7 @@ export class Debouncer { this.debounceCancel = () => {}; }) .catch((reason) => { + reject(reason); console.error(reason); }); }, this.wait); From 1d803fbede7f6c99cc028c2df8f023ae0e3ba646 Mon Sep 17 00:00:00 2001 From: bronco Date: Tue, 13 Dec 2022 12:31:07 +0100 Subject: [PATCH 14/23] fix: cyclic dependency in graph model --- balancer-js/src/modules/exits/exits.module.ts | 4 +-- balancer-js/src/modules/graph/graph.ts | 27 +++++++++---------- balancer-js/src/modules/joins/joins.module.ts | 3 +-- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/balancer-js/src/modules/exits/exits.module.ts b/balancer-js/src/modules/exits/exits.module.ts index 31eb137d4..68dd80bef 100644 --- a/balancer-js/src/modules/exits/exits.module.ts +++ b/balancer-js/src/modules/exits/exits.module.ts @@ -37,7 +37,7 @@ export class Exit { constructor( private pools: Findable, - private networkConfig: BalancerNetworkConfig + networkConfig: BalancerNetworkConfig ) { const { tokens, contracts } = networkAddresses(networkConfig.chainId); this.wrappedNativeAsset = tokens.wrappedNativeAsset; @@ -76,7 +76,6 @@ export class Exit { // Create nodes and order by breadth first const orderedNodes = await PoolGraph.getGraphNodes( false, - this.networkConfig.chainId, poolId, this.pools, false @@ -156,7 +155,6 @@ export class Exit { // Create nodes for each pool/token interaction and order by breadth first const orderedNodesForJoin = await PoolGraph.getGraphNodes( true, - this.networkConfig.chainId, poolId, this.pools, false diff --git a/balancer-js/src/modules/graph/graph.ts b/balancer-js/src/modules/graph/graph.ts index c2bc03cbc..69940c50b 100644 --- a/balancer-js/src/modules/graph/graph.ts +++ b/balancer-js/src/modules/graph/graph.ts @@ -1,11 +1,10 @@ import { BalancerError, BalancerErrorCode } from '@/balancerErrors'; import { isSameAddress, parsePoolInfo } from '@/lib/utils'; -import { BalancerSdkConfig, Pool, PoolAttribute, PoolType } from '@/types'; +import { Pool, PoolAttribute, PoolType } from '@/types'; import { Zero, WeiPerEther } from '@ethersproject/constants'; import { BigNumber, parseFixed } from '@ethersproject/bignumber'; import { Findable } from '../data/types'; -import { Pools } from '../pools'; -import { getNetworkConfig } from '../sdk.helpers'; +import { PoolTypeConcerns } from '../pools/pool-type-concerns'; type SpotPrices = { [tokenIn: string]: string }; export interface Node { @@ -63,10 +62,7 @@ exitActions.set(PoolType.Weighted, 'exitPool'); exitActions.set(PoolType.ComposableStable, 'exitPool'); export class PoolGraph { - constructor( - private pools: Findable, - private sdkConfig: BalancerSdkConfig - ) {} + constructor(private pools: Findable) {} async buildGraphFromRootPool( poolId: string, @@ -139,8 +135,8 @@ export class PoolGraph { throw new BalancerError(BalancerErrorCode.UNSUPPORTED_POOL_TYPE); const tokenTotal = this.getTokenTotal(pool); - const network = getNetworkConfig(this.sdkConfig); - const controller = Pools.wrap(pool, network); + // Spot price service + const { spotPriceCalculator } = PoolTypeConcerns.from(pool.poolType); const spotPrices: SpotPrices = {}; let decimals = 18; // Spot price of a path is product of the sp of each pool in path. We calculate the sp for each pool token here to use as required later. @@ -150,7 +146,12 @@ export class PoolGraph { decimals = token.decimals ? token.decimals : 18; return; } - const sp = controller.calcSpotPrice(token.address, pool.address, true); + const sp = spotPriceCalculator.calcPoolSpotPrice( + token.address, + pool.address, + pool, + true + ); spotPrices[token.address] = sp; }); @@ -356,17 +357,13 @@ export class PoolGraph { // Get full graph from root pool and return ordered nodes static getGraphNodes = async ( isJoin: boolean, - chainId: number, poolId: string, pools: Findable, wrapMainTokens: boolean ): Promise => { const rootPool = await pools.find(poolId); if (!rootPool) throw new BalancerError(BalancerErrorCode.POOL_DOESNT_EXIST); - const poolsGraph = new PoolGraph(pools, { - network: chainId, - rpcUrl: '', - }); + const poolsGraph = new PoolGraph(pools); const rootNode = await poolsGraph.buildGraphFromRootPool( poolId, diff --git a/balancer-js/src/modules/joins/joins.module.ts b/balancer-js/src/modules/joins/joins.module.ts index da74ed427..7feba4fa0 100644 --- a/balancer-js/src/modules/joins/joins.module.ts +++ b/balancer-js/src/modules/joins/joins.module.ts @@ -44,7 +44,7 @@ export class Join { private tenderlyHelper: TenderlyHelper; constructor( private pools: Findable, - private networkConfig: BalancerNetworkConfig + networkConfig: BalancerNetworkConfig ) { const { tokens, contracts } = networkAddresses(networkConfig.chainId); this.relayer = contracts.relayerV4 as string; @@ -77,7 +77,6 @@ export class Join { // Create nodes for each pool/token interaction and order by breadth first const orderedNodes = await PoolGraph.getGraphNodes( true, - this.networkConfig.chainId, poolId, this.pools, wrapMainTokens From c316578dda96dc2a10bf308622c8bfe74f1af2e0 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Tue, 13 Dec 2022 13:39:31 +0000 Subject: [PATCH 15/23] chore: version bump v0.1.41-beta.0 --- balancer-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/package.json b/balancer-js/package.json index 86e9737bf..9fa379972 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -1,6 +1,6 @@ { "name": "@balancer-labs/sdk", - "version": "0.1.40", + "version": "0.1.41-beta.0", "description": "JavaScript SDK for interacting with the Balancer Protocol V2", "license": "GPL-3.0-only", "homepage": "https://github.com/balancer-labs/balancer-sdk#readme", From 9d61386eea7d046b24b8ebee48c26a98b8b92a9d Mon Sep 17 00:00:00 2001 From: bronco Date: Tue, 13 Dec 2022 15:27:04 +0100 Subject: [PATCH 16/23] fix: beta release workflow --- .github/workflows/beta-release.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/beta-release.yaml b/.github/workflows/beta-release.yaml index a7dc22001..1cab0e285 100644 --- a/.github/workflows/beta-release.yaml +++ b/.github/workflows/beta-release.yaml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v3 with: ref: develop - token: ${{ secrets.RELEASE_PAT }} + persist-credentials: false - uses: actions/setup-node@v3 with: node-version: 18 @@ -32,6 +32,8 @@ jobs: if: steps.cache.outputs.cache-hit != 'true' - env: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }} run: | echo -n "$SIGNING_KEY" | base64 --decode | gpg --import git config --global user.name "johngrantuk" @@ -48,5 +50,3 @@ jobs: env: CI: true - NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - GITHUB_TOKEN: ${{ secrets.RELEASE_PAT }} From 4eb471a43197247ea18446ee8395d73067c2b0bc Mon Sep 17 00:00:00 2001 From: bronco Date: Tue, 13 Dec 2022 17:56:45 +0100 Subject: [PATCH 17/23] new: fees example --- balancer-js/examples/pools/fees.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 balancer-js/examples/pools/fees.ts diff --git a/balancer-js/examples/pools/fees.ts b/balancer-js/examples/pools/fees.ts new file mode 100644 index 000000000..5137d9c6d --- /dev/null +++ b/balancer-js/examples/pools/fees.ts @@ -0,0 +1,20 @@ +import { BalancerSDK } from '@/.'; + +const sdk = new BalancerSDK({ + network: 1, + rpcUrl: 'https://eth-rpc.gateway.pokt.network', +}); + +(() => { + [ + '0xa5533a44d06800eaf2daad5aad3f9aa9e1dc36140002000000000000000001b8', + ].forEach(async (poolId) => { + const pool = await sdk.pools.find(poolId); + if (pool) { + const fees = await sdk.pools.fees(pool); + console.log(fees); + } + }) +})(); + +// yarn examples:run ./examples/pools/fees.ts From 6d4a3b880999399d7021a1e6ac516ca5c5836959 Mon Sep 17 00:00:00 2001 From: bronco Date: Wed, 14 Dec 2022 14:52:43 +0100 Subject: [PATCH 18/23] fix: beta release tweaks --- .github/workflows/beta-release.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/beta-release.yaml b/.github/workflows/beta-release.yaml index 1cab0e285..abee14acb 100644 --- a/.github/workflows/beta-release.yaml +++ b/.github/workflows/beta-release.yaml @@ -44,6 +44,7 @@ jobs: export NEW_VERSION=$(jq -r '.version' package.json) git commit -S -am "chore: version bump v$NEW_VERSION" git tag "v$NEW_VERSION" + git remote set-url origin "https://johngrantuk:$GITHUB_TOKEN@github.com/balancer-labs/balancer-sdk" git push yarn build yarn publish --non-interactive --tag beta From 77c9cd8a4b65c10d8462c58c0749cec220cb69bb Mon Sep 17 00:00:00 2001 From: bronco Date: Wed, 14 Dec 2022 15:16:40 +0100 Subject: [PATCH 19/23] improvement: handle missing concerns --- balancer-js/src/modules/pools/index.ts | 234 ++++++++++++------ .../modules/pools/pools.integration.spec.ts | 28 ++- 2 files changed, 180 insertions(+), 82 deletions(-) diff --git a/balancer-js/src/modules/pools/index.ts b/balancer-js/src/modules/pools/index.ts index a4353ebaa..93e100561 100644 --- a/balancer-js/src/modules/pools/index.ts +++ b/balancer-js/src/modules/pools/index.ts @@ -18,6 +18,11 @@ import { Exit } from '../exits/exits.module'; import { PoolVolume } from './volume/volume'; import { PoolFees } from './fees/fees'; import * as Queries from './queries'; +import { BalancerError } from '@/balancerErrors'; + +const notImplemented = (poolType: string, name: string) => () => { + throw `${name} for poolType ${poolType} not implemented`; +}; /** * Controller / use-case layer for interacting with pools data. @@ -186,94 +191,155 @@ export class Pools implements Findable { return this.volumeService.last24h(pool); } - controller(pool: Pool): PoolWithMethods { - return Pools.wrap(pool, this.networkConfig); - } - static wrap( pool: Pool, networkConfig: BalancerNetworkConfig ): PoolWithMethods { - const methods = PoolTypeConcerns.from(pool.poolType); - const queries = new Queries.ParamsBuilder(pool); - const wrappedNativeAsset = - networkConfig.addresses.tokens.wrappedNativeAsset.toLowerCase(); - return { - ...pool, - buildQueryJoinExactIn: queries.buildQueryJoinExactIn.bind(queries), - buildQueryJoinExactOut: queries.buildQueryJoinExactOut.bind(queries), - buildQueryExitExactOut: queries.buildQueryExitExactOut.bind(queries), - buildQueryExitToSingleToken: - queries.buildQueryExitToSingleToken.bind(queries), - buildQueryExitProportionally: - queries.buildQueryExitProportionally.bind(queries), - buildJoin: ( - joiner: string, - tokensIn: string[], - amountsIn: string[], - slippage: string - ): JoinPoolAttributes => { - return methods.join.buildJoin({ - joiner, - pool, - tokensIn, - amountsIn, - slippage, - wrappedNativeAsset, - }); - }, - calcPriceImpact: async ( - amountsIn: string[], - minBPTOut: string, - isJoin: boolean - ) => - methods.priceImpactCalculator.calcPriceImpact( - pool, - amountsIn, - minBPTOut, - isJoin - ), - buildExitExactBPTIn: ( - exiter, - bptIn, - slippage, - shouldUnwrapNativeAsset = false, - singleTokenMaxOut - ) => { - if (methods.exit.buildExitExactBPTIn) { - return methods.exit.buildExitExactBPTIn({ - exiter, + let concerns: ReturnType; + let queries: Queries.ParamsBuilder; + let methods; + try { + concerns = PoolTypeConcerns.from(pool.poolType); + methods = { + buildJoin: ( + joiner: string, + tokensIn: string[], + amountsIn: string[], + slippage: string + ): JoinPoolAttributes => { + return concerns.join.buildJoin({ + joiner, pool, - bptIn, + tokensIn, + amountsIn, slippage, - shouldUnwrapNativeAsset, wrappedNativeAsset, - singleTokenMaxOut, }); - } else { - throw 'ExitExactBPTIn not supported'; - } - }, - buildExitExactTokensOut: (exiter, tokensOut, amountsOut, slippage) => - methods.exit.buildExitExactTokensOut({ - exiter, - pool, - tokensOut, - amountsOut, - slippage, - wrappedNativeAsset, - }), - // TODO: spotPrice fails, because it needs a subgraphType, - // either we refetch or it needs a type transformation from SDK internal to SOR (subgraph) - // spotPrice: async (tokenIn: string, tokenOut: string) => - // methods.spotPriceCalculator.calcPoolSpotPrice(tokenIn, tokenOut, data), - calcSpotPrice: (tokenIn: string, tokenOut: string, isDefault?: boolean) => - methods.spotPriceCalculator.calcPoolSpotPrice( - tokenIn, - tokenOut, - pool, - isDefault + }, + calcPriceImpact: async ( + amountsIn: string[], + minBPTOut: string, + isJoin: boolean + ) => + concerns.priceImpactCalculator.calcPriceImpact( + pool, + amountsIn, + minBPTOut, + isJoin + ), + buildExitExactBPTIn: ( + exiter: string, + bptIn: string, + slippage: string, + shouldUnwrapNativeAsset = false, + singleTokenMaxOut?: string + ) => { + if (concerns.exit.buildExitExactBPTIn) { + return concerns.exit.buildExitExactBPTIn({ + exiter, + pool, + bptIn, + slippage, + shouldUnwrapNativeAsset, + wrappedNativeAsset, + singleTokenMaxOut, + }); + } else { + throw 'ExitExactBPTIn not supported'; + } + }, + buildExitExactTokensOut: ( + exiter: string, + tokensOut: string[], + amountsOut: string[], + slippage: string + ) => + concerns.exit.buildExitExactTokensOut({ + exiter, + pool, + tokensOut, + amountsOut, + slippage, + wrappedNativeAsset, + }), + // TODO: spotPrice fails, because it needs a subgraphType, + // either we refetch or it needs a type transformation from SDK internal to SOR (subgraph) + // spotPrice: async (tokenIn: string, tokenOut: string) => + // methods.spotPriceCalculator.calcPoolSpotPrice(tokenIn, tokenOut, data), + calcSpotPrice: ( + tokenIn: string, + tokenOut: string, + isDefault?: boolean + ) => + concerns.spotPriceCalculator.calcPoolSpotPrice( + tokenIn, + tokenOut, + pool, + isDefault + ), + }; + } catch (error) { + if ((error as BalancerError).code != 'UNSUPPORTED_POOL_TYPE') { + console.error(error); + } + + methods = { + buildJoin: notImplemented(pool.poolType, 'buildJoin'), + calcPriceImpact: notImplemented(pool.poolType, 'calcPriceImpact'), + buildExitExactBPTIn: notImplemented( + pool.poolType, + 'buildExitExactBPTIn' ), + buildExitExactTokensOut: notImplemented( + pool.poolType, + 'buildExitExactTokensOut' + ), + calcSpotPrice: notImplemented(pool.poolType, 'calcSpotPrice'), + }; + } + + try { + queries = new Queries.ParamsBuilder(pool); + methods = { + ...methods, + buildQueryJoinExactIn: queries.buildQueryJoinExactIn.bind(queries), + buildQueryJoinExactOut: queries.buildQueryJoinExactOut.bind(queries), + buildQueryExitExactOut: queries.buildQueryExitExactOut.bind(queries), + buildQueryExitToSingleToken: + queries.buildQueryExitToSingleToken.bind(queries), + buildQueryExitProportionally: + queries.buildQueryExitProportionally.bind(queries), + }; + } catch (error) { + methods = { + ...methods, + buildQueryJoinExactIn: notImplemented( + pool.poolType, + 'buildQueryJoinExactIn' + ), + buildQueryJoinExactOut: notImplemented( + pool.poolType, + 'buildQueryJoinExactOut' + ), + buildQueryExitExactOut: notImplemented( + pool.poolType, + 'buildQueryExitExactOut' + ), + buildQueryExitToSingleToken: notImplemented( + pool.poolType, + 'buildQueryExitToSingleToken' + ), + buildQueryExitProportionally: notImplemented( + pool.poolType, + 'buildQueryExitProportionally' + ), + }; + } + const wrappedNativeAsset = + networkConfig.addresses.tokens.wrappedNativeAsset.toLowerCase(); + return { + ...pool, + ...methods, }; } @@ -304,13 +370,19 @@ export class Pools implements Findable { const list = await this.dataSource().all(); if (!list) return []; - return list.map((data: Pool) => Pools.wrap(data, this.networkConfig)); + return list + .map((data: Pool) => Pools.wrap(data, this.networkConfig)) + .filter((p) => p) as PoolWithMethods[]; } async where(filter: (pool: Pool) => boolean): Promise { const list = await this.dataSource().where(filter); if (!list) return []; - return list.map((data: Pool) => Pools.wrap(data, this.networkConfig)); + const wrapped = list.map((data: Pool) => + Pools.wrap(data, this.networkConfig) + ); + + return wrapped.filter((p) => p) as PoolWithMethods[]; } } diff --git a/balancer-js/src/modules/pools/pools.integration.spec.ts b/balancer-js/src/modules/pools/pools.integration.spec.ts index 25332fc83..602816b61 100644 --- a/balancer-js/src/modules/pools/pools.integration.spec.ts +++ b/balancer-js/src/modules/pools/pools.integration.spec.ts @@ -1,7 +1,9 @@ import { expect } from 'chai'; -import { BalancerSDK, Network, PoolWithMethods } from '@/.'; +import { BalancerSDK, Network, Pool, PoolWithMethods, Pools } from '@/.'; import { AddressZero, Zero } from '@ethersproject/constants'; import { bn } from '@/lib/utils'; +import { poolFactory } from '@/test/factories/sdk'; +import { BALANCER_NETWORK_CONFIG } from '@/lib/constants/config'; const rpcUrl = 'http://127.0.0.1:8545'; const network = Network.MAINNET; @@ -23,12 +25,36 @@ describe('pools module', () => { if (!pool) { return false; } + const params = pool.buildQueryJoinExactIn({ sender: AddressZero, maxAmountsIn: [bn(1), Zero], }); + const join = await balancerHelpers.queryJoin(...params); expect(Number(join.bptOut)).to.be.gt(0); }); }); + + describe('wrapping', () => { + it('should handle not implemented pool types', () => { + const pool = { + ...poolFactory.build(), + poolType: 'unknown', + }; + + const poolWithServices = Pools.wrap( + pool as Pool, + BALANCER_NETWORK_CONFIG[1] + ); + expect(poolWithServices.id).to.eq(pool.id); + let error; + try { + poolWithServices.calcPriceImpact([], '', false); + } catch (err) { + error = err; + } + expect(error).to.include('not implemented'); + }); + }); }); From 34b646866f03945b84e795479cf04ce07eba4852 Mon Sep 17 00:00:00 2001 From: bronco Date: Wed, 14 Dec 2022 15:19:27 +0100 Subject: [PATCH 20/23] improvement: examples --- balancer-js/examples/data/emissions.ts | 26 ++++++++++++++++++++++ balancer-js/examples/pools/aprs.polygon.ts | 4 ++-- balancer-js/examples/pools/aprs.ts | 11 ++------- 3 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 balancer-js/examples/data/emissions.ts diff --git a/balancer-js/examples/data/emissions.ts b/balancer-js/examples/data/emissions.ts new file mode 100644 index 000000000..d16475207 --- /dev/null +++ b/balancer-js/examples/data/emissions.ts @@ -0,0 +1,26 @@ +/** + * yarn examples:run ./examples/data/emissions.ts + */ +import { BalancerSDK } from '@/.'; +import { balEmissions } from '@/modules/data'; + +const sdk = new BalancerSDK({ + network: 1, + rpcUrl: 'https://eth-rpc.gateway.pokt.network' +}) + +const { data } = sdk; + +const now = Math.round(new Date().getTime() / 1000) +const totalBalEmissions = balEmissions.between(now, now + 365 * 86400) + +const main = async () => { + if (data.liquidityGauges) { + const gauge = await data.liquidityGauges.findBy('poolId', '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d'); + if (gauge) { + console.log(`yearly emissions share: ${totalBalEmissions * gauge.relativeWeight} BAL`); + } + } +} + +main() diff --git a/balancer-js/examples/pools/aprs.polygon.ts b/balancer-js/examples/pools/aprs.polygon.ts index d91943f3e..d51e7dccb 100644 --- a/balancer-js/examples/pools/aprs.polygon.ts +++ b/balancer-js/examples/pools/aprs.polygon.ts @@ -1,5 +1,5 @@ /** - * Display APRs for pool ids hardcoded under `const ids` + * Display APRs * Run command: yarn examples:run ./examples/pools/aprs.polygon.ts */ import dotenv from 'dotenv'; @@ -19,7 +19,7 @@ const { pools } = sdk; const main = async () => { const pool = await pools.find( - '0x8159462d255c1d24915cb51ec361f700174cd99400000000000000000000075d' + '0x726e324c29a1e49309672b244bdc4ff62a270407000200000000000000000702' ); if (pool) { diff --git a/balancer-js/examples/pools/aprs.ts b/balancer-js/examples/pools/aprs.ts index b86b62cf5..681f44187 100644 --- a/balancer-js/examples/pools/aprs.ts +++ b/balancer-js/examples/pools/aprs.ts @@ -3,7 +3,7 @@ * Run command: yarn examples:run ./examples/pools/aprs.ts */ import dotenv from 'dotenv'; -import { BalancerSDK } from '../../src/modules/sdk.module'; +import { BalancerSDK } from '@/.'; dotenv.config(); @@ -15,14 +15,7 @@ const sdk = new BalancerSDK({ const { pools } = sdk; const main = async () => { - const list = ( - await pools.where( - (pool) => - pool.poolType != 'Element' && - pool.poolType != 'AaveLinear' && - pool.poolType != 'LiquidityBootstrapping' - ) - ) + const list = (await pools.all()) // .filter((p) => p.id === '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d') .sort((a, b) => parseFloat(b.totalLiquidity) - parseFloat(a.totalLiquidity)) .slice(0, 30); From 7d63a9271a83ad381ba269d0012d1f0f825969cc Mon Sep 17 00:00:00 2001 From: bronco Date: Wed, 14 Dec 2022 16:49:29 +0100 Subject: [PATCH 21/23] new: maticx yield --- balancer-js/examples/data/token-yields.ts | 5 +-- .../modules/data/token-yields/repository.ts | 2 ++ .../data/token-yields/tokens/maticx.spec.ts | 28 +++++++++++++++ .../data/token-yields/tokens/maticx.ts | 34 +++++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 balancer-js/src/modules/data/token-yields/tokens/maticx.spec.ts create mode 100644 balancer-js/src/modules/data/token-yields/tokens/maticx.ts diff --git a/balancer-js/examples/data/token-yields.ts b/balancer-js/examples/data/token-yields.ts index 5ac33cec1..3d785e9ad 100644 --- a/balancer-js/examples/data/token-yields.ts +++ b/balancer-js/examples/data/token-yields.ts @@ -1,5 +1,5 @@ /** - * Display APRs for pool ids hardcoded under `const ids` + * Display token yields * Run command: yarn examples:run ./examples/data/token-yields.ts */ import { BalancerSDK } from '../../src/modules/sdk.module'; @@ -10,9 +10,10 @@ const { data } = sdk; const tokens = [ yieldTokens[1].waDAI, - '0x3a58a54c066fdc0f2d55fc9c89f0415c92ebf3c4', // stMatic '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', // stETH '0xac3e018457b222d93114458476f3e3416abbe38f', // sfrxETH + '0x3a58a54c066fdc0f2d55fc9c89f0415c92ebf3c4', // stMatic (polygon) + '0xfa68fb4628dff1028cfec22b4162fccd0d45efb6', // maticX (polygon) ] const main = async () => { diff --git a/balancer-js/src/modules/data/token-yields/repository.ts b/balancer-js/src/modules/data/token-yields/repository.ts index 0e3677fbf..198792141 100644 --- a/balancer-js/src/modules/data/token-yields/repository.ts +++ b/balancer-js/src/modules/data/token-yields/repository.ts @@ -10,6 +10,7 @@ import { import { aave, allYieldTokens as aaveTokens } from './tokens/aave'; import { overnight, yieldTokens as overnightTokens } from './tokens/overnight'; import { sfrxETH, yieldTokens as fraxTokens } from './tokens/sfrxeth'; +import { maticX, yieldTokens as staderLabsTokens } from './tokens/maticx'; import { Network, Findable } from '@/types'; /** @@ -28,6 +29,7 @@ const yieldSourceMap: { [address: string]: AprFetcher } = Object.fromEntries([ ...Object.values(overnightTokens).map((k) => [k, overnight]), ...Object.values(rocketpoolTokens).map((k) => [k, rocketpool]), ...Object.values(fraxTokens).map((k) => [k, sfrxETH]), + ...Object.values(staderLabsTokens).map((k) => [k, maticX]), ]); export class TokenYieldsRepository implements Findable { diff --git a/balancer-js/src/modules/data/token-yields/tokens/maticx.spec.ts b/balancer-js/src/modules/data/token-yields/tokens/maticx.spec.ts new file mode 100644 index 000000000..2206331e1 --- /dev/null +++ b/balancer-js/src/modules/data/token-yields/tokens/maticx.spec.ts @@ -0,0 +1,28 @@ +import { expect } from 'chai'; +import { maticX, yieldTokens } from './maticx'; +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; + +const mockedResponse = { + value: '1', +}; + +describe('rocketpool apr', () => { + let mock: MockAdapter; + + before(() => { + mock = new MockAdapter(axios); + mock + .onGet('https://generic-apr-proxy.balancer.workers.dev/?provider=stader') + .reply(() => [200, mockedResponse]); + }); + + after(() => { + mock.restore(); + }); + + it('is getting fetched', async () => { + const apr = (await maticX())[yieldTokens.maticX]; + expect(apr).to.eq(100); + }); +}); diff --git a/balancer-js/src/modules/data/token-yields/tokens/maticx.ts b/balancer-js/src/modules/data/token-yields/tokens/maticx.ts new file mode 100644 index 000000000..46337cb4f --- /dev/null +++ b/balancer-js/src/modules/data/token-yields/tokens/maticx.ts @@ -0,0 +1,34 @@ +import { AprFetcher } from '../repository'; +import axios from 'axios'; + +export const yieldTokens = { + maticX: '0xfa68fb4628dff1028cfec22b4162fccd0d45efb6', +}; + +interface StaderLabsAPIResponse { + value: string; +} + +/** + * APR fetching + * + * @returns APR in bsp + */ +export const maticX: AprFetcher = async () => { + let apr = 0; + + try { + const response = await axios.get( + 'https://generic-apr-proxy.balancer.workers.dev/?provider=stader' + ); + const { value } = response.data; + + apr = Math.round(parseFloat(value) * 100); + } catch (error) { + console.error('Failed to fetch APR:', error); + } + + return { + [yieldTokens.maticX]: apr, + }; +}; From 8abc68a13ee8e6719b021012f5756ebf392f6087 Mon Sep 17 00:00:00 2001 From: bronco Date: Wed, 14 Dec 2022 17:09:57 +0100 Subject: [PATCH 22/23] docs: shares example command --- balancer-js/examples/data/gauge-shares.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/examples/data/gauge-shares.ts b/balancer-js/examples/data/gauge-shares.ts index 501abf18d..0a6340b39 100644 --- a/balancer-js/examples/data/gauge-shares.ts +++ b/balancer-js/examples/data/gauge-shares.ts @@ -37,4 +37,4 @@ const { gaugeShares } = sdk.data; })(); - // npm run examples:run -- ./examples/data/gauge-shares.ts + // yarn examples:run ./examples/data/gauge-shares.ts From 3d7d15ba85c9582cdcf41f9b6a619b09afe8ec84 Mon Sep 17 00:00:00 2001 From: johngrantuk Date: Thu, 15 Dec 2022 10:33:46 +0000 Subject: [PATCH 23/23] Update version to 0.1.41. --- balancer-js/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/balancer-js/package.json b/balancer-js/package.json index 9fa379972..cb6f78612 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -1,6 +1,6 @@ { "name": "@balancer-labs/sdk", - "version": "0.1.41-beta.0", + "version": "0.1.41", "description": "JavaScript SDK for interacting with the Balancer Protocol V2", "license": "GPL-3.0-only", "homepage": "https://github.com/balancer-labs/balancer-sdk#readme",