def get_OCEAN_in_BPTs(state, agent): """Value of BPTs that this agent owns across all pools, denominated in OCEAN Args: state: SimState -- SimState, holds all pool agents (& their pools) agent: AgentBase -- agent of interest Returns: value_held: float -- value of BPTs, denominated in OCEAN """ OCEAN_address = globaltokens.OCEAN_address() value_held = 0 for pool_agent in state.agents.filterToPool().values(): pool = pool_agent._pool DT = pool_agent._dt price = fromBase18(pool.getSpotPrice(OCEAN_address, DT.address)) pool_value_DT = price * fromBase18(pool.getBalance(DT.address)) pool_value_OCEAN = fromBase18(pool.getBalance(OCEAN_address)) pool_value = pool_value_DT + pool_value_OCEAN amt_pool_BPTs = fromBase18(pool.totalSupply()) agent_percent_pool = agent.BPT(pool) / amt_pool_BPTs value_held += agent_percent_pool * pool_value return value_held
def sellDTV4(self, pool, DT, DT_sell_amt: float, min_OCEAN_amt: float = 0.0): """Swap DT for OCEAN. min_OCEAN_amt>0 protects from slippage.""" DT.approve(pool.address, toBase18(DT_sell_amt), {"from": self._account}) tokenIn_address = DT.address # entering pool tokenAmountIn_base = toBase18(DT_sell_amt) # "" tokenOut_address = globaltokens.OCEAN_address() # leaving pool minAmountOut_base = toBase18(min_OCEAN_amt) # "" maxPrice_base = 2 ** 255 # limit by min_OCEAN_amt, not price marketFeeAddress = OPF_ADDRESS tokenInOutMarket = [ tokenIn_address, tokenOut_address, marketFeeAddress, ] # [tokenIn,tokenOut,marketFeeAddress] amountsInOutMaxFee = [ tokenAmountIn_base, minAmountOut_base, maxPrice_base, 0, ] # [exactAmountIn,minAmountOut,maxPrice,_swapMarketFee] pool.swapExactAmountIn( tokenInOutMarket, amountsInOutMaxFee, {"from": self._account}, ) self.resetCachedInfo()
def _candPoolAgents( # pylint: disable=too-many-locals self, state ) -> List[PoolAgent]: """Pools that this agent can afford to buy 1.0 datatokens from, at least based on a first approximation. """ OCEAN_address = globaltokens.OCEAN_address() OCEAN_base = toBase18(self.OCEAN()) all_pool_agents = state.agents.filterToPoolV4() cand_pool_agents = [] for pool_name, pool_agent in all_pool_agents.items(): # filter 1: pool rugged? if hasattr(state, "rugged_pools") and pool_name in state.rugged_pools: continue # filter 2: agent has enough funds? pool = pool_agent.pool DT_address = pool_agent.datatoken_address tokenAmountOut = toBase18(1.0) # number of DTs swapFee = pool.getSwapFee() OCEANamountIn_base = pool.getAmountInExactOut( OCEAN_address, DT_address, tokenAmountOut, swapFee ) if OCEANamountIn_base >= OCEAN_base: continue # passed all filters! Add this agent cand_pool_agents.append(pool_agent) return cand_pool_agents
def test_get_OCEAN_in_BPTs(alice_info): state = MockSimState() pool, DT = alice_info.pool, alice_info.DT pool_agent = PoolAgent("pool_agent", pool) state.agents["agent1"] = pool_agent foo_agent = FooAgent("foo_agent") OCEAN_address = globaltokens.OCEAN_address() price = fromBase18(pool.getSpotPrice(OCEAN_address, DT.address)) pool_value_DT = price * fromBase18(pool.getBalance(DT.address)) pool_value_OCEAN = fromBase18(pool.getBalance(OCEAN_address)) pool_value = pool_value_DT + pool_value_OCEAN # case: foo_agent no BPTs value_held = KPIs.get_OCEAN_in_BPTs(state, foo_agent) foo_agent_pool_shape = foo_agent._BPT / fromBase18(pool.totalSupply()) assert value_held == approx(foo_agent_pool_shape * pool_value) # case: foo_agent has all BPTs foo_agent._BPT = fromBase18( pool.totalSupply()) # make pool think agent has 100% of BPTs value_held = KPIs.get_OCEAN_in_BPTs(state, foo_agent) assert value_held == 1.0 * pool_value
def buyDTV4(self, pool, DT, DT_buy_amt: float, max_OCEAN_allow: float): """Swap OCEAN for DT, oceanv4 contracts""" OCEAN = globaltokens.OCEANtoken() OCEAN.approve(pool.address, toBase18(max_OCEAN_allow), {"from": self._account}) tokenIn_address = globaltokens.OCEAN_address() tokenOut_address = DT.address marketFeeAddress = OPF_ADDRESS maxAmountIn_base = toBase18(max_OCEAN_allow) tokenAmountOut_base = toBase18(DT_buy_amt) maxPrice_base = 2 ** 255 tokenInOutMarket = [ tokenIn_address, tokenOut_address, marketFeeAddress, ] # // [tokenIn,tokenOut,marketFeeAddress] amountsInOutMaxFee = [ maxAmountIn_base, tokenAmountOut_base, maxPrice_base, 0, ] # [maxAmountIn,exactAmountOut,maxPrice,_swapMarketFee] pool.swapExactAmountOut( tokenInOutMarket, amountsInOutMaxFee, {"from": self._account} ) self.resetCachedInfo()
def _datatokenAddress(self): addrs = self._pool.getCurrentTokens() assert len(addrs) == 2 OCEAN_addr = globaltokens.OCEAN_address() for addr in addrs: if addr != OCEAN_addr: return addr raise AssertionError("should never get here")
def _poolToDTaddress(pool) -> str: """Return the address of the datatoken of this pool. Don't make this public because it has strong assumptions: assumes 2 tokens; assumes one token is OCEAN; assumes other is DT """ cur_addrs = pool.getCurrentTokens() assert len(cur_addrs) == 2, "this method currently assumes 2 tokens" OCEAN_addr = globaltokens.OCEAN_address() assert OCEAN_addr in cur_addrs, "this method expects one token is OCEAN" DT_addr = [addr for addr in cur_addrs if addr != OCEAN_addr][0] return DT_addr
def unstakeOCEAN(self, BPT_unstake: float, pool): tokenOut_address = globaltokens.OCEAN_address() poolAmountIn_base = toBase18(BPT_unstake) minAmountOut_base = toBase18(0.0) pool.exitswapPoolAmountIn( tokenOut_address, poolAmountIn_base, minAmountOut_base, {"from": self._account}, ) self.resetCachedInfo()
def stakeOCEAN(self, OCEAN_stake: float, pool): """Convert some OCEAN to DT, then add both as liquidity.""" OCEAN = globaltokens.OCEANtoken() OCEAN.approve(pool.address, toBase18(OCEAN_stake), {"from": self._account}) tokenIn_address = globaltokens.OCEAN_address() tokenAmountIn_base = toBase18(OCEAN_stake) minPoolAmountOut_base = toBase18(0.0) pool.joinswapExternAmountIn( tokenIn_address, tokenAmountIn_base, minPoolAmountOut_base, {"from": self._account}, ) self.resetCachedInfo()
def test_get_OCEAN_in_DTs(alice_info): state = MockSimState() pool, DT = alice_info.pool, alice_info.DT pool_agent = PoolAgent("pool_agent", pool) state.agents["agent1"] = pool_agent foo_agent = FooAgent("foo_agent") OCEAN_address = globaltokens.OCEAN_address() price = fromBase18(pool.getSpotPrice(OCEAN_address, DT.address)) amt_DT = foo_agent.DT("bar") assert amt_DT == 3.0 value_held = KPIs.get_OCEAN_in_DTs(state, foo_agent) assert value_held == amt_DT * price
def sellDT(self, pool, DT, DT_sell_amt: float, min_OCEAN_amt: float = 0.0): """Swap DT for OCEAN. min_OCEAN_amt>0 protects from slippage.""" DT.approve(pool.address, toBase18(DT_sell_amt), {"from": self._account}) tokenIn_address = DT.address # entering pool tokenAmountIn_base = toBase18(DT_sell_amt) # "" tokenOut_address = globaltokens.OCEAN_address() # leaving pool minAmountOut_base = toBase18(min_OCEAN_amt) # "" maxPrice_base = 2 ** 255 # limit by min_OCEAN_amt, not price pool.swapExactAmountIn( tokenIn_address, tokenAmountIn_base, tokenOut_address, minAmountOut_base, maxPrice_base, {"from": self._account}, ) self.resetCachedInfo()
def buyDT(self, pool, DT, DT_buy_amt: float, max_OCEAN_allow: float): """Swap OCEAN for DT""" OCEAN = globaltokens.OCEANtoken() OCEAN.approve(pool.address, toBase18(max_OCEAN_allow), {"from": self._account}) tokenIn_address = globaltokens.OCEAN_address() maxAmountIn_base = toBase18(max_OCEAN_allow) tokenOut_address = DT.address tokenAmountOut_base = toBase18(DT_buy_amt) maxPrice_base = 2 ** 255 pool.swapExactAmountOut( tokenIn_address, maxAmountIn_base, tokenOut_address, tokenAmountOut_base, maxPrice_base, {"from": self._account}, ) self.resetCachedInfo()
def get_OCEAN_in_DTs(state, agent) -> float: """Value of DT that this agent staked across all pools, denominated in OCEAN Args: state: SimState -- SimState, holds all pool agents (& their pools) agent: AgentBase -- agent of interest Returns: value_held: float -- value staked, denominated in OCEAN """ OCEAN_address = globaltokens.OCEAN_address() value_held = 0.0 for pool_agent in state.agents.filterToPoolV4().values(): pool = pool_agent._pool DT = pool_agent._dt swapFee = pool.getSwapFee() price = fromBase18(pool.getSpotPrice(OCEAN_address, DT.address, swapFee)) amt_DT = agent.DT(DT) value_held += amt_DT * price return value_held
def test_OCEAN(): assert globaltokens.OCEANtoken().symbol() == "OCEAN" assert globaltokens.OCEAN_address()[:2] == "0x"
import brownie import pytest import sol057.contracts.oceanv3.oceanv3util from util import globaltokens from util.base18 import toBase18, fromBase18 accounts = brownie.network.accounts account0, account1 = accounts[0], accounts[1] address0, address1 = account0.address, account1.address OCEAN_address = globaltokens.OCEAN_address() HUGEINT = 2**255 def test_notokens_basic(): pool = _deployBPool() assert not pool.isPublicSwap() assert not pool.isFinalized() assert not pool.isBound(OCEAN_address) assert pool.getNumTokens() == 0 assert pool.getCurrentTokens() == [] with pytest.raises(Exception): pool.getFinalTokens() # pool's not finalized assert pool.getSwapFee() == toBase18(1e-6) assert pool.getController() == address0 assert str(pool) with pytest.raises(Exception): pool.finalize() # can't finalize if no tokens
def netlist_createLogData(state): #pylint: disable=too-many-statements """SimEngine constructor uses this""" s = [] # for console logging dataheader = [] # for csv logging: list of string datarow = [] # for csv logging: list of float # SimEngine already logs: Tick, Second, Min, Hour, Day, Month, Year # So we log other things... agents_names = [ "publisher", # "consumer", "stakerSpeculator", "speculator", # "buySellRobot", # "maliciousPublisher" # "erc721" ] # tracking OCEAN OCEANtoken = globaltokens.OCEANtoken() OCEAN_address = globaltokens.OCEAN_address() for name in agents_names: agent = state.getAgent(name) # in wallet dataheader += [f"{name}_OCEAN"] datarow += [agent.OCEAN()] # in DTs dataheader += [f"{name}_OCEAN_in_DTs"] datarow += [get_OCEAN_in_DTs(state, agent)] # in BPTs dataheader += [f"{name}_OCEAN_in_BPTs"] datarow += [get_OCEAN_in_BPTs(state, agent)] # networth dataheader += [f"{name}_OCEAN_networth"] datarow += [ agent.OCEAN() + get_OCEAN_in_DTs(state, agent) + get_OCEAN_in_BPTs(state, agent) ] dataheader += [f"DT_{name}"] dataheader += [f"BPT_{name}"] # Tracking DT and BPT balances of agents if any(state.agents.filterToPoolV4().values()): poolAgent_0 = list(state.agents.filterToPoolV4().values())[0] DT = poolAgent_0._dt amt_DT = agent.DT(DT) datarow += [amt_DT] datarow += [agent.BPT(poolAgent_0._pool)] # agent.BPT(pool) else: datarow += [0, 0] # Track pool0 # 1. DT in pool, # 2. DT balance of 1ss contract # 3. BPT total supply, # 4. BPT balance of 1ss contract, # 5. DT spot price # 6. OCEAN in pool dataheader += ["DT_pool"] dataheader += ["DT_1ss_contract"] dataheader += ["BPT_total"] dataheader += ["BPT_1ss_contract"] dataheader += ["DT_price"] dataheader += ["pool_OCEAN"] if any(state.agents.filterToPoolV4().values()): poolAgent_0 = list(state.agents.filterToPoolV4().values())[0] pool0 = poolAgent_0._pool DT = poolAgent_0._dt oneSSContractAddress = pool0.getController() datarow += [fromBase18(DT.balanceOf(pool0.address))] # 1 datarow += [fromBase18(DT.balanceOf(oneSSContractAddress))] # 2 datarow += [fromBase18(pool0.totalSupply())] # 3 datarow += [fromBase18(pool0.balanceOf(oneSSContractAddress))] # 4 swapFee = pool0.getSwapFee() datarow += [ fromBase18(pool0.getSpotPrice(OCEAN_address, DT.address, swapFee)) ] # 5 datarow += [fromBase18(OCEANtoken.balanceOf(pool0.address))] else: datarow += [0, 0, 0, 0, 0, 0] pool_agents = state.agents.filterToPoolV4() n_pools = len(pool_agents) s += [f"; # pools={n_pools}"] dataheader += ["n_pools"] datarow += [n_pools] rugged_pool = state.rugged_pools n_rugged = len(rugged_pool) # s += [f"; # rugged pools={n_rugged}"] s += [f"; # block height={brownie.chain.height}"] dataheader += ["n_rugged"] datarow += [n_rugged] return s, dataheader, datarow