def unstakeOCEAN(self, BPT_unstake: float, pool: bpool.BPool): pool.exitswapPoolAmountIn( tokenOut_address=globaltokens.OCEAN_address(), poolAmountIn_base=toBase18(BPT_unstake), minAmountOut_base=toBase18(0.0), from_wallet=self._web3wallet) self.resetCachedInfo()
def test_ERC20(alice_wallet, alice_address, bob_wallet, bob_address): #use DTFactory to create the token, and Datatoken to mint it f = dtfactory.DTFactory() dt_address = f.createToken('', 'TOK', 'TOK', web3util.toBase18(100.0), from_wallet=alice_wallet) dt = datatoken.Datatoken(dt_address) dt.mint(alice_address, web3util.toBase18(100.0), from_wallet=alice_wallet) #now that we've created & minted the token, we can use it with BToken interface token = btoken.BToken(dt_address) assert token.address == dt_address assert token.decimals() == 18 assert token.balanceOf_base(alice_address) == web3util.toBase18(100.0) assert token.balanceOf_base(bob_address) == 0 assert token.allowance_base(alice_address, bob_address) == 0 token.approve(bob_address, int(1e18), from_wallet=alice_wallet) assert token.allowance_base(alice_address, bob_address) == int(1e18) #alice sends all her tokens to Bob, then Bob sends it back alice_TOK = token.balanceOf_base(alice_address) bob_TOK = token.balanceOf_base(bob_address) token.transfer(bob_address, alice_TOK, from_wallet=alice_wallet) assert token.balanceOf_base(alice_address) == 0 assert token.balanceOf_base(bob_address) == (alice_TOK + bob_TOK) token.transfer(alice_address, alice_TOK, from_wallet=bob_wallet) assert token.balanceOf_base(alice_address) == alice_TOK assert token.balanceOf_base(bob_address) == bob_TOK
def testSendEth(): _web3 = web3util.get_web3() network = web3util.get_network() #wallet1 should have got ETH from ganache startup (see deploy.py) private_key1 = web3util.confFileValue(network, 'TEST_PRIVATE_KEY1') wallet1 = web3wallet.Web3Wallet(private_key1) orig_bal1_base = wallet1.ETH_base() print("orig bal1 = %s" % web3util.fromBase18(orig_bal1_base)) assert orig_bal1_base > web3util.toBase18(1.0) #wallet2 should have 0 ETH wallet2 = web3wallet.randomWeb3Wallet() orig_bal2_base = wallet2.ETH_base() print("orig bal2 = %s" % web3util.fromBase18(orig_bal2_base)) assert orig_bal2_base == 0 #wallet1 gives wallet2 1.0 ETH sent_base = web3util.toBase18(1.0) wallet1.sendEth(wallet2.address, sent_base) new_bal1_base = wallet1.ETH_base() new_bal2_base = wallet2.ETH_base() print("new bal1 = %s" % web3util.fromBase18(new_bal1_base)) print("new bal2 = %s" % web3util.fromBase18(new_bal2_base)) assert new_bal2_base == sent_base assert (orig_bal1_base - sent_base*1.1) < new_bal1_base < (orig_bal1_base - sent_base)
def test_gulp(T1, alice_wallet): pool = _deployBPool(alice_wallet) #bind T1 to the pool, with a balance of 2.0 T1.approve(pool.address, toBase18(50.0), from_wallet=alice_wallet) pool.bind(T1.address, toBase18(2.0), toBase18(50.0), from_wallet=alice_wallet) #T1 is now pool's (a) ERC20 balance (b) _records[token].balance assert T1.balanceOf_base(pool.address) == toBase18(2.0) #ERC20 balance assert pool.getBalance_base(T1.address) == toBase18(2.0) #records[] #but then some joker accidentally sends 5.0 tokens to the pool's address # rather than binding / rebinding. So it's in ERC20 bal but not records[] T1.transfer(pool.address, toBase18(5.0), from_wallet=alice_wallet) assert T1.balanceOf_base(pool.address) == toBase18(2.0 + 5.0) #ERC20 bal assert pool.getBalance_base(T1.address) == toBase18(2.0) #records[] #so, 'gulp' gets the pool to absorb the tokens into its balances. # i.e. to update _records[token].balance to be in sync with ERC20 balance pool.gulp(T1.address, from_wallet=alice_wallet) assert T1.balanceOf_base(pool.address) == toBase18(2.0 + 5.0) #ERC20 assert pool.getBalance_base(T1.address) == toBase18(2.0 + 5.0) #records[]
def test_calcSpotPrice_base(T1, T2, alice_address, alice_wallet): pool = _deployBPool(alice_wallet) x = pool.calcSpotPrice_base(tokenBalanceIn_base=toBase18(10.0), tokenWeightIn_base=toBase18(1.0), tokenBalanceOut_base=toBase18(11.0), tokenWeightOut_base=toBase18(1.0), swapFee_base=0) assert round(fromBase18(x), 3) == 0.909
def _createDatatoken(self,dt_name:str,mint_amt:float)-> datatoken.Datatoken: """Create datatoken contract and mint DTs to self.""" wallet = self._wallet._web3wallet DT_address = dtfactory.DTFactory().createToken( '', dt_name, dt_name, toBase18(mint_amt), from_wallet=wallet) DT = datatoken.Datatoken(DT_address) DT.mint(wallet.address, toBase18(mint_amt), from_wallet=wallet) return DT
def test_calcInGivenOut_base(alice_wallet): pool = _deployBPool(alice_wallet) x = pool.calcInGivenOut_base(tokenBalanceIn_base=toBase18(10.0), tokenWeightIn_base=toBase18(1.0), tokenBalanceOut_base=toBase18(10.1), tokenWeightOut_base=toBase18(1.0), tokenAmountOut_base=toBase18(1.0), swapFee_base=0) assert round(fromBase18(x), 3) == 1.099
def _createDT(web3_w: web3wallet.Web3Wallet): DT_address = dtfactory.DTFactory().createToken('foo', 'DT1', 'DT1', toBase18(_DT_INIT), from_wallet=web3_w) DT = datatoken.Datatoken(DT_address) DT.mint(web3_w.address, toBase18(_DT_INIT), from_wallet=web3_w) return DT
def test_calcPoolInGivenSingleOut_base(alice_wallet): pool = _deployBPool(alice_wallet) x = pool.calcPoolInGivenSingleOut(tokenBalanceOut_base=toBase18(1000.0), tokenWeightOut_base=toBase18(5.0), poolSupply_base=toBase18(100.0), totalWeight_base=toBase18(10.0), tokenAmountOut_base=toBase18(0.1), swapFee_base=0) assert round(fromBase18(x), 3) == 0.005
def test_calcSingleOutGivenPoolIn_base(alice_wallet): pool = _deployBPool(alice_wallet) x = pool.calcSingleOutGivenPoolIn_base(tokenBalanceOut_base=toBase18(10.0), tokenWeightOut_base=toBase18(1.0), poolSupply_base=toBase18(120.0), totalWeight_base=toBase18(2.0), poolAmountIn_base=toBase18(10.0), swapFee_base=0) assert round(fromBase18(x), 3) == 1.597
def test_setSwapFee_fails(alice_wallet, alice_address, bob_wallet, bob_address): factory = bfactory.BFactory() pool_address = factory.newBPool(alice_wallet) pool = bpool.BPool(pool_address) with pytest.raises(Exception): pool.setSwapFee(toBase18(0.011), from_wallet=bob_wallet) #not ok, bob isn't controller pool.setController(bob_address, from_wallet=alice_wallet) pool.setSwapFee(toBase18(0.011), from_wallet=bob_wallet) #ok now
def stakeOCEAN(self, OCEAN_stake: float, pool: bpool.BPool): OCEAN = globaltokens.OCEANtoken() OCEAN.approve(pool.address, toBase18(OCEAN_stake), from_wallet=self._web3wallet) pool.joinswapExternAmountIn( tokenIn_address=globaltokens.OCEAN_address(), tokenAmountIn_base=toBase18(OCEAN_stake), minPoolAmountOut_base=toBase18(0.0), from_wallet=self._web3wallet) self.resetCachedInfo()
def test_exitswapExternAmountOut(T1, T2, alice_address, alice_wallet): pool = _createPoolWith2Tokens(T1, T2, alice_wallet, 90.0, 10.0, 9.0, 1.0) BPT = pool pool.finalize(from_wallet=alice_wallet) assert fromBase18(T1.balanceOf_base(alice_address)) == 910.0 pool.exitswapExternAmountOut( tokenOut_address=T1.address, tokenAmountOut_base=toBase18(2.0), #T1 wanted maxPoolAmountIn_base=toBase18(10.0), #max BPT spent from_wallet=alice_wallet) assert fromBase18(T1.balanceOf_base(alice_address)) == (910.0 + 2.0) assert fromBase18(BPT.balanceOf_base(alice_address)) >= (100.0 - 10.0)
def test_joinswapPoolAmountOut(T1, T2, alice_address, alice_wallet): pool = _createPoolWith2Tokens(T1, T2, alice_wallet, 90.0, 10.0, 9.0, 1.0) BPT = pool pool.finalize(from_wallet=alice_wallet) T1.approve(pool.address, toBase18(90.0), from_wallet=alice_wallet) assert fromBase18(T1.balanceOf_base(alice_address)) == 910.0 pool.joinswapPoolAmountOut( tokenIn_address=T1.address, poolAmountOut_base=toBase18(10.0), #BPT wanted maxAmountIn_base=toBase18(90.0), #max T1 to spend from_wallet=alice_wallet) assert fromBase18(T1.balanceOf_base(alice_address)) >= (910.0 - 90.0) assert fromBase18(BPT.balanceOf_base(alice_address)) == (100.0 + 10.0)
def _candPoolAgents(self, state): """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 = self.OCEAN() all_pool_agents = state.agents.filterToPool().values() cand_pool_agents = [] for pool_agent in all_pool_agents: pool = pool_agent.pool DT_address = pool_agent.datatoken_address pool_DT_balance_base = pool.getBalance_base(DT_address) pool_OCEAN_balance_base = pool.getBalance_base(OCEAN_address) pool_DT_weight_base = pool.getDenormalizedWeight_base(DT_address) pool_OCEAN_weight_base = pool.getDenormalizedWeight_base( OCEAN_address) pool_swapFee_base = pool.getSwapFee_base() DT_amount_out_base = toBase18(1.0) spotPriceBefore_base = pool.getSpotPrice_base( tokenIn_address=OCEAN_address, tokenOut_address=DT_address) OCEANamountIn_base = pool.calcInGivenOut_base( tokenBalanceIn_base=pool_OCEAN_balance_base, tokenWeightIn_base=pool_OCEAN_weight_base, tokenBalanceOut_base=pool_DT_balance_base, tokenWeightOut_base=pool_DT_weight_base, tokenAmountOut_base=DT_amount_out_base, swapFee_base=pool_swapFee_base) if OCEANamountIn_base < OCEAN: cand_pool_agents.append(pool) return cand_pool_agents
def _buy(self, state): """Buy, and consume dataset""" OCEAN_address = globaltokens.OCEAN_address() OCEAN = self.OCEAN() OCEAN_base = self._wallet._OCEAN_base() DT_amount_out_base = toBase18(1.0) cand_pool_agents = self._candPoolAgents() assert cand_pool_agents random.shuffle(cand_pool_agents) #FIXME: there will be times when slippage is sufficiently high that # the data consumer won't be able to successfully buy the DT. # In that case, should pick the second choice in cand_pool_agents. Etc. pool_agent = cand_pool_agents[0] pool = pool_agent.pool DT_address = pool_agent.datatoken_address pool.swapExactAmountOut(tokenIn_address=OCEAN_address, maxAmountIn_base=OCEAN_base, tokenOut_address=DT_address, tokenAmountOut_base=DT_amount_out_base, maxPrice_base=constants.HUGEINT, from_wallet=self._wallet._web3wallet) self._wallet.resetCachedInfo()
def transferOCEAN(self, dst_wallet, amt: float) -> None: assert isinstance(dst_wallet, AgentWallet) or \ isinstance(dst_wallet, 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 (%s) exceeds OCEAN holdings (%s)" % (fromBase18(amt_base), fromBase18(OCEAN_base))) globaltokens.OCEANtoken().transfer(dst_address, amt_base, self._web3wallet) dst_wallet._total_OCEAN_in += amt self.resetCachedInfo() dst_wallet.resetCachedInfo()
def test_rebind_more_tokens(T1, T2, alice_wallet): pool = _createPoolWith2Tokens(T1, T2, alice_wallet, 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_wallet=alice_wallet) #sufficient allowance T1.approve(pool.address, toBase18(30.0), from_wallet=alice_wallet) pool.rebind(T1.address, toBase18(120.0), toBase18(9.0), from_wallet=alice_wallet)
def test_finalize(T1, T2, alice_address, alice_wallet): pool = _createPoolWith2Tokens(T1, T2, alice_wallet, 90.0, 10.0, 9.0, 1.0) assert not pool.isPublicSwap() assert not pool.isFinalized() assert pool.totalSupply_base() == 0 assert pool.balanceOf_base(alice_address) == 0 assert pool.allowance_base(alice_address, pool.address) == 0 pool.finalize(from_wallet=alice_wallet) assert pool.isPublicSwap() assert pool.isFinalized() assert pool.totalSupply_base() == toBase18(100.0) assert pool.balanceOf_base(alice_address) == toBase18(100.0) assert pool.allowance_base(alice_address, pool.address) == 0 assert pool.getFinalTokens() == [T1.address, T2.address] assert pool.getCurrentTokens() == [T1.address, T2.address]
def __init__(self, USD: float = 0.0, OCEAN: float = 0.0, private_key=None): if private_key is None: self._web3wallet = web3wallet.randomWeb3Wallet() else: self._web3wallet = web3wallet.Web3Wallet(private_key) #Give the new wallet ETH to pay gas fees (but don't track otherwise) self._web3wallet.fundFromAbove(toBase18(0.01)) #magic number #USD self._USD = USD #lump in ETH too #OCEAN globaltokens.mintOCEAN(address=self._web3wallet.address, value_base=toBase18(OCEAN)) self._cached_OCEAN_base: typing.Union[int, None] = None #for speed #amount self._total_USD_in: float = USD self._total_OCEAN_in: float = OCEAN
def test_OCEAN(): wallet = web3wallet.randomWeb3Wallet() OCEAN_base = web3util.toBase18(3.0) globaltokens.mintOCEAN(address=wallet.address, value_base=OCEAN_base) OCEAN_token = globaltokens.OCEANtoken() assert isinstance(OCEAN_token, datatoken.Datatoken) assert OCEAN_token.symbol() == 'OCEAN' assert OCEAN_token.balanceOf_base(wallet.address) == OCEAN_base globaltokens.mintOCEAN(address=wallet.address, value_base=OCEAN_base) assert OCEAN_token.balanceOf_base(wallet.address) == OCEAN_base * 2
def create_pool(self, datatoken: Datatoken, _DT_STAKE: float, _OCEAN_STAKE: float, POOL_WEIGHT_DT: float, POOL_WEIGHT_OCEAN: float): OCEAN = OCEANtoken() dt_stake: int = web3util.toBase18(_DT_STAKE) ocean_stake: int = web3util.toBase18(_OCEAN_STAKE) pool_weight_dt: int = web3util.toBase18(POOL_WEIGHT_DT) pool_weight_ocean: int = web3util.toBase18(POOL_WEIGHT_OCEAN) pool_address = BFactory().newBPool(from_wallet=self.web3wallet) pool = BPool(pool_address) datatoken.approve(pool.address, web3util.toBase18(dt_stake), from_wallet=self.web3wallet) OCEAN.approve(pool.address, web3util.toBase18(_OCEAN_STAKE), from_wallet=self.web3wallet) pool.bind( token_address=datatoken.address, balance_base=dt_stake, weight_base=pool_weight_dt, from_wallet=self.web3wallet) pool.bind( token_address=OCEAN.address, balance_base=ocean_stake, weight_base=pool_weight_ocean, from_wallet=self.web3wallet) pool.finalize(from_wallet=self.web3wallet) return pool
def test_ERC20(alice_wallet, alice_address, bob_wallet, bob_address): f = dtfactory.DTFactory() dt_address = f.createToken('foo', 'DT1', 'DT1', web3util.toBase18(100.0), from_wallet=alice_wallet) dt = datatoken.Datatoken(dt_address) dt.mint(alice_address, web3util.toBase18(100.0), from_wallet=alice_wallet) #functionality inherited from btoken assert dt.address == dt_address assert dt.symbol() == 'DT1' assert dt.decimals() == 18 assert dt.balanceOf_base(alice_address) == web3util.toBase18(100.0) dt.transfer(bob_address, web3util.toBase18(50.0), from_wallet=alice_wallet) assert dt.allowance_base(alice_address, bob_address) == 0 dt.approve(bob_address, int(1e18), from_wallet=alice_wallet) assert dt.allowance_base(alice_address, bob_address) == int(1e18) #functionality for just datatoken assert dt.blob() == 'foo' assert 'mint' in dir(dt) assert 'setMinter' in dir(dt)
def test_joinSwapExternAmountIn(T1, T2, alice_wallet, alice_address): pool = _createPoolWith2Tokens(T1, T2, alice_wallet, 90.0, 10.0, 9.0, 1.0) T1.approve(pool.address, toBase18(100.0), from_wallet=alice_wallet) #pool's not public with pytest.raises(Exception): pool.swapExactAmountOut(tokenIn_address=T1.address, maxAmountIn_base=toBase18(100.0), tokenOut_address=T2.address, tokenAmountOut_base=toBase18(10.0), maxPrice_base=HUGEINT, from_wallet=alice_wallet) #pool's public pool.setPublicSwap(True, from_wallet=alice_wallet) pool.swapExactAmountOut(tokenIn_address=T1.address, maxAmountIn_base=toBase18(100.0), tokenOut_address=T2.address, tokenAmountOut_base=toBase18(1.0), maxPrice_base=HUGEINT, from_wallet=alice_wallet) assert 908.94 <= fromBase18(T1.balanceOf_base(alice_address)) <= 908.95 assert fromBase18(T2.balanceOf_base(alice_address)) == (1000.0 - 9.0)
def _deployAndMintToken(symbol: str, to_address: str) -> datatoken.Datatoken: network = web3util.get_network() private_key = web3util.confFileValue(network, 'TEST_PRIVATE_KEY1') from_wallet = Web3Wallet(private_key=private_key) factory = dtfactory.DTFactory() amount_base = web3util.toBase18(1000.0) dt_address = factory.createToken('', symbol, symbol, amount_base, from_wallet) dt = datatoken.Datatoken(dt_address) dt.mint(account=to_address, value_base=amount_base, from_wallet=from_wallet) return dt
def create_datatoken(self, blob: str, name: str, symbol: str, cap_base: float, use_cache: bool=False): if self._CACHED_DT and use_cache: return self._CACHED_DT cap_base = web3util.toBase18(cap_base) datatoken_address = DTFactory().createToken(blob, name, symbol, cap_base, self.web3wallet) dt = Datatoken(datatoken_address) dt.mint(account=self.web3wallet.address, value_base=cap_base, from_wallet=self.web3wallet) self._CACHED_DT = dt self.datatokens_created[symbol] = dt return dt
def test_notokens_basic(OCEAN_address, alice_wallet, alice_address): pool = _deployBPool(alice_wallet) 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_base() == toBase18(1e-6) assert pool.getController() == alice_address assert str(pool) with pytest.raises(Exception): pool.finalize() #can't finalize if no tokens
def _createPoolWith2Tokens(T1: btoken.BToken, T2: btoken.BToken, wallet: web3wallet.Web3Wallet, bal1: float, bal2: float, w1: float, w2: float): pool = _deployBPool(wallet) T1.approve(pool.address, toBase18(bal1), from_wallet=wallet) T2.approve(pool.address, toBase18(bal2), from_wallet=wallet) pool.bind(T1.address, toBase18(bal1), toBase18(w1), from_wallet=wallet) pool.bind(T2.address, toBase18(bal2), toBase18(w2), from_wallet=wallet) return pool
def _createPool(DT:datatoken.Datatoken, web3_w:web3wallet.Web3Wallet): OCEAN = globaltokens.OCEANtoken() #Create OCEAN-DT pool p_address = bfactory.BFactory().newBPool(from_wallet=web3_w) pool = bpool.BPool(p_address) DT.approve(pool.address, toBase18(_DT_STAKE), from_wallet=web3_w) OCEAN.approve(pool.address, toBase18(_OCEAN_STAKE),from_wallet=web3_w) pool.bind(DT.address, toBase18(_DT_STAKE), toBase18(POOL_WEIGHT_DT), from_wallet=web3_w) pool.bind(OCEAN.address, toBase18(_OCEAN_STAKE), toBase18(POOL_WEIGHT_OCEAN), from_wallet=web3_w) pool.finalize(from_wallet=web3_w) return pool
def _createPoolAgent(self, state) -> PoolAgent: assert self.OCEAN() > 0.0, "should not call if no OCEAN" wallet = self._wallet._web3wallet OCEAN = globaltokens.OCEANtoken() #name pool_i = len(state.agents.filterToPool()) dt_name = f'DT{pool_i}' pool_agent_name = f'pool{pool_i}' #new DT DT = self._createDatatoken(dt_name, mint_amt=1000.0) #magic number #new pool pool_address = bfactory.BFactory().newBPool(from_wallet=wallet) pool = bpool.BPool(pool_address) #bind tokens & add initial liquidity OCEAN_bind_amt = self.OCEAN() #magic number: use all the OCEAN DT_bind_amt = 20.0 #magic number DT.approve(pool.address, toBase18(DT_bind_amt), from_wallet=wallet) OCEAN.approve(pool.address, toBase18(OCEAN_bind_amt), from_wallet=wallet) pool.bind(DT.address, toBase18(DT_bind_amt), toBase18(POOL_WEIGHT_DT), from_wallet=wallet) pool.bind(OCEAN.address, toBase18(OCEAN_bind_amt), toBase18(POOL_WEIGHT_OCEAN), from_wallet=wallet) pool.finalize(from_wallet=wallet) #create agent pool_agent = PoolAgent(pool_agent_name, pool) state.addAgent(pool_agent) self._wallet.resetCachedInfo() return pool_agent