Skip to content

Commit

Permalink
Merge pull request #312 from balancer-labs/develop
Browse files Browse the repository at this point in the history
Release 0.1.48
  • Loading branch information
Bruno Eidam Guerios authored Feb 7, 2023
2 parents 8bc2ea2 + 6ba7629 commit 5129524
Show file tree
Hide file tree
Showing 45 changed files with 2,196 additions and 131 deletions.
10 changes: 6 additions & 4 deletions balancer-js/examples/data/token-yields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@ const { data } = sdk;

const tokens = [
yieldTokens[1].waDAI,
'0x9559aaa82d9649c7a7b220e7c461d2e74c9a3593', // rETH (stafi)
'0x93ef1ea305d11a9b2a3ebb9bb4fcc34695292e7d', // qETH
'0xae7ab96520de3a18e5e111b5eaab095312d7fe84', // stETH
'0xac3e018457b222d93114458476f3e3416abbe38f', // sfrxETH
'0x3a58a54c066fdc0f2d55fc9c89f0415c92ebf3c4', // stMatic (polygon)
'0xfa68fb4628dff1028cfec22b4162fccd0d45efb6', // maticX (polygon)
]
'0xaf0d9d65fc54de245cda37af3d18cbec860a4d4b', // USDR
];

const main = async () => {
const yields = await Promise.all(
tokens.map((token) => data.tokenYields.find(token))
)
);

tokens.forEach((token, id) => {
console.log(token, yields[id])
})
console.log(token, yields[id]);
});
};

main();
100 changes: 100 additions & 0 deletions balancer-js/examples/pools/composable-stable/join.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Network } from "@/lib/constants";
import { BalancerSDK } from "@/modules/sdk.module";
import { Pools } from "@/modules/pools";
import { forkSetup, getBalances } from "@/test/lib/utils";
import { Pool } from "@/types";
import { parseFixed } from "@ethersproject/bignumber";
import { ethers } from "hardhat";
import dotenv from "dotenv";
import pools_16350000 from "@/test/lib/pools_16350000.json";


dotenv.config();

const { ALCHEMY_URL: jsonRpcUrl } = process.env;
const rpcUrl = 'http://127.0.0.1:8545';
const network = Network.MAINNET;
const blockNumber = 16350000;

const poolObj = pools_16350000.find(
({ id }) =>
id == '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d'
) as unknown as Pool;

const sdk = new BalancerSDK({ network, rpcUrl });

const { networkConfig } = sdk;

const initialBalance = '100000';

//REMOVING THE BPT FROM THE TOKENS IN
const tokensIn = [
...poolObj.tokens
.filter(({ address }) => address !== poolObj.address)
.map(({ address }) => address),
];

const bptIndex = poolObj.tokens.findIndex(({ address }) => address === poolObj.address);

const amountsIn = [
parseFixed('100', 18).toString(),
parseFixed('100', 18).toString(),
parseFixed('100', 18).toString(),
];
const slots = [0, 0, 0, 0];

const pool = Pools.wrap(poolObj, networkConfig);

async function composableStableJoin() {
const provider = new ethers.providers.JsonRpcProvider(rpcUrl, network);
const signer = provider.getSigner();
let signerAddress: string;
const balances = poolObj.tokens.map((token) =>
token ? parseFixed(initialBalance, token.decimals).toString() : '0'
);
await forkSetup(
signer,
poolObj.tokens.map((t) => t.address),
slots,
balances,
jsonRpcUrl as string,
blockNumber
// holds the same state as the static repository
);
signerAddress = await signer.getAddress();
const tokensBalanceBefore = await getBalances(
poolObj.tokens.map(({ address }) => address),
signer,
signerAddress
);
const bptBalanceBefore = tokensBalanceBefore.splice(bptIndex, 1);

const slippage = '1';

const { to, data, minBPTOut } = pool.buildJoin(
signerAddress,
tokensIn,
amountsIn,
slippage
);

await signer.sendTransaction(
{
to, data, gasLimit: 3000000
}
)

const tokensBalanceAfter = await getBalances(
poolObj.tokens.map(({ address }) => address),
signer,
signerAddress
);
const bptBalanceAfter = tokensBalanceAfter.splice(bptIndex, 1);
console.log("bptBalanceBefore: " + bptBalanceBefore);
console.log("bptBalanceAfter: " + bptBalanceAfter);
console.log("minBptBalanceExpected: " + (BigInt(bptBalanceBefore.toString()) + BigInt(minBPTOut)));
console.log("tokensBalanceBefore: " + tokensBalanceBefore.map(b => b.toString()));
console.log("tokensBalanceAfter: " + tokensBalanceAfter.map(b => b.toString()));
}

export default composableStableJoin();
13 changes: 8 additions & 5 deletions balancer-js/examples/swapSor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ async function getAndProcessSwaps(
useJoinExitPaths
);

