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 createDatatokenFromDataNFT( DT_name: str, DT_symbol: str, DT_cap: int, dataNFT, from_account ): erc20_template_index = 1 strings = [ DT_name, DT_symbol, ] addresses = [ from_account.address, # minter from_account.address, # fee mgr from_account.address, # pub mkt ZERO_ADDRESS, # pub mkt fee token addr ] uints = [ toBase18(DT_cap), toBase18(0.0), # pub mkt fee amt ] _bytes: List[Any] = [] tx = dataNFT.createERC20( erc20_template_index, strings, addresses, uints, _bytes, {"from": from_account} ) DT_address = tx.events["TokenCreated"]["newTokenAddress"] DT = BROWNIE_PROJECT080.ERC20Template.at(DT_address) return DT
def test_createBPool_via_util(): brownie.chain.reset() router = oceanv4util.deployRouter(account0) (dataNFT, erc721_factory) = oceanv4util.createDataNFT("dataNFT", "DATANFT", account0, router) DT = oceanv4util.createDatatokenFromDataNFT("DT", "DTSymbol", 10000, dataNFT, account0) fundOCEANFromAbove(address0, toBase18(10000.0)) OCEAN = oceanv4util.OCEANtoken() OCEAN_init_liquidity = 2000.0 DT_OCEAN_rate = 0.1 DT_vest_amt = 100 DT_vest_num_blocks = 600 LP_swap_fee = 0.03 mkt_swap_fee = 0.01 pool = oceanv4util.createBPoolFromDatatoken( DT, erc721_factory, account0, OCEAN_init_liquidity, DT_OCEAN_rate, DT_vest_amt, DT_vest_num_blocks, LP_swap_fee, mkt_swap_fee, ) pool_address = pool.address assert pool.getBaseTokenAddress() == OCEAN.address assert OCEAN.balanceOf(pool_address) < toBase18(10000.0) assert pool.getMarketFee() == toBase18(mkt_swap_fee) assert pool.getSwapFee() == toBase18(LP_swap_fee)
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_exitPool_receiveTokens(): brownie.chain.reset() OCEAN = OCEANtoken() (DT, pool, ssbot) = _deployBPool(do_extra_funding=True) account0_DT_balance = DT.balanceOf(address0) account0_OCEAN_balance = OCEAN.balanceOf(address0) account0_BPT_balance = pool.balanceOf(address0) ssContractDTbalance = DT.balanceOf(ssbot.address) ssContractBPTbalance = pool.balanceOf(ssbot.address) BPTAmountIn = toBase18(0.5) minAmountOut = [toBase18(1), toBase18(1)] tx = pool.exitPool(BPTAmountIn, minAmountOut, {"from": account0}) assert tx.events["LOG_EXIT"][0]["tokenOut"] == DT.address assert tx.events["LOG_EXIT"][1]["tokenOut"] == OCEAN.address assert tx.events["LOG_EXIT"][0][ "tokenAmountOut"] + account0_DT_balance == DT.balanceOf(address0) assert tx.events["LOG_EXIT"][1][ "tokenAmountOut"] + account0_OCEAN_balance == OCEAN.balanceOf(address0) assert pool.balanceOf(address0) + BPTAmountIn == account0_BPT_balance # check the ssContract BPT and DT balance didn"t change assert ssContractBPTbalance == pool.balanceOf(ssbot.address) assert ssContractDTbalance == DT.balanceOf(ssbot.address)
def test_basic(): n_blocks = len(chain) beneficiary = address1 start_block = n_blocks + 1 num_blocks_duration = 4 # constructor vesting_wallet = BROWNIE_PROJECT080.VestingWallet080.deploy( beneficiary, toBase18(start_block), toBase18(num_blocks_duration), {"from": account0}, ) assert vesting_wallet.beneficiary() == beneficiary start_block_measured = int(vesting_wallet.startBlock() / 1e18) assert start_block_measured in [ start_block - 1, start_block, start_block + 1 ] assert int(vesting_wallet.numBlocksDuration() / 1e18) == 4 assert vesting_wallet.released() == 0 # time passes chain.mine(blocks=15, timedelta=1) assert vesting_wallet.released() == 0 # haven't released anything # call release vesting_wallet.release() assert vesting_wallet.released() == 0 # wallet never got funds to release!
def test_joinswapExternAmountIn_addOCEAN(): brownie.chain.reset() OCEAN = OCEANtoken() (DT, pool, ssbot) = _deployBPool(do_extra_funding=True) account0_DT_balance = DT.balanceOf(address0) ssContractDTbalance = DT.balanceOf(ssbot.address) ssContractBPTbalance = pool.balanceOf(ssbot.address) oceanAmountIn = toBase18(100) minBPTOut = toBase18(0.1) tx = pool.joinswapExternAmountIn(oceanAmountIn, minBPTOut, {"from": account0}) assert tx.events["LOG_JOIN"][0]["tokenIn"] == OCEAN.address assert tx.events["LOG_JOIN"][0]["tokenAmountIn"] == oceanAmountIn assert tx.events["LOG_JOIN"][1]["tokenIn"] == DT.address ssbotAmountIn = ssContractDTbalance - DT.balanceOf(ssbot.address) assert ssbotAmountIn == tx.events["LOG_JOIN"][1]["tokenAmountIn"] # we check ssContract actually moved DT and got back BPT assert (DT.balanceOf(ssbot.address) == ssContractDTbalance - tx.events["LOG_JOIN"][1]["tokenAmountIn"]) assert (pool.balanceOf(ssbot.address) == ssContractBPTbalance + tx.events["LOG_BPT"]["bptAmount"]) # no datatoken where taken from account0 assert account0_DT_balance == DT.balanceOf(address0)
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 test_setSwapFee_fails(): pool = _deployBPool() with pytest.raises(Exception): # fail, because account1 isn't controller pool.setSwapFee(toBase18(0.011), {"from": account1}) pool.setController(address1, {"from": account0}) pool.setSwapFee(toBase18(0.011), {"from": account1}) # pass now
def _createDatatoken(self, dt_name: str, mint_amt: float): """Create datatoken contract and mint DTs to self.""" account = self._wallet._account DT = oceanv3util.newDatatoken("", dt_name, dt_name, toBase18(mint_amt), account) DT.mint(account.address, toBase18(mint_amt), {"from": account}) self._wallet.resetCachedInfo() return DT
def _dts(): """Create datatokens, and mint into account0""" cap = toBase18(1e3) dts = [] for i in range(2): dt = newDatatoken("blob", f"datatoken{i+1}", f"DT{i+1}", cap, account0) dt.mint(account0, toBase18(1e3), {"from": account0}) dts.append(dt) return dts
def test_via_util(): dt = sol057.contracts.oceanv3.oceanv3util.newDatatoken( "foo_blob", "datatoken1", "DT1", toBase18(100.0), account0) dt.mint(address0, toBase18(100.0), {"from": account0}) assert dt.name() == "datatoken1" assert dt.symbol() == "DT1" assert dt.decimals() == 18 assert dt.balanceOf(address0) == toBase18(100.0) assert dt.blob() == "foo_blob"
def test_calcSpotPrice(): pool = _deployBPool() tokenBalanceIn = toBase18(10.0) tokenWeightIn = toBase18(1.0) tokenBalanceOut = toBase18(11.0) tokenWeightOut = toBase18(1.0) swapFee = 0 x = pool.calcSpotPrice(tokenBalanceIn, tokenWeightIn, tokenBalanceOut, tokenWeightOut, swapFee) assert round(fromBase18(x), 3) == 0.909
def test_rebind_more_tokens(T1, T2): pool = _createPoolWith2Tokens(T1, T2, 90.0, 10.0, 9.0, 1.0) # insufficient allowance with pytest.raises(Exception): pool.rebind(T1.address, toBase18(120.0), toBase18(9.0), {"from": account0}) # sufficient allowance T1.approve(pool.address, toBase18(30.0), {"from": account0}) pool.rebind(T1.address, toBase18(120.0), toBase18(9.0), {"from": account0})
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 test_exitswapPoolAmountIn(T1, T2): pool = _createPoolWith2Tokens(T1, T2, 90.0, 10.0, 9.0, 1.0) BPT = pool pool.finalize({"from": account0}) assert fromBase18(T1.balanceOf(address0)) == 910.0 tokenOut_address = T1.address poolAmountIn = toBase18(10.0) # BPT spent minAmountOut = toBase18(1.0) # min T1 wanted pool.exitswapPoolAmountIn(tokenOut_address, poolAmountIn, minAmountOut, {"from": account0}) assert fromBase18(T1.balanceOf(address0)) >= (910.0 + 1.0) assert fromBase18(BPT.balanceOf(address0)) == (100.0 - 10.0)
def test_calcPoolOutGivenSingleIn(): pool = _deployBPool() tokenBalanceIn = toBase18(10.0) tokenWeightIn = toBase18(1.0) poolSupply = toBase18(120.0) totalWeight = toBase18(2.0) tokenAmountIn = toBase18(0.1) swapFee = 0 x = pool.calcPoolOutGivenSingleIn(tokenBalanceIn, tokenWeightIn, poolSupply, totalWeight, tokenAmountIn, swapFee) assert round(fromBase18(x), 3) == 0.599
def test_calcSingleOutGivenPoolIn(): pool = _deployBPool() tokenBalanceOut = toBase18(10.0) tokenWeightOut = toBase18(1.0) poolSupply = toBase18(120.0) totalWeight = toBase18(2.0) poolAmountIn = toBase18(10.0) swapFee = 0 x = pool.calcSingleOutGivenPoolIn(tokenBalanceOut, tokenWeightOut, poolSupply, totalWeight, poolAmountIn, swapFee) assert round(fromBase18(x), 3) == 1.597
def test_joinswapPoolAmountOut(T1, T2): pool = _createPoolWith2Tokens(T1, T2, 90.0, 10.0, 9.0, 1.0) BPT = pool pool.finalize({"from": account0}) T1.approve(pool.address, toBase18(90.0), {"from": account0}) assert fromBase18(T1.balanceOf(address0)) == 910.0 tokenIn_address = T1.address poolAmountOut = toBase18(10.0) # BPT wanted maxAmountIn = toBase18(90.0) # max T1 to spend pool.joinswapPoolAmountOut(tokenIn_address, poolAmountOut, maxAmountIn, {"from": account0}) assert fromBase18(T1.balanceOf(address0)) >= (910.0 - 90.0) assert fromBase18(BPT.balanceOf(address0)) == (100.0 + 10.0)
def joinPoolAddOCEAN(self, OCEAN_stake: float, pool): """adds more liquidity with joinswapExternAmountIn (only OCEAN), oceanv4 contracts""" OCEAN = globaltokens.OCEANtoken() OCEAN.approve(pool.address, toBase18(OCEAN_stake), {"from": self._account}) tokenAmountIn_base = toBase18(OCEAN_stake) minPoolAmountOut_base = toBase18(0.1) pool.joinswapExternAmountIn( OCEAN.address, tokenAmountIn_base, minPoolAmountOut_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 _make_info(account): assert account.address != GOD_ACCOUNT.address OCEAN = globaltokens.OCEANtoken() # reset OCEAN balances on-chain, to avoid relying on brownie chain reverts # -assumes that DT and BPT in each test are new tokens each time, and # therefore don't need re-setting for a in accounts: if a.address != GOD_ACCOUNT.address: OCEAN.transfer(GOD_ACCOUNT, OCEAN.balanceOf(a), {"from": a}) class Info: def __init__(self): self.account = None self.agent = None self.DT = None self.pool = None info = Info() info.account = account class SimpleAgent(AgentBase.AgentBaseEvm): def takeStep(self, state): pass info.agent = SimpleAgent("agent1", USD=0.0, OCEAN=0.0) info.agent._wallet._account = account # force agent to use this account info.agent._wallet.resetCachedInfo() # because account changed wallet info.DT = _createDT(account) info.agent._wallet.resetCachedInfo() # because DT was deposited to account assert info.DT.balanceOf(account) == toBase18(_DT_INIT) globaltokens.fundOCEANFromAbove(account.address, toBase18(_OCEAN_INIT)) info.agent._wallet.resetCachedInfo( ) # because OCEAN was deposited to account info.pool = _createPool(info.DT, account) # create pool, stake DT & OCEAN info.agent._wallet.resetCachedInfo() # because OCEAN & DT was staked # postconditions w = info.agent._wallet OCEAN1 = w.OCEAN() assert w._cached_OCEAN_base is not None OCEAN2 = fromBase18(int(w._cached_OCEAN_base)) OCEAN3 = fromBase18(OCEAN.balanceOf(account)) assert OCEAN1 == OCEAN2 == OCEAN3, (OCEAN1, OCEAN2, OCEAN3) return info
def test_via_newDatatoken_util(): dt = sol057.contracts.oceanv3.oceanv3util.newDatatoken( "foo_blob", "datatoken1", "DT1", toBase18(100.0), account0 ) assert dt.blob() == "foo_blob" assert dt.name() == "datatoken1" assert dt.symbol() == "DT1"
def test_exitswapExternAmountOut(T1, T2): pool = _createPoolWith2Tokens(T1, T2, 90.0, 10.0, 9.0, 1.0) BPT = pool pool.finalize({"from": account0}) assert fromBase18(T1.balanceOf(address0)) == 910.0 tokenOut_address = T1.address tokenAmountOut = toBase18(2.0) # T1 wanted maxPoolAmountIn = toBase18(10.0) # max BPT spent pool.exitswapExternAmountOut( tokenOut_address, tokenAmountOut, # T1 wanted maxPoolAmountIn, # max BPT spent {"from": account0}, ) assert fromBase18(T1.balanceOf(address0)) == (910.0 + 2.0) assert fromBase18(BPT.balanceOf(address0)) >= (100.0 - 10.0)
def _deployBPool(): brownie.chain.reset() router = oceanv4util.deployRouter(account0) fundOCEANFromAbove(address0, toBase18(100000)) (dataNFT, erc721_factory) = oceanv4util.createDataNFT("dataNFT", "DATANFTSYMBOL", account0, router) DT_cap = 10000 datatoken = oceanv4util.createDatatokenFromDataNFT("DT", "DTSYMBOL", DT_cap, dataNFT, account0) OCEAN_init_liquidity = 80000 DT_OCEAN_rate = 0.1 DT_vest_amt = 1000 DT_vest_num_blocks = 600 pool = oceanv4util.createBPoolFromDatatoken( datatoken, erc721_factory, account0, OCEAN_init_liquidity, DT_OCEAN_rate, DT_vest_amt, DT_vest_num_blocks, ) return pool
def __init__(self, USD: float = 0.0, OCEAN: float = 0.0, private_key=None): AgentWalletAbstract.__init__(self, USD, OCEAN, private_key) UsdNoEvmWalletMixIn.__init__(self, USD) self._account: Account = None accounts = brownie.network.accounts if private_key is None: self._account = accounts.add() else: self._account = accounts.add(private_key=private_key) # Give the new wallet ETH to pay gas fees (but don't track otherwise) GOD_ACCOUNT.transfer(self._account, "0.01 ether") # OCEAN is tracked in EVM, not here. But we cache here for speed self._burnOCEAN_nocache() # ensure 0 OCEAN (eg >1 unit tests) self._cached_OCEAN_base: typing.Union[int, None] = None self._total_OCEAN_in: float = OCEAN assert self.OCEAN() == 0.0 globaltokens.fundOCEANFromAbove(self._account.address, toBase18(OCEAN)) self._cached_OCEAN_base = None # postconditions assert self.USD() == USD assert self.OCEAN() == OCEAN
def transferOCEAN(self, dst_wallet, amt: float) -> None: assert isinstance(dst_wallet, (AgentWalletEvm, BurnWallet)) dst_address = dst_wallet.address amt_base = toBase18(amt) assert amt_base >= 0 if amt_base == 0: return OCEAN_base = self._OCEAN_base() if OCEAN_base == 0: raise ValueError("no funds to transfer from") tol = 1e-12 if (1.0 - tol) <= amt / fromBase18(OCEAN_base) <= (1.0 + tol): amt_base = OCEAN_base if amt_base > OCEAN_base: raise ValueError( "transfer amt ({fromBase18(amt_base)})" " exceeds OCEAN holdings ({fromBase18(OCEAN_base)})" ) globaltokens.OCEANtoken().transfer( dst_address, amt_base, {"from": self._account} ) dst_wallet._total_OCEAN_in += amt self.resetCachedInfo() dst_wallet.resetCachedInfo()
def test_swapExactAmountOut(): brownie.chain.reset() OCEAN = OCEANtoken() (DT, pool, _) = _deployBPool(do_extra_funding=True) tokenInOutMarket = [ OCEAN.address, DT.address, address0, ] # // [tokenIn,tokenOut,marketFeeAddress] amountsInOutMaxFee = [toBase18(1000), toBase18(10), toBase18(1000), 0] assert DT.balanceOf(address0) == 0 pool.swapExactAmountOut(tokenInOutMarket, amountsInOutMaxFee, {"from": account0}) assert DT.balanceOf(address0) > 0
def test_calcInGivenOut(): pool = _deployBPool() tokenBalanceIn = toBase18(10.0) tokenWeightIn = toBase18(1.0) tokenBalanceOut = toBase18(10.1) tokenWeightOut = toBase18(1.0) tokenAmountOut = toBase18(1.0) swapFee = 0 x = pool.calcInGivenOut( tokenBalanceIn, tokenWeightIn, tokenBalanceOut, tokenWeightOut, tokenAmountOut, swapFee, ) assert round(fromBase18(x), 3) == 1.099
def test_calcPoolInGivenSingleOut(): pool = _deployBPool() tokenBalanceOut = toBase18(1000.0) tokenWeightOut = toBase18(5.0) poolSupply = toBase18(100.0) totalWeight = toBase18(10.0) tokenAmountOut = toBase18(0.1) swapFee = 0 x = pool.calcPoolInGivenSingleOut( tokenBalanceOut, tokenWeightOut, poolSupply, totalWeight, tokenAmountOut, swapFee, ) assert round(fromBase18(x), 3) == 0.005