diff --git a/balpy/balancerv2cad/src/balancerv2cad/StableMath.py b/balpy/balancerv2cad/src/balancerv2cad/StableMath.py index 1bc5ac2..802514f 100644 --- a/balpy/balancerv2cad/src/balancerv2cad/StableMath.py +++ b/balpy/balancerv2cad/src/balancerv2cad/StableMath.py @@ -15,12 +15,10 @@ class BalancerMathResult: class StableMath: - # ------------------------------------- @staticmethod def calculateInvariant(amplificationParameter: Decimal, balances: list) -> Decimal: - # /********************************************************************************************** # // invariant // # // D = invariant D^(n+1) // @@ -224,7 +222,6 @@ def calcInGivenOut( tokenIndexOut: int, tokenAmountOut: Decimal, ) -> Decimal: - # /************************************************************************************************************** # // inGivenOut token x for y - polynomial equation to solve // # // ax = amount in to calculate // @@ -257,7 +254,6 @@ def calcOutGivenIn( tokenIndexOut: int, tokenAmountIn: Decimal, ) -> Decimal: - # /************************************************************************************************************** # // outGivenIn token x for y - polynomial equation to solve // # // ay = amount out to calculate // @@ -340,7 +336,6 @@ def calcTokenInGivenExactBptOut( def calcTokensOutGivenExactBptIn( balances: list, bptAmountIn: Decimal, bptTotalSupply: Decimal ) -> list: - # /********************************************************************************************** # // exactBPTInForTokensOut // # // (per token) // diff --git a/balpy/balancerv2cad/src/balancerv2cad/WeightedMath.py b/balpy/balancerv2cad/src/balancerv2cad/WeightedMath.py index 731826a..dfb342e 100644 --- a/balpy/balancerv2cad/src/balancerv2cad/WeightedMath.py +++ b/balpy/balancerv2cad/src/balancerv2cad/WeightedMath.py @@ -17,7 +17,6 @@ class WeightedMath: @staticmethod def calculate_invariant( normalized_weights: List[Decimal], balances: List[Decimal]): - # /********************************************************************************************** # // invariant _____ // # // wi = weight index i | | wi // @@ -40,7 +39,6 @@ def calc_out_given_in( weight_out: Decimal, amount_in: Decimal, ) -> Decimal: - # /********************************************************************************************** # // outGivenIn // # // aO = amountOut // @@ -69,7 +67,6 @@ def calc_in_given_out( weight_out: Decimal, amount_out: Decimal, ): - # /********************************************************************************************** # // inGivenOut // # // aO = amount_out // @@ -95,7 +92,6 @@ def calc_bpt_out_given_exact_tokens_in( bptTotalSupply: Decimal, swap_fee: Decimal, ): - balance_ratios_with_fee = [None] * len(amounts_in) invariant_ratio_with_fees = 0 for i in range(len(balances)): @@ -142,7 +138,6 @@ def calc_token_in_given_exact_bpt_out( bpt_total_supply: Decimal, swap_fee: Decimal, ) -> Decimal: - # /****************************************************************************************** # // tokenInForExactBPTOut // # // a = amountIn // @@ -176,7 +171,6 @@ def calc_bpt_in_given_exact_tokens_out( bpt_total_supply: Decimal, swap_fee: Decimal, ) -> Decimal: - balance_ratios_without_fee = [Decimal(0)] * len(amounts_out) invariant_ratio_without_fees = Decimal(0) for i in range(len(balances)): @@ -219,7 +213,6 @@ def calc_token_out_given_exact_bpt_in( bpt_total_supply: Decimal, swap_fee: Decimal, ) -> Decimal: - # /***************************************************************************************** # // exactBPTInForTokenOut // # // a = amountOut // @@ -247,7 +240,6 @@ def calc_token_out_given_exact_bpt_in( def calc_tokens_out_given_exact_bpt_in( balances: List[Decimal], bpt_amount_in: Decimal, total_bpt: Decimal ) -> List: - # /********************************************************************************************** # // exactBPTInForTokensOut // # // (per token) // @@ -271,7 +263,6 @@ def calc_due_token_protocol_swap_fee_amount( current_invariant: Decimal, protocol_swap_fee_percentage: Decimal, ) -> Decimal: - # /********************************************************************************* # /* protocol_swap_fee_percentage * balanceToken * ( 1 - (previous_invariant / current_invariant) ^ (1 / weightToken)) # *********************************************************************************/ diff --git a/balpy/balancerv2cad/src/balancerv2cad/util.py b/balpy/balancerv2cad/src/balancerv2cad/util.py index 91ccdab..a0dabd5 100644 --- a/balpy/balancerv2cad/src/balancerv2cad/util.py +++ b/balpy/balancerv2cad/src/balancerv2cad/util.py @@ -9,7 +9,6 @@ def mulUp(a: Decimal, b: Decimal) -> Decimal: def divUp(a: Decimal, b: Decimal) -> Decimal: if a * b == 0: - return Decimal(0) else: getcontext().prec = 28 @@ -31,7 +30,6 @@ def divDown(a: Decimal, b: Decimal) -> Decimal: def complement(a: Decimal) -> Decimal: - return Decimal(1 - a) if a < 1 else Decimal(0) diff --git a/balpy/balancerv2cad/tests/unit-test/test_StableMath.py b/balpy/balancerv2cad/tests/unit-test/test_StableMath.py index 966d919..09b9d14 100644 --- a/balpy/balancerv2cad/tests/unit-test/test_StableMath.py +++ b/balpy/balancerv2cad/tests/unit-test/test_StableMath.py @@ -187,7 +187,6 @@ def test_calcTokensOutGivenExactBptIn(stablemath_test): # TODO give critical results def test_getTokenBalanceGivenInvariantAndAllOtherBalances(stablemath_test): - # assert StableMath.getTokenBalanceGivenInvariantAndAllOtherBalances(22, [2,3,4,20], 1, 2) == 0.002573235526125192 # self.assertAlmostEqual( diff --git a/balpy/balpy.py b/balpy/balpy.py index 03e4086..31aabb5 100644 --- a/balpy/balpy.py +++ b/balpy/balpy.py @@ -30,13 +30,13 @@ from web3.middleware import geth_poa_middleware from .enums.types import Chain, SwapType - # balpy modules from . import balancerErrors as be from .enums.stablePoolJoinExitKind import StablePhantomPoolJoinKind +from .enums.types import Chain, SwapType from .enums.weightedPoolJoinExitKind import WeightedPoolExitKind, WeightedPoolJoinKind +from .graph.graph import BALANCER_API_ENDPOINT, DEFAULT_SWAP_OPTIONS, TheGraph -BALANCER_API_ENDPOINT = "https://api-v3.balancer.fi/" class Suppressor(object): def __enter__(self): @@ -163,7 +163,7 @@ class balpy(object): "id": 8453, "blockExplorerUrl": "basescan.org", "balFrontend": "app.balancer.fi/#/base", - } + }, } apiEndpoint = BALANCER_API_ENDPOINT @@ -335,7 +335,6 @@ def __init__( usingCustomConfig = customConfigFile is not None customConfig = None if usingCustomConfig: - # load custom config file if it exists, quit if not if not os.path.isfile(customConfigFile): self.ERROR( @@ -432,6 +431,10 @@ def __init__( print() print("==============================================================") + self.graph = TheGraph( + network, customUrl=BALANCER_API_ENDPOINT, usingJsonEndpoint=True + ) + # ====================== # ====Color Printing==== # ====================== @@ -582,7 +585,6 @@ def erc20GetContract(self, tokenAddress): @cache def erc20GetDecimals(self, tokenAddress): - # keep the manually maintained cache since the # multicaller function can populate it too if tokenAddress in self.decimals.keys(): @@ -1771,7 +1773,6 @@ def balJoinPoolInit( gasEstimateOverride=-1, gasPriceGweiOverride=-1, ): - if poolDescription["poolType"] in [ "AaveLinearPool", "ERC4626LinearPool"]: slippageTolerancePercent = 1 @@ -1826,7 +1827,6 @@ def balLinearPoolInitJoin( gasEstimateOverride=-1, gasPriceGweiOverride=-1, ): - phantomBptAddress = self.balPooldIdToAddress(poolId) batchSwap = {} @@ -2414,7 +2414,6 @@ def balLoadArbitraryContract(self, address, abi): @cache def balPoolGetAbi(self, poolType): - if poolType == "HighAmpComposableStable": poolType = "ComposableStable" @@ -2968,7 +2967,6 @@ def balQueryBatchSwaps(self, originalSwapsDescription): swapsDescription = copy.deepcopy(originalSwapsDescription) vault = self.balLoadContract("Vault") for swapDescription in swapsDescription: - # do deep copy to avoid modifying the swapDescription in place, # breaking index remappings deepCopySwapDescription = copy.deepcopy(swapDescription) @@ -3048,15 +3046,10 @@ def balGetApiEndpointSor(self): ) def balSorQuery(self, data): - query = data["sor"] # scale amount based on input/output - token_for_decimals = query["orderKind"].lower() + "Token" - amount_scaled = self.erc20ScaleDecimalsWei( - query[token_for_decimals], query["amount"] - ) - query["amount"] = int(amount_scaled) + query["orderKind"].lower() + "Token" # get gas price if not provided if "gasPrice" not in query.keys(): @@ -3068,28 +3061,31 @@ def balSorQuery(self, data): # API gets grumpy when you send it numbers. Send everything as a string for field in query: query[field] = str(query[field]) - # breakpoint() - response = requests.post( - self.balGetApiEndpointSor(), - headers={"Content-Type": "application/json"}, - data=json.dumps(query), + response = self.graph.getSorGetSwapPaths( + chain=self.network, + swapAmount=query["amount"], + tokenIn=query["sellToken"], + tokenOut=query["buyToken"], + swapType=SwapType.EXACT_IN.value, ) - batch_swap = self.balSorResponseToBatchSwapFormat( - data, response.json()) + batch_swap = self.balSorResponseToBatchSwapFormat(data, response) + + batch_swap["returnAmount"] = response["returnAmount"] return batch_swap - def _getSorGetSwapPaths(self, - chain: Chain, - swapAmount: float, - tokenIn: str, - tokenOut: str, - swapType: SwapType = SwapType.EXACT_IN, - swapOptions: dict = None, - queryBatchSwap: bool = True, - ): + def _getSorGetSwapPaths( + self, + chain: Chain, + swapAmount: float, + tokenIn: str, + tokenOut: str, + swapType: SwapType = SwapType.EXACT_IN, + swapOptions: dict = None, + queryBatchSwap: bool = True, + ): """ Calls the SOR api from the Balancer to get swap paths. Args: @@ -3102,7 +3098,7 @@ def _getSorGetSwapPaths(self, queryBatchSwap (bool, optional): Whether to query batch swap. Defaults to True. Returns: dict: The response from the SOR. - + """ if not swapOptions: swapOptions = DEFAULT_SWAP_OPTIONS @@ -3134,7 +3130,7 @@ def _getSorGetSwapPaths(self, assetInIndex, poolId } - + returnAmount, tokenInAmount, tokenOutAmount, @@ -3148,13 +3144,17 @@ def _getSorGetSwapPaths(self, "swapAmount": swapAmount, "tokenIn": tokenIn, "tokenOut": tokenOut, - "swapType": swapType, + "swapType": swapType.value, "queryBatchSwap": queryBatchSwap, } - response = requests.post(BALANCER_API_ENDPOINT, json={"query": query_string, "variables": params}) + response = requests.post( + BALANCER_API_ENDPOINT, json={ + "query": query_string, "variables": params} + ) + + return response.json()["data"]["sorGetSwapPaths"] - return response.json()['data']['sorGetSwapPaths'] def balSorResponseToBatchSwapFormat(self, query, response): sor = query["sor"] del query["sor"] @@ -3401,7 +3401,7 @@ def generateDeploymentsDocsTable(self): contracts = ["Contracts", contractDashLine] + contracts addresses = ["Addresses", addressDashLine] + addresses - for (c, a) in zip(contracts, addresses): + for c, a in zip(contracts, addresses): cPadded = padWithSpaces(c, longestContractStringLength) aPadded = padWithSpaces(a, longestAddressStringLength) line = "| " + cPadded + " | " + aPadded + " |\n" diff --git a/balpy/enums/types.py b/balpy/enums/types.py index ff91137..5b0d39a 100644 --- a/balpy/enums/types.py +++ b/balpy/enums/types.py @@ -4,6 +4,7 @@ from enum import Enum + class Chain(Enum): MAINNET = "MAINNET" GNOSIS = "GNOSIS" @@ -13,4 +14,4 @@ class Chain(Enum): class SwapType(Enum): - EXACT_IN = "EXACT_IN" \ No newline at end of file + EXACT_IN = "EXACT_IN" diff --git a/balpy/graph/graph.py b/balpy/graph/graph.py index f7302d0..500799a 100644 --- a/balpy/graph/graph.py +++ b/balpy/graph/graph.py @@ -13,13 +13,13 @@ from balpy.enums.types import Chain, SwapType -from balpy.balpy import BALANCER_API_ENDPOINT - DEFAULT_SWAP_OPTIONS = { "maxPools": 4, "queryBatchSwap": True, } +BALANCER_API_ENDPOINT = "https://api-v3.balancer.fi/" + class TheGraph(object): client = None @@ -47,7 +47,6 @@ def printJson(self, curr_dict): print(json.dumps(curr_dict, indent=4)) def callCustomEndpoint(self, query): - query = query.replace("\n", " ") query = query.replace("\t", "") queryDict = {"query": query} @@ -94,7 +93,6 @@ def initBalV2Graph(self, customUrl, usingJsonEndpoint, verbose=False): print("Successfully initialized on network:", self.network) def getPoolTokens(self, pool_id, verbose=False): - self.assertInit() if verbose: @@ -126,7 +124,6 @@ def getPoolTokens(self, pool_id, verbose=False): return response["poolTokens"] def getNumPools(self, verbose=False): - self.assertInit() if verbose: print("Querying number of pools...") @@ -155,7 +152,6 @@ def getNumPools(self, verbose=False): return None def getPools(self, batch_size, skips, verbose=False): - self.assertInit() if verbose: print( @@ -189,7 +185,6 @@ def getPools(self, batch_size, skips, verbose=False): return response def getV2Pools(self, batch_size, verbose=False): - if self.client is None: self.initBalV2Graph(verbose=verbose) @@ -216,7 +211,6 @@ def getV2Pools(self, batch_size, verbose=False): return pool_tokens def getV2PoolIDs(self, batch_size, pool_filter=None, verbose=False): - if self.client is None: self.initBalV2Graph(verbose=verbose) @@ -252,7 +246,6 @@ def getV2PoolIDs(self, batch_size, pool_filter=None, verbose=False): return data def getPoolBptPriceEstimate(self, poolId, verbose=False): - self.assertInit() if verbose: print("Getting data for pool", poolId, "from the subgraph...") @@ -301,15 +294,17 @@ def getPoolsAndTokens(self, batch_size, skips, verbose=False): response = self.client.execute(gql(formatted_query_string)) return response - def getSorGetSwapPaths(self, - chain: Chain, - swapAmount: float, - tokenIn: str, - tokenOut: str, - swapType: SwapType = SwapType.EXACT_IN, - swapOptions: dict = None, - queryBatchSwap: bool = True, - ): + def getSorGetSwapPaths( + self, + chain: Chain, + swapAmount: float, + tokenIn: str, + tokenOut: str, + swapType: str = SwapType.EXACT_IN.value, + swapOptions: dict = None, + queryBatchSwap: bool = True, + retries: int = 3, + ): """ Calls the SOR api from the Balancer to get swap paths. Args: @@ -322,7 +317,7 @@ def getSorGetSwapPaths(self, queryBatchSwap (bool, optional): Whether to query batch swap. Defaults to True. Returns: dict: The response from the SOR. - + """ if not swapOptions: swapOptions = DEFAULT_SWAP_OPTIONS @@ -354,7 +349,7 @@ def getSorGetSwapPaths(self, assetInIndex, poolId } - + returnAmount, tokenInAmount, tokenOutAmount, @@ -364,7 +359,7 @@ def getSorGetSwapPaths(self, } """ params = { - "chain": chain, + "chain": chain.upper(), "swapAmount": swapAmount, "tokenIn": tokenIn, "tokenOut": tokenOut, @@ -372,54 +367,52 @@ def getSorGetSwapPaths(self, "queryBatchSwap": queryBatchSwap, } - response = requests.post(BALANCER_API_ENDPOINT, json={"query": query_string, "variables": params}) - - return response.json()['data']['sorGetSwapPaths'] - -def main(): - - batch_size = 30 - print() - - if len(sys.argv) < 2: - print("Usage: python", sys.argv[0], "") - print("No network given; defaulting to mainnet Ethereum") - network = "mainnet" - else: - network = sys.argv[1] + response = requests.post( + BALANCER_API_ENDPOINT, json={ + "query": query_string, "variables": params} + ) + if response.status_code != 200: + if "banned" in response.text and retries > 0: + # We sleep for 5 seconds to avoid being banned + time.sleep(5) + print ( + "We got banned from the Balancer API. Sleeping for 5 seconds." + ) + return self.getSorGetSwapPaths( + chain, swapAmount, tokenIn, tokenOut, swapType, swapOptions, queryBatchSwap + ) - networks = ["mainnet", "kovan", "polygon", "arbitrum", "gnosis"] + raise Exception( + f"Error querying the Balancer API: {response.text} {response.status_code}" + ) - if network not in networks: - print("Network", network, "is not supported!") - print("Supported networks are:") - for n in networks: - print("\t" + n) - print("Quitting") - quit() + return response.json()["data"]["sorGetSwapPaths"] - verbose = True - graph = TheGraph(network, customUrl=BALANCER_API_ENDPOINT, usingJsonEndpoint=True) - # pools = graph.getNumPools(verbose=verbose) - # pools = graph.getV2Pools(batch_size, verbose=verbose) - # graph.printJson(pools) +def main(): + network = Chain.GNOSIS.value + graph = TheGraph( + network, + customUrl=BALANCER_API_ENDPOINT, + usingJsonEndpoint=True) for i in range(0, 10): config = { - "chain": Chain.GNOSIS.value, - "swapAmount": str(50.00 * (1 + i ** 3)), + "chain": network, + "swapAmount": str(50.00 * (1 + i**3)), "swapType": "EXACT_IN", # "tokenOut": "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f", # "tokenIn": "0xe91d153e0b41518a2ce8dd3d7944fa863463a97d", "tokenIn": "0xcE11e14225575945b8E6Dc0D4F2dD4C570f79d9f", "tokenOut": "0xe91d153e0b41518a2ce8dd3d7944fa863463a97d", } - + paths = graph.getSorGetSwapPaths(**config) - rate = paths['effectivePrice'] - print( f"{1 / float(rate) } for 1 {config['tokenIn']} to {config['tokenOut']} size: {config['swapAmount']} output: {paths['returnAmount']}") + rate = paths["effectivePrice"] + print( + f"{1 / float(rate) } for 1 {config['tokenIn']} to {config['tokenOut']} size: {config['swapAmount']} output: {paths['returnAmount']}" + ) if __name__ == "__main__": diff --git a/generateMissingPoolArtifacts.py b/generateMissingPoolArtifacts.py index 0fa91b0..9c55679 100644 --- a/generateMissingPoolArtifacts.py +++ b/generateMissingPoolArtifacts.py @@ -3,7 +3,6 @@ def main(): - deprecated_string = "" for i in range(2): # Traverse all deprecated tasks @@ -27,7 +26,6 @@ def main(): for f in files: if "pool" in f.lower() and "factory" in f.lower(): - # Check to see if there is an artifact for the pool AND the # factory print("\tFound factory:", f) diff --git a/samples/batchSwaps/batchSwapSample.py b/samples/batchSwaps/batchSwapSample.py index d263d65..dd7f52f 100644 --- a/samples/batchSwaps/batchSwapSample.py +++ b/samples/batchSwaps/batchSwapSample.py @@ -6,7 +6,6 @@ def main(): - if len(sys.argv) < 2: print("Usage: python3", sys.argv[0], "/path/to/swap.json") quit() diff --git a/samples/batchSwaps/queryBatchSwapSample.py b/samples/batchSwaps/queryBatchSwapSample.py index a4bdf44..9084f9c 100644 --- a/samples/batchSwaps/queryBatchSwapSample.py +++ b/samples/batchSwaps/queryBatchSwapSample.py @@ -6,7 +6,6 @@ def main(): - if len(sys.argv) < 2: print("Usage: python3", sys.argv[0], "/path/to/swap.json") quit() diff --git a/samples/batchSwaps/queryBatchSwapsSample.py b/samples/batchSwaps/queryBatchSwapsSample.py index a32d1f7..47d2766 100644 --- a/samples/batchSwaps/queryBatchSwapsSample.py +++ b/samples/batchSwaps/queryBatchSwapsSample.py @@ -6,7 +6,6 @@ def main(): - if len(sys.argv) < 2: print("Usage: python3", sys.argv[0], "/path/to/swap.json") quit() diff --git a/samples/batchSwaps/sorSwapSample.py b/samples/batchSwaps/sorSwapSample.py index 4961ee7..97666d7 100644 --- a/samples/batchSwaps/sorSwapSample.py +++ b/samples/batchSwaps/sorSwapSample.py @@ -7,7 +7,6 @@ def main(): - if len(sys.argv) < 2: print("Usage: python3", sys.argv[0], "/path/to/swap.json") quit() diff --git a/samples/joinPool/joinPoolSample.py b/samples/joinPool/joinPoolSample.py index ad7d726..68b2772 100644 --- a/samples/joinPool/joinPoolSample.py +++ b/samples/joinPool/joinPoolSample.py @@ -8,7 +8,6 @@ def main(): - if len(sys.argv) < 2: print("Usage: python3", sys.argv[0], "/path/to/join.json") quit() diff --git a/samples/poolCreation/poolCreationSample.py b/samples/poolCreation/poolCreationSample.py index 3f5cdfa..6c9cf98 100644 --- a/samples/poolCreation/poolCreationSample.py +++ b/samples/poolCreation/poolCreationSample.py @@ -10,7 +10,6 @@ def main(): - if len(sys.argv) < 2: print("Usage: python3", sys.argv[0], "/path/to/pool.json") quit() diff --git a/samples/singleSwap/swapSample.py b/samples/singleSwap/swapSample.py index 9e36df1..b3dd45a 100644 --- a/samples/singleSwap/swapSample.py +++ b/samples/singleSwap/swapSample.py @@ -6,7 +6,6 @@ def main(): - if len(sys.argv) < 2: print("Usage: python3", sys.argv[0], "/path/to/swap.json") quit() diff --git a/samples/theGraph/getPoolIds.py b/samples/theGraph/getPoolIds.py index 7c67357..de6dd3a 100644 --- a/samples/theGraph/getPoolIds.py +++ b/samples/theGraph/getPoolIds.py @@ -5,7 +5,6 @@ def main(network="mainnet"): - batch_size = 100 networks = ["mainnet", "kovan", "polygon", "gnosis-chain"] diff --git a/samples/theGraph/getPools.py b/samples/theGraph/getPools.py index 5f6f247..0a237cd 100644 --- a/samples/theGraph/getPools.py +++ b/samples/theGraph/getPools.py @@ -5,7 +5,6 @@ def main(network="mainnet"): - batch_size = 100 networks = ["mainnet", "kovan", "polygon", "goerli", "gnosis-chain"] @@ -25,7 +24,6 @@ def main(network="mainnet"): if __name__ == "__main__": - print() if len(sys.argv) < 2: print("Usage: python", sys.argv[0], "") diff --git a/tests/test_graph.py b/tests/test_graph.py index d9db068..7777d47 100644 --- a/tests/test_graph.py +++ b/tests/test_graph.py @@ -1,6 +1,8 @@ from samples.theGraph.getPoolIds import main as get_pool_ids from samples.theGraph.getPools import main as get_pools +from balpy.graph.graph import main as endpoint_prices + TEST_NETWORK = "gnosis-chain" @@ -10,3 +12,7 @@ def test_get_pool_ids(): def test_get_pools(): get_pools(TEST_NETWORK) + + +def test_bal_graph(): + endpoint_prices()