console.log(balancer.swaps.sor.getPools().length);

if (swapInfo.returnAmount.isZero()) {
console.log('No Swap');
return;
Expand Down Expand Up @@ -119,12 +121,12 @@ async function getAndProcessSwaps(
}

async function swapExample() {
const network = Network.POLYGON;
const network = Network.GOERLI;
const rpcUrl = PROVIDER_URLS[network];
const tokenIn = ADDRESSES[network].USDC.address;
const tokenOut = ADDRESSES[network].brz.address;
const tokenIn = ADDRESSES[network].DAI.address;
const tokenOut = ADDRESSES[network].USDT.address;
const swapType = SwapTypes.SwapExactIn;
const amount = parseFixed('200', 6);
const amount = parseFixed('200', 18);
// Currently Relayer only suitable for ExactIn and non-eth swaps
const canUseJoinExitPaths = canUseJoinExit(swapType, tokenIn!, tokenOut!);

Expand All @@ -133,7 +135,8 @@ async function swapExample() {
rpcUrl,
});

await balancer.swaps.sor.fetchPools();
const result = await balancer.swaps.sor.fetchPools();
console.log(result);

await getAndProcessSwaps(
balancer,
Expand Down
2 changes: 1 addition & 1 deletion balancer-js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@balancer-labs/sdk",
"version": "0.1.47",
"version": "0.1.48",
"description": "JavaScript SDK for interacting with the Balancer Protocol V2",
"license": "GPL-3.0-only",
"homepage": "https://github.com/balancer-labs/balancer-sdk#readme",
Expand Down
11 changes: 11 additions & 0 deletions balancer-js/src/lib/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,14 @@ export * from './math';

export const isSameAddress = (address1: string, address2: string): boolean =>
getAddress(address1) === getAddress(address2);

export function insert<T>(arr: T[], index: number, newItem: T): T[] {
return [
// part of the array before the specified index
...arr.slice(0, index),
// inserted item
newItem,
// part of the array after the specified index
...arr.slice(index),
];
}
63 changes: 54 additions & 9 deletions balancer-js/src/lib/utils/poolHelper.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,85 @@
import { parseFixed } from '@ethersproject/bignumber';
import { Pool } from '../../types';
import { _computeScalingFactor } from '@/lib/utils/solidityMaths';
import { AssetHelpers } from './assetHelpers';

const AMP_PRECISION = 3; // number of decimals -> precision 1000

/**
* Parse pool info into EVM amounts
* Parse pool info into EVM amounts. Sorts by token order if wrappedNativeAsset param passed.
* @param {Pool} pool
* @param {string} wrappedNativeAsset
* @returns parsed pool info
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const parsePoolInfo = (pool: Pool) => {
const parsedTokens = pool.tokens.map((token) => token.address);
const parsedDecimals = pool.tokens.map((token) => {
export const parsePoolInfo = (pool: Pool, wrappedNativeAsset?: string) => {
let parsedTokens = pool.tokens.map((token) => token.address);
let parsedDecimals = pool.tokens.map((token) => {
return token.decimals ? token.decimals.toString() : undefined;
});
const scalingFactors = parsedDecimals.map((decimals) =>
let scalingFactors = parsedDecimals.map((decimals) =>
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
_computeScalingFactor(BigInt(decimals!))
);
const parsedBalances = pool.tokens.map((token) =>
let parsedBalances = pool.tokens.map((token) =>
parseFixed(token.balance, token.decimals).toString()
);
// This assumes token.balance is in human scale (e.g. from SG)
const upScaledBalances = pool.tokens.map((token) =>
let upScaledBalances = pool.tokens.map((token) =>
parseFixed(token.balance, 18).toString()
);
const parsedWeights = pool.tokens.map((token) => {
let parsedWeights = pool.tokens.map((token) => {
return token.weight ? parseFixed(token.weight, 18).toString() : undefined;
});
const parsedPriceRates = pool.tokens.map((token) => {
let parsedPriceRates = pool.tokens.map((token) => {
return token.priceRate
? parseFixed(token.priceRate, 18).toString()
: undefined;
});

if (wrappedNativeAsset) {
const assetHelpers = new AssetHelpers(wrappedNativeAsset);
let sfString;
[
parsedTokens,
parsedDecimals,
sfString,
parsedBalances,
upScaledBalances,
parsedWeights,
parsedPriceRates,
] = assetHelpers.sortTokens(
parsedTokens,
parsedDecimals,
scalingFactors,
parsedBalances,
upScaledBalances,
parsedWeights,
parsedPriceRates
) as [string[], string[], string[], string[], string[], string[], string[]];
scalingFactors = sfString.map(BigInt);
}

const parsedAmp = pool.amp
? parseFixed(pool.amp, AMP_PRECISION).toString() // Solidity maths uses precison method for amp that must be replicated
: undefined;
const parsedTotalShares = parseFixed(pool.totalShares, 18).toString();
const parsedSwapFee = parseFixed(pool.swapFee, 18).toString();

const scalingFactorsWithoutBpt: bigint[] = [],
parsedTokensWithoutBpt: string[] = [],
parsedBalancesWithoutBpt: string[] = [];
const bptIndex = parsedTokens.indexOf(pool.address);
if (bptIndex !== -1) {
scalingFactors.forEach((_, i) => {
if (i !== bptIndex) {
scalingFactorsWithoutBpt.push(scalingFactors[i]);
parsedTokensWithoutBpt.push(parsedTokens[i]);
parsedBalancesWithoutBpt.push(parsedBalances[i]);
}
});
}

return {
parsedTokens,
parsedDecimals,
Expand All @@ -50,5 +91,9 @@ export const parsePoolInfo = (pool: Pool) => {
parsedSwapFee,
upScaledBalances,
scalingFactors,
scalingFactorsWithoutBpt,
parsedTokensWithoutBpt,
parsedBalancesWithoutBpt,
bptIndex,
};
};
8 changes: 8 additions & 0 deletions balancer-js/src/lib/utils/time.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const oneSecondInMs = 1000;
export const twentyFourHoursInSecs = 24 * 60 * 60;

export function toJsTimestamp(unixTimestamp: number): number {
return unixTimestamp * oneSecondInMs;
Expand All @@ -7,3 +8,10 @@ export function toJsTimestamp(unixTimestamp: number): number {
export function toUnixTimestamp(jsTimestamp: number): number {
return Math.round(jsTimestamp / oneSecondInMs);
}

export function isLessThan24Hours(incomingDateInSec: number): boolean {
const now = Math.round(Date.now() / 1000);
const difference = now - incomingDateInSec;

return difference < twentyFourHoursInSecs;
}
9 changes: 6 additions & 3 deletions balancer-js/src/modules/data/token-prices/coingecko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,13 @@ export class CoingeckoPriceRepository implements Findable<Price> {
}: { signal?: AbortSignal } = {}): Promise<Price> {
console.time(`fetching coingecko for native token`);
enum Assets {
ETH = 'matic-network',
MATIC = 'ethereum',
ETH = 'ethereum',
MATIC = 'matic-network',
XDAI = 'xdai',
}
const assetId = this.chainId === 137 ? 'matic-network' : 'ethereum';
let assetId: Assets = Assets.ETH;
if (this.chainId === 137) assetId = Assets.MATIC;
if (this.chainId === 100) assetId = Assets.XDAI;
return axios
.get<{ [key in Assets]: Price }>(
`https://api.coingecko.com/api/v3/simple/price/?vs_currencies=eth,usd&ids=${assetId}`,
Expand Down
15 changes: 12 additions & 3 deletions balancer-js/src/modules/data/token-yields/repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,21 @@ 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 { tranchess, yieldTokens as tranchessTokens } from './tokens/tranchess';
import { usdr, yieldTokens as usdrTokens } from './tokens/usdr';
import { stafi, yieldTokens as stafiTokens } from './tokens/stafi';
import { tessera, yieldTokens as tesseraTokens } from './tokens/tessera';
import { Network, Findable } from '@/types';

/**
* Common interface for fetching APR from external sources
* @interal
*
* @param address is optional, used when same source, eg: aave has multiple tokens and all of them can be fetched in one call.
* @param network is optional, used when same source, eg: aave has multiple tokens and all of them can be fetched in one call.
* @param other is optional, used for passing mocked data for testing.
*/
export interface AprFetcher {
(network?: Network): Promise<{ [address: string]: number }>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(network?: Network, other?: any): Promise<{ [address: string]: number }>;
}

const yieldSourceMap: { [address: string]: AprFetcher } = Object.fromEntries([
Expand All @@ -32,6 +38,9 @@ const yieldSourceMap: { [address: string]: AprFetcher } = Object.fromEntries([
...Object.values(fraxTokens).map((k) => [k, sfrxETH]),
...Object.values(staderLabsTokens).map((k) => [k, maticX]),
...Object.values(tranchessTokens).map((k) => [k, tranchess]),
...Object.values(usdrTokens).map((k) => [k, usdr]),
...Object.values(stafiTokens).map((k) => [k, stafi]),
...Object.values(tesseraTokens).map((k) => [k, tessera]),
]);

export class TokenYieldsRepository implements Findable<number> {
Expand All @@ -53,7 +62,7 @@ export class TokenYieldsRepository implements Findable<number> {
Object.keys(this.sources).includes(lowercase) &&
!Object.keys(this.yields).includes(lowercase)
) {
await this.fetch(address);
await this.fetch(lowercase);
}

return this.yields[lowercase];
Expand Down
Loading

0 comments on commit 5129524

Please sign in to comment.