예제 #1
0
파일: dss.py 프로젝트: pcatana/keeper
 def vat(self) -> Address:
     return Address(self._contract.call().vat())
예제 #2
0
파일: dss.py 프로젝트: pcatana/keeper
 def flopper(self) -> Address:
     return Address(self._contract.call().flopper())
예제 #3
0
def gal_address(web3):
    return Address(web3.eth.accounts[3])
예제 #4
0
파일: dss.py 프로젝트: pcatana/keeper
        def __init__(self, lognote: LogNote):
            assert isinstance(lognote, LogNote)

            self.ilk = str(Web3.toText(lognote.arg1)).replace('\x00', '')
            self.urn = Address(Web3.toHex(lognote.arg2)[26:])
            self.collateral_owner = Address(Web3.toHex(lognote.arg3)[26:])
예제 #5
0
# reduce logspew
logging.getLogger('urllib3').setLevel(logging.INFO)
logging.getLogger("web3").setLevel(logging.INFO)
logging.getLogger("asyncio").setLevel(logging.INFO)
logging.getLogger("requests").setLevel(logging.INFO)

endpoint_uri = sys.argv[1]  # ex: https://localhost:8545
web3 = Web3(
    HTTPProvider(endpoint_uri=endpoint_uri, request_kwargs={"timeout": 30}))
if len(sys.argv) > 3:
    web3.eth.defaultAccount = sys.argv[
        2]  # ex: 0x0000000000000000000000000000000aBcdef123
    register_keys(
        web3, [sys.argv[3]]
    )  # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass
    our_address = Address(web3.eth.defaultAccount)
    run_transactions = True
else:
    our_address = Address(sys.argv[2])
    run_transactions = False

mcd = DssDeployment.from_node(web3)
collateral = mcd.collaterals['ETH-A']
ilk = collateral.ilk
if run_transactions:
    collateral.approve(our_address)
past_blocks = 100


class TestApp:
    def __init__(self):
예제 #6
0
 def owner(self) -> Address:
     return Address(self._contract.call().owner())
예제 #7
0
    def get_orders(self, p_token: Token = None,  b_token: Token = None, block_ident: Optional[str] = None) -> List[Order]:
        """Get all active orders.

        If both `p_token` and `b_token` are specified, orders will be filtered by these.
        In case of the _MatchingMarket_ implementation, order enumeration will be much efficient
        if these two parameters are supplied, as then orders can be fetched using `getBestOffer`
        and a series of `getWorseOffer` calls. This approach will result in much lower number of calls
        comparing to the naive 0..get_last_order_id approach, especially if the number of inactive orders
        is very high.

        Either none or both of these parameters have to be specified.

        Args:
            `p_token`: Token object (see `model.py`) of the `pay_token` to filter the orders by.
            `b_token`: Token object (see `model.py`) of the `buy_token` to filter the orders by.
            `block_ident`: Block identifier can either be a str, int or None and will fallback to 'latest' block.
                           Used for historical retreival of orders.

        Returns:
            A list of `Order` objects representing all active orders on Oasis.
        """

        assert((isinstance(p_token, Token) and isinstance(b_token, Token))
               or ((p_token is None) and (b_token is None)))

        assert(isinstance(block_ident, int) or isinstance(block_ident, str) or (block_ident is None))

        block_ident = 'latest' if block_ident is None else block_ident

        if (p_token is None) or (b_token is None):
            pay_token = None
            buy_token = None
        else:
            pay_token = p_token.address
            buy_token = b_token.address

        if pay_token is not None and buy_token is not None:
            orders = []

            if self._support_contract:
                result = self._support_contract.functions.getOffers(self.address.address, pay_token.address, buy_token.address).call(block_identifier=block_ident)

                while True:
                    count = 0
                    for i in range(0, 100):
                        if result[3][i] != '0x0000000000000000000000000000000000000000':
                            count += 1

                            orders.append(Order(market=self,
                                                order_id=result[0][i],
                                                maker=Address(result[3][i]),
                                                pay_token=pay_token,
                                                pay_amount=p_token.normalize_amount(Wad(result[1][i])),
                                                buy_token=buy_token,
                                                buy_amount=b_token.normalize_amount(Wad(result[2][i])),
                                                timestamp=result[4][i]))

                    if count == 100:
                        next_order_id = self._contract.functions.getWorseOffer(orders[-1].order_id).call(block_identifier=block_ident)
                        result = self._support_contract.functions.getOffers(self.address.address, next_order_id).call(block_identifier=block_ident)

                    else:
                        break

            else:
                order_id = self._contract.functions.getBestOffer(pay_token.address, buy_token.address).call(block_identifier=block_ident)
                while order_id != 0:
                    order = self.get_order(order_id, block_ident)
                    if order is not None:
                        orders.append(order)

                    order_id = self._contract.functions.getWorseOffer(order_id).call(block_identifier=block_ident)

            return sorted(orders, key=lambda order: order.order_id)
        else:
            return super(MatchingMarket, self).get_orders(pay_token, buy_token)
예제 #8
0
class ZrxExchangeV2(Contract):
    """A client for the 0x V2 exchange contract.

    You can find the `0x V2` exchange contract here:
    <https://etherscan.io/address/0x4f833a24e1f95d70f028921e27040ca56e09ab0b>.

    Attributes:
        web3: An instance of `Web` from `web3.py`.
        address: Ethereum address of the _0x_ `Exchange` contract.
    """

    abi = Contract._load_abi(__name__, 'abi/ExchangeV2.abi')
    bin = Contract._load_bin(__name__, 'abi/ExchangeV2.bin')

    _ZERO_ADDRESS = Address("0x0000000000000000000000000000000000000000")

    ORDER_INFO_TYPE = '(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)'

    @staticmethod
    def deploy(web3: Web3, zrx_asset: str):
        """Deploy a new instance of the 0x `Exchange` contract.

        Args:
            web3: An instance of `Web` from `web3.py`.
            zrx_token: The address of the ZRX token this exchange will use.

        Returns:
            A `ZrxExchange` class instance.
        """
        return ZrxExchangeV2(web3=web3,
                             address=Contract._deploy(web3, ZrxExchangeV2.abi,
                                                      ZrxExchangeV2.bin, []))

    def __init__(self, web3: Web3, address: Address):
        assert (isinstance(web3, Web3))
        assert (isinstance(address, Address))

        self.web3 = web3
        self.address = address
        self._contract = self._get_contract(web3, self.abi, address)

    def zrx_asset(self) -> str:
        """Get the asset data of the ZRX token contract associated with this `ExchangeV2` contract.

        Returns:
            The asset data of the `ZRX` token.
        """
        return str(bytes_to_hexstring(self._contract.call().ZRX_ASSET_DATA()))

    def zrx_token(self) -> Address:
        """Get the address of the ZRX token contract associated with this `ExchangeV2` contract.

        Returns:
            The address of the `ZRX` token.
        """
        return Address("0x" + self.zrx_asset()[-40:])

    def asset_transfer_proxy(self, proxy_id: str) -> Address:
        """Get the address of the `ERC20Proxy` contract associated with this `Exchange` contract.

        Returns:
            The address of the `ERC20Proxy` token.
        """
        assert (isinstance(proxy_id, str))

        return Address(self._contract.call().getAssetProxy(
            hexstring_to_bytes(proxy_id)))

    def approve(self, tokens: List[ERC20Token], approval_function):
        """Approve the 0x ERC20Proxy contract to fully access balances of specified tokens.

        In case of 0x V2, it's the ERC20Proxy contract that actually gets the approvals,
        not the 0x Exchange contract itself. In addition to the tokens specified as the `tokens`
        parameter, the ZRX token always gets approved as well as without it the 0x Exchange
        contract wouldn't be able to charge maker and taker fees.

        For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager`
        in `pymaker.approval`.

        Args:
            tokens: List of :py:class:`pymaker.token.ERC20Token` class instances.
            approval_function: Approval function (i.e. approval mode).
        """
        assert (isinstance(tokens, list))
        assert (callable(approval_function))

        for token in tokens:  # TODO  + [ERC20Token(web3=self.web3, address=self.zrx_token())]
            approval_function(token, self.asset_transfer_proxy(ERC20Asset.ID),
                              '0x ERC20Proxy contract')

    def past_fill(self,
                  number_of_past_blocks: int,
                  event_filter: dict = None) -> List[LogFill]:
        """Synchronously retrieve past LogFill events.

        `LogFill` events are emitted by the 0x contract every time someone fills an order.

        Args:
            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.
            event_filter: Filter which will be applied to returned events.

        Returns:
            List of past `LogFill` events represented as :py:class:`pymaker.zrx.LogFill` class.
        """
        assert (isinstance(number_of_past_blocks, int))
        assert (isinstance(event_filter, dict) or (event_filter is None))

        return self._past_events(self._contract, 'Fill', LogFill,
                                 number_of_past_blocks, event_filter)

    def past_cancel(self,
                    number_of_past_blocks: int,
                    event_filter: dict = None) -> List[LogCancel]:
        """Synchronously retrieve past LogCancel events.

        `LogCancel` events are emitted by the 0x contract every time someone cancels an order.

        Args:
            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.
            event_filter: Filter which will be applied to returned events.

        Returns:
            List of past `LogCancel` events represented as :py:class:`pymaker.zrx.LogCancel` class.
        """
        assert (isinstance(number_of_past_blocks, int))
        assert (isinstance(event_filter, dict) or (event_filter is None))

        return self._past_events(self._contract, 'Cancel', LogCancel,
                                 number_of_past_blocks, event_filter)

    def create_order(self, pay_asset: Asset, pay_amount: Wad, buy_asset: Asset,
                     buy_amount: Wad, expiration: int) -> Order:
        """Creates a new order.

        The `maker_fee`, `taker_fee` and `fee_recipient` fields are by default set to zero.
        Before signing the order and submitting it to the relayer, they may need to be
        populated using the `calculate_fees()` method of the `ZrxRelayerApi` class.

        Args:
            pay_asset: The asset you want to put on sale.
            pay_amount: Amount of the `pay_asset` token you want to put on sale.
            buy_asset: The asset you want to be paid with.
            buy_amount: Amount of the `buy_asset` you want to receive.
            expiration: Unix timestamp (in seconds) when the order will expire.

        Returns:
            New order as an instance of the :py:class:`pymaker.zrx.Order` class.
        """
        assert (isinstance(pay_asset, Asset))
        assert (isinstance(pay_amount, Wad))
        assert (isinstance(buy_asset, Asset))
        assert (isinstance(buy_amount, Wad))
        assert (isinstance(expiration, int))

        return Order(exchange=self,
                     sender=self._ZERO_ADDRESS,
                     maker=Address(self.web3.eth.defaultAccount),
                     taker=self._ZERO_ADDRESS,
                     maker_fee=Wad(0),
                     taker_fee=Wad(0),
                     pay_asset=pay_asset,
                     pay_amount=pay_amount,
                     buy_asset=buy_asset,
                     buy_amount=buy_amount,
                     salt=self.random_salt(),
                     fee_recipient=self._ZERO_ADDRESS,
                     expiration=expiration,
                     exchange_contract_address=self.address,
                     signature=None)

    def _get_order_info(self, order):
        assert (isinstance(order, Order))

        method_signature = self.web3.sha3(
            text=f"getOrderInfo({self.ORDER_INFO_TYPE})")[0:4]
        method_parameters = encode_single(f"({self.ORDER_INFO_TYPE})",
                                          [self._order_tuple(order)])

        request = bytes_to_hexstring(method_signature + method_parameters)
        response = self.web3.eth.call({
            'to': self.address.address,
            'data': request
        })
        response_decoded = decode_single("((uint8,bytes32,uint256))", response)

        return response_decoded

    def get_order_hash(self, order: Order) -> str:
        """Calculates hash of an order.

        Args:
            order: Order you want to calculate the hash of.

        Returns:
            Order hash as a hex string starting with `0x`.
        """
        assert (isinstance(order, Order))

        # the hash depends on the exchange contract address as well
        assert (order.exchange_contract_address == self.address)

        return bytes_to_hexstring(self._get_order_info(order)[0][1])

    def get_unavailable_buy_amount(self, order: Order) -> Wad:
        """Return the order amount which was either taken or cancelled.

        Args:
            order: Order you want to get the unavailable amount of.

        Returns:
            The unavailable amount of the order (i.e. the amount which was either taken or cancelled),
            expressed in terms of the `buy_token` token.
        """
        assert (isinstance(order, Order))

        order_info = self._get_order_info(order)[0]

        if order_info[0] in [
                0,  # INVALID,                     // Default value
                1,  # INVALID_MAKER_ASSET_AMOUNT,  // Order does not have a valid maker asset amount
                2,  # INVALID_TAKER_ASSET_AMOUNT,  // Order does not have a valid taker asset amount
                4,  # EXPIRED,                     // Order has already expired
                5,  # FULLY_FILLED,                // Order is fully filled
                6
        ]:  # CANCELLED                    // Order has been cancelled
            return order.buy_amount

        else:
            return Wad(order_info[2])

    def sign_order(self, order: Order) -> Order:
        """Signs an order so it can be submitted to the relayer.

        Order will be signed by the `web3.eth.defaultAccount` account.

        Args:
            order: Order you want to sign.

        Returns:
            Signed order. Copy of the order passed as a parameter with the `signature` field filled with signature.
        """
        assert (isinstance(order, Order))

        signature = eth_sign(hexstring_to_bytes(self.get_order_hash(order)),
                             self.web3)
        v, r, s = to_vrs(signature)

        signed_order = copy.copy(order)
        signed_order.signature = bytes_to_hexstring(bytes([v])) + \
                                 bytes_to_hexstring(r)[2:] + \
                                 bytes_to_hexstring(s)[2:] + \
                                 "03"  # EthSign
        return signed_order

    def fill_order(self, order: Order, fill_buy_amount: Wad) -> Transact:
        """Fills an order.

        Args:
            order: The order to be filled.
            fill_buy_amount: The amount (in terms of `buy_token` of the original order) to be filled.

        Returns:
            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.
        """
        assert (isinstance(order, Order))
        assert (isinstance(fill_buy_amount, Wad))

        method_signature = self.web3.sha3(
            text=f"fillOrder({self.ORDER_INFO_TYPE},uint256,bytes)")[0:4]
        method_parameters = encode_single(
            f"({self.ORDER_INFO_TYPE},uint256,bytes)", [
                self._order_tuple(order), fill_buy_amount.value,
                hexstring_to_bytes(order.signature)
            ])

        request = bytes_to_hexstring(method_signature + method_parameters)

        return Transact(self, self.web3, self.abi, self.address,
                        self._contract, None, [request])

    def cancel_order(self, order: Order) -> Transact:
        """Cancels an order.

        Args:
            order: Order you want to cancel.

        Returns:
            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.
        """
        assert (isinstance(order, Order))

        method_signature = self.web3.sha3(
            text=f"cancelOrder({self.ORDER_INFO_TYPE})")[0:4]
        method_parameters = encode_single(f"({self.ORDER_INFO_TYPE})",
                                          [self._order_tuple(order)])

        request = bytes_to_hexstring(method_signature + method_parameters)

        return Transact(self, self.web3, self.abi, self.address,
                        self._contract, None, [request])

    @staticmethod
    def _order_tuple(order):
        return (order.maker.address, order.taker.address,
                order.fee_recipient.address, order.sender.address,
                order.pay_amount.value, order.buy_amount.value,
                order.maker_fee.value, order.taker_fee.value, order.expiration,
                order.salt, hexstring_to_bytes(order.pay_asset.serialize()),
                hexstring_to_bytes(order.buy_asset.serialize()))

    @staticmethod
    def random_salt() -> int:
        return int(time.time() * 1000)

    def __repr__(self):
        return f"ZrxExchangeV2('{self.address}')"
예제 #9
0
    def _create_signature(self, params: str) -> str:
        assert(isinstance(params, str))

        return eth_sign(bytes(params, 'utf-8'), self.web3, self.api_secret, False, Address(self.account_id))
예제 #10
0
    def approve_token(self, token_address: str, amount: int) -> Transact:
        assert(isinstance(token_address, str))
        assert(isinstance(amount, int))

        token_contract = self._get_contract(self.web3, self.token_abi, Address(token_address))
        return Transact(self, self.web3, self.token_abi, Address(token_address), token_contract, "approve",[self.address.address, int(amount)], {}).transact(from_address=self.middle_account)
예제 #11
0
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='oasis-market-maker-keeper')

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument("--rpc-timeout",
                            type=int,
                            default=10,
                            help="JSON-RPC timeout (in seconds, default: 10)")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")

        parser.add_argument("--tub-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the Tub contract")

        parser.add_argument("--oasis-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the OasisDEX contract")

        parser.add_argument("--config",
                            type=str,
                            required=True,
                            help="Buy/sell bands configuration file")

        parser.add_argument(
            "--price-feed",
            type=str,
            help=
            "Source of price feed. Tub price feed will be used if not specified"
        )

        parser.add_argument(
            "--price-feed-expiry",
            type=int,
            default=120,
            help="Maximum age of non-Tub price feed (in seconds, default: 120)"
        )

        parser.add_argument(
            "--round-places",
            type=int,
            default=2,
            help="Number of decimal places to round order prices to (default=2)"
        )

        parser.add_argument(
            "--min-eth-balance",
            type=float,
            default=0,
            help=
            "Minimum ETH balance below which keeper with either terminate or not start at all"
        )

        parser.add_argument("--gas-price",
                            type=int,
                            default=0,
                            help="Gas price (in Wei)")

        parser.add_argument(
            "--gas-price-increase",
            type=int,
            help="Gas price increase (in Wei) if no confirmation within"
            " `--gas-price-increase-every` seconds")

        parser.add_argument(
            "--gas-price-increase-every",
            type=int,
            default=120,
            help="Gas price increase frequency (in seconds, default: 120)")

        parser.add_argument("--gas-price-max",
                            type=int,
                            help="Maximum gas price (in Wei)")

        parser.add_argument("--gas-price-file",
                            type=str,
                            help="Gas price configuration file")

        parser.add_argument(
            "--smart-gas-price",
            dest='smart_gas_price',
            action='store_true',
            help=
            "Use smart gas pricing strategy, based on the ethgasstation.info feed"
        )

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=
                f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}",
                request_kwargs={"timeout": self.arguments.rpc_timeout}))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        self.otc = MatchingMarket(web3=self.web3,
                                  address=Address(
                                      self.arguments.oasis_address))
        self.tub = Tub(web3=self.web3,
                       address=Address(self.arguments.tub_address))
        self.vox = Vox(web3=self.web3, address=self.tub.vox())
        self.sai = ERC20Token(web3=self.web3, address=self.tub.sai())
        self.gem = ERC20Token(web3=self.web3, address=self.tub.gem())

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))
        logging.getLogger('urllib3.connectionpool').setLevel(logging.INFO)
        logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(
            logging.INFO)

        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)
        self.bands_config = ReloadableConfig(self.arguments.config)
        self.gas_price = GasPriceFactory().create_gas_price(self.arguments)
        self.price_feed = PriceFeedFactory().create_price_feed(
            self.arguments.price_feed, self.arguments.price_feed_expiry,
            self.tub, self.vox)
예제 #12
0
class ZrxExchange(Contract):
    """A client for the 0x exchange contract.

    You can find the source code of the `0x` exchange contract here:
    <https://etherscan.io/address/0x12459c951127e0c374ff9105dda097662a027093#code>.

    Attributes:
        web3: An instance of `Web` from `web3.py`.
        address: Ethereum address of the _0x_ `Exchange` contract.
    """

    abi = Contract._load_abi(__name__, 'abi/Exchange.abi')
    bin = Contract._load_bin(__name__, 'abi/Exchange.bin')

    _ZERO_ADDRESS = Address("0x0000000000000000000000000000000000000000")

    @staticmethod
    def deploy(web3: Web3, zrx_token: Address, token_transfer_proxy: Address):
        """Deploy a new instance of the 0x `Exchange` contract.

        Args:
            web3: An instance of `Web` from `web3.py`.
            zrx_token: The address of the ZRX token this exchange will use.
            token_transfer_proxy: The address of the token transfer proxy this exchange will use.

        Returns:
            A `ZrxExchange` class instance.
        """
        return ZrxExchange(web3=web3,
                           address=Contract._deploy(web3, ZrxExchange.abi, ZrxExchange.bin, [
                              zrx_token.address,
                              token_transfer_proxy.address
                          ]))

    def __init__(self, web3: Web3, address: Address):
        assert(isinstance(web3, Web3))
        assert(isinstance(address, Address))

        self.web3 = web3
        self.address = address
        self._contract = self._get_contract(web3, self.abi, address)

    def zrx_token(self) -> Address:
        """Get the address of the ZRX token contract associated with this `Exchange` contract.

        Returns:
            The address of the `ZRX` token.
        """
        return Address(self._contract.call().ZRX_TOKEN_CONTRACT())

    def token_transfer_proxy(self) -> Address:
        """Get the address of the `TokenTransferProxy` contract associated with this `Exchange` contract.

        Returns:
            The address of the `TokenTransferProxy` token.
        """
        return Address(self._contract.call().TOKEN_TRANSFER_PROXY_CONTRACT())

    def approve(self, tokens: List[ERC20Token], approval_function):
        """Approve the 0x Exchange TokenTransferProxy contract to fully access balances of specified tokens.

        In case of 0x, it's the TokenTransferProxy contract that actually gets the approvals,
        not the 0x Exchange contract itself. In addition to the tokens specified as the `tokens`
        parameter, the ZRX token always gets approved as well as without it the 0x Exchange
        contract wouldn't be able to charge maker and taker fees.

        For available approval functions (i.e. approval modes) see `directly` and `via_tx_manager`
        in `pymaker.approval`.

        Args:
            tokens: List of :py:class:`pymaker.token.ERC20Token` class instances.
            approval_function: Approval function (i.e. approval mode).
        """
        assert(isinstance(tokens, list))
        assert(callable(approval_function))

        for token in tokens + [ERC20Token(web3=self.web3, address=self.zrx_token())]:
            approval_function(token, self.token_transfer_proxy(), '0x Exchange contract')

    def on_fill(self, handler, event_filter: dict = None):
        """Subscribe to LogFill events.

        `LogFill` events are emitted by the 0x contract every time someone fills an order.

        Args:
            handler: Function which will be called for each subsequent `LogFill` event.
                This handler will receive a :py:class:`pymaker.`zrx.LogFill` class instance.
            event_filter: Filter which will be applied to event subscription.
        """
        assert(callable(handler))
        assert(isinstance(event_filter, dict) or (event_filter is None))

        self._on_event(self._contract, 'LogFill', LogFill, handler, event_filter)

    def on_cancel(self, handler, event_filter: dict = None):
        """Subscribe to LogCancel events.

        `LogCancel` events are emitted by the 0x contract every time someone cancels an order.

        Args:
            handler: Function which will be called for each subsequent `LogCancel` event.
                This handler will receive a :py:class:`pymaker.`zrx.LogCancel` class instance.
            event_filter: Filter which will be applied to event subscription.
        """
        assert(callable(handler))
        assert(isinstance(event_filter, dict) or (event_filter is None))

        self._on_event(self._contract, 'LogCancel', LogCancel, handler, event_filter)

    def past_fill(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogFill]:
        """Synchronously retrieve past LogFill events.

        `LogFill` events are emitted by the 0x contract every time someone fills an order.

        Args:
            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.
            event_filter: Filter which will be applied to returned events.

        Returns:
            List of past `LogFill` events represented as :py:class:`pymaker.zrx.LogFill` class.
        """
        assert(isinstance(number_of_past_blocks, int))
        assert(isinstance(event_filter, dict) or (event_filter is None))

        return self._past_events(self._contract, 'LogFill', LogFill, number_of_past_blocks, event_filter)

    def past_cancel(self, number_of_past_blocks: int, event_filter: dict = None) -> List[LogCancel]:
        """Synchronously retrieve past LogCancel events.

        `LogCancel` events are emitted by the 0x contract every time someone cancels an order.

        Args:
            number_of_past_blocks: Number of past Ethereum blocks to retrieve the events from.
            event_filter: Filter which will be applied to returned events.

        Returns:
            List of past `LogCancel` events represented as :py:class:`pymaker.zrx.LogCancel` class.
        """
        assert(isinstance(number_of_past_blocks, int))
        assert(isinstance(event_filter, dict) or (event_filter is None))

        return self._past_events(self._contract, 'LogCancel', LogCancel, number_of_past_blocks, event_filter)

    def create_order(self,
                     pay_token: Address,
                     pay_amount: Wad,
                     buy_token: Address,
                     buy_amount: Wad,
                     expiration: int) -> Order:
        """Creates a new order.

        The `maker_fee`, `taker_fee` and `fee_recipient` fields are by default set to zero.
        Before signing the order and submitting it to the relayer, they may need to be
        populated using the `calculate_fees()` method of the `ZrxRelayerApi` class.

        Args:
            pay_token: Address of the ERC20 token you want to put on sale.
            pay_amount: Amount of the `pay_token` token you want to put on sale.
            buy_token: Address of the ERC20 token you want to be paid with.
            buy_amount: Amount of the `buy_token` you want to receive.
            expiration: Unix timestamp (in seconds) when the order will expire.

        Returns:
            New order as an instance of the :py:class:`pymaker.zrx.Order` class.
        """
        assert(isinstance(pay_token, Address))
        assert(isinstance(pay_amount, Wad))
        assert(isinstance(buy_token, Address))
        assert(isinstance(buy_amount, Wad))
        assert(isinstance(expiration, int))

        return Order(exchange=self,
                     maker=Address(self.web3.eth.defaultAccount),
                     taker=self._ZERO_ADDRESS,
                     maker_fee=Wad(0),
                     taker_fee=Wad(0),
                     pay_token=pay_token,
                     pay_amount=pay_amount,
                     buy_token=buy_token,
                     buy_amount=buy_amount,
                     salt=self.random_salt(),
                     fee_recipient=self._ZERO_ADDRESS,
                     expiration=expiration,
                     exchange_contract_address=self.address,
                     ec_signature_r=None,
                     ec_signature_s=None,
                     ec_signature_v=None)

    def get_order_hash(self, order: Order) -> str:
        """Calculates hash of an order.

        Args:
            order: Order you want to calculate the hash of.

        Returns:
            Order hash as a hex string starting with `0x`.
        """
        assert(isinstance(order, Order))

        # the hash depends on the exchange contract address as well
        assert(order.exchange_contract_address == self.address)

        result = self._contract.call().getOrderHash(self._order_addresses(order), self._order_values(order))
        return bytes_to_hexstring(array.array('B', [ord(x) for x in result]).tobytes())

    def get_unavailable_buy_amount(self, order: Order) -> Wad:
        """Return the order amount which was either taken or cancelled.

        Args:
            order: Order you want to get the unavailable amount of.

        Returns:
            The unavailable amount of the order (i.e. the amount which was either taken or cancelled),
            expressed in terms of the `buy_token` token.
        """
        assert(isinstance(order, Order))

        return Wad(self._contract.call().getUnavailableTakerTokenAmount(hexstring_to_bytes(self.get_order_hash(order))))

    def sign_order(self, order: Order) -> Order:
        """Signs an order so it can be submitted to the relayer.

        Order will be signed by the `web3.eth.defaultAccount` account.

        Args:
            order: Order you want to sign.

        Returns:
            Signed order. Copy of the order passed as a parameter with the `ec_signature_r`, `ec_signature_s`
            and `ec_signature_v` fields filled with signature values.
        """
        assert(isinstance(order, Order))

        signature = eth_sign(hexstring_to_bytes(self.get_order_hash(order)), self.web3)
        v, r, s = to_vrs(signature)

        signed_order = copy.copy(order)
        signed_order.ec_signature_r = bytes_to_hexstring(r)
        signed_order.ec_signature_s = bytes_to_hexstring(s)
        signed_order.ec_signature_v = v
        return signed_order

    def fill_order(self, order: Order, fill_buy_amount: Wad) -> Transact:
        """Fills an order.

        Args:
            order: The order to be filled.
            fill_buy_amount: The amount (in terms of `buy_token` of the original order) to be filled.

        Returns:
            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.
        """
        assert(isinstance(order, Order))
        assert(isinstance(fill_buy_amount, Wad))

        return Transact(self, self.web3, self.abi, self.address, self._contract, 'fillOrder',
                        [self._order_addresses(order), self._order_values(order), fill_buy_amount.value,
                         True, order.ec_signature_v,
                         hexstring_to_bytes(order.ec_signature_r),
                         hexstring_to_bytes(order.ec_signature_s)])

    def cancel_order(self, order: Order) -> Transact:
        """Cancels an order.

        Args:
            order: Order you want to cancel.

        Returns:
            A :py:class:`pymaker.Transact` instance, which can be used to trigger the transaction.
        """
        assert(isinstance(order, Order))

        return Transact(self, self.web3, self.abi, self.address, self._contract, 'cancelOrder',
                        [self._order_addresses(order), self._order_values(order), order.buy_amount.value])

    @staticmethod
    def _order_values(order):
        return [order.pay_amount.value,
                order.buy_amount.value,
                order.maker_fee.value,
                order.taker_fee.value,
                order.expiration,
                order.salt]

    @staticmethod
    def _order_addresses(order):
        return [order.maker.address,
                order.taker.address,
                order.pay_token.address,
                order.buy_token.address,
                order.fee_recipient.address]

    @staticmethod
    def random_salt() -> int:
        return random.randint(1, 2**256 - 1)

    def __repr__(self):
        return f"ZrxExchange('{self.address}')"
예제 #13
0
 def setup_method(self):
     self.web3 = Web3(HTTPProvider("http://localhost:8555"))
     self.web3.eth.defaultAccount = self.web3.eth.accounts[0]
     self.our_address = Address(self.web3.eth.defaultAccount)
     self.ds_guard = DSGuard.deploy(self.web3)
예제 #14
0
파일: dss.py 프로젝트: pcatana/keeper
    def flipper(self, ilk: Ilk) -> Address:
        assert isinstance(ilk, Ilk)

        (flip, chop, lump) = self._contract.call().ilks(ilk.toBytes())
        return Address(flip)
예제 #15
0
from web3 import Web3, HTTPProvider

from pymaker import Address
from pymaker.deployment import DssDeployment
from pymaker.keys import register_keys
from pymaker.numeric import Wad

assert os.environ['ETH_RPC_URL']
web3 = Web3(
    HTTPProvider(endpoint_uri=os.environ['ETH_RPC_URL'],
                 request_kwargs={"timeout": 10}))
our_address = None
if len(sys.argv) > 1:
    web3.eth.defaultAccount = sys.argv[
        1]  # ex: 0x0000000000000000000000000000000aBcdef123
    our_address = Address(web3.eth.defaultAccount)
if len(sys.argv) > 2:
    register_keys(
        web3, [sys.argv[2]]
    )  # ex: key_file=~keys/default-account.json,pass_file=~keys/default-account.pass
    run_transactions = False
else:
    run_transactions = False
mcd = DssDeployment.from_node(web3)

# Print a list of collaterals available for this deployment of MCD
for collateral in mcd.collaterals.values():
    print(
        f"Found {collateral.ilk.name:>9} - {collateral.gem.name():<20} with {collateral.adapter.dec():>2} decimals"
    )
예제 #16
0
파일: model.py 프로젝트: w1r2p1/pymaker
 def is_eth(self) -> bool:
     return self.address == Address(
         '0x0000000000000000000000000000000000000000')
    def __init__(self, args: list, **kwargs):
        parser = argparse.ArgumentParser(prog='radarrelay-market-maker-keeper')

        parser.add_argument("--rpc-host",
                            type=str,
                            default="localhost",
                            help="JSON-RPC host (default: `localhost')")

        parser.add_argument("--rpc-port",
                            type=int,
                            default=8545,
                            help="JSON-RPC port (default: `8545')")

        parser.add_argument(
            "--eth-from",
            type=str,
            required=True,
            help="Ethereum account from which to send transactions")

        parser.add_argument("--tub-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the Tub contract")

        parser.add_argument(
            "--exchange-address",
            type=str,
            required=True,
            help="Ethereum address of the 0x Exchange contract")

        parser.add_argument("--weth-address",
                            type=str,
                            required=True,
                            help="Ethereum address of the WETH token")

        parser.add_argument("--relayer-api-server",
                            type=str,
                            required=True,
                            help="Address of the 0x Relayer API")

        parser.add_argument("--config",
                            type=str,
                            required=True,
                            help="Buy/sell bands configuration file")

        parser.add_argument(
            "--price-feed",
            type=str,
            help=
            "Source of price feed. Tub price feed will be used if not specified"
        )

        parser.add_argument(
            "--order-expiry",
            type=int,
            required=True,
            help="Expiration time of created orders (in seconds)")

        parser.add_argument(
            "--order-expiry-threshold",
            type=int,
            default=0,
            help=
            "Order expiration time at which order is considered already expired (in seconds)"
        )

        parser.add_argument(
            "--min-eth-balance",
            type=float,
            default=0,
            help=
            "Minimum ETH balance below which keeper with either terminate or not start at all"
        )

        parser.add_argument(
            '--cancel-on-shutdown',
            dest='cancel_on_shutdown',
            action='store_true',
            help=
            "Whether should cancel all open orders on RadarRelay on keeper shutdown"
        )

        parser.add_argument("--gas-price",
                            type=int,
                            default=0,
                            help="Gas price (in Wei)")

        parser.add_argument("--debug",
                            dest='debug',
                            action='store_true',
                            help="Enable debug output")

        self.arguments = parser.parse_args(args)

        self.web3 = kwargs['web3'] if 'web3' in kwargs else Web3(
            HTTPProvider(
                endpoint_uri=
                f"http://{self.arguments.rpc_host}:{self.arguments.rpc_port}"))
        self.web3.eth.defaultAccount = self.arguments.eth_from
        self.our_address = Address(self.arguments.eth_from)
        self.tub = Tub(web3=self.web3,
                       address=Address(self.arguments.tub_address))
        self.vox = Vox(web3=self.web3, address=self.tub.vox())
        self.sai = ERC20Token(web3=self.web3, address=self.tub.sai())
        self.ether_token = ERC20Token(web3=self.web3,
                                      address=Address(
                                          self.arguments.weth_address))

        logging.basicConfig(
            format='%(asctime)-15s %(levelname)-8s %(message)s',
            level=(logging.DEBUG if self.arguments.debug else logging.INFO))

        self.bands_config = ReloadableConfig(self.arguments.config)
        self.min_eth_balance = Wad.from_number(self.arguments.min_eth_balance)
        self.price_feed = PriceFeedFactory().create_price_feed(
            self.arguments.price_feed, self.tub, self.vox)

        self.radar_relay = ZrxExchange(web3=self.web3,
                                       address=Address(
                                           self.arguments.exchange_address))
        self.radar_relay_api = ZrxRelayerApi(
            exchange=self.radar_relay,
            api_server=self.arguments.relayer_api_server)
예제 #18
0
    def get_pair_address(self, token_a_address: Address, token_b_address: Address) -> Address:
        assert (isinstance(token_a_address, Address))
        assert (isinstance(token_b_address, Address))

        return Address(self._factory_contract.functions.getPair(token_a_address.address, token_b_address.address).call())
예제 #19
0
파일: dss.py 프로젝트: pcatana/keeper
    def fromBytes(urn: bytes):
        assert isinstance(urn, bytes)

        address = Address(Web3.toHex(urn[-20:]))
        return Urn(address)
예제 #20
0
    create_surplus(mcd, flapper, deployment_address)
    joy = mcd.vat.dai(mcd.vow.address)
    assert joy > mcd.vat.sin(mcd.vow.address) + mcd.vow.bump() + mcd.vow.hump()
    assert (mcd.vat.sin(mcd.vow.address) -
            mcd.vow.sin()) - mcd.vow.ash() == Rad(0)
    assert mcd.vow.flap().transact()

    mint_mkr(mcd.mkr, our_address, Wad.from_number(10))
    flapper.approve(mcd.mkr.address, directly(from_address=our_address))
    bid = Wad.from_number(0.001)
    assert mcd.mkr.balance_of(our_address) > bid
    assert flapper.tend(flapper.kicks(), mcd.vow.bump(),
                        bid).transact(from_address=our_address)


nobody = Address("0x0000000000000000000000000000000000000000")


class TestShutdownModule:
    """This test must be run after other MCD tests because it will leave the testchain `cage`d."""
    def test_init(self, mcd, deployment_address, our_address):
        assert mcd.esm is not None
        assert isinstance(mcd.esm, ShutdownModule)
        assert isinstance(mcd.esm.address, Address)
        assert mcd.esm.sum() == Wad(0)
        assert mcd.esm.min() > Wad(0)
        assert mcd.end.live()

        joy = mcd.vat.dai(mcd.vow.address)
        awe = mcd.vat.sin(mcd.vow.address)
        # If `test_shutdown.py` is run in isolation, create a flap auction to exercise `yank`
예제 #21
0
파일: dss.py 프로젝트: pcatana/keeper
 def gem(self) -> DSToken:
     address = Address(self._contract.call().gem())
     return DSToken(self.web3, address)
예제 #22
0
        def from_json(web3: Web3, conf: str):
            conf = json.loads(conf)
            pause = DSPause(web3, Address(conf['MCD_PAUSE']))
            vat = Vat(web3, Address(conf['MCD_VAT']))
            vow = Vow(web3, Address(conf['MCD_VOW']))
            jug = Jug(web3, Address(conf['MCD_JUG']))
            cat = Cat(web3, Address(conf['MCD_CAT']))
            dai = DSToken(web3, Address(conf['MCD_DAI']))
            dai_adapter = DaiJoin(web3, Address(conf['MCD_JOIN_DAI']))
            flapper = Flapper(web3, Address(conf['MCD_FLAP']))
            flopper = Flopper(web3, Address(conf['MCD_FLOP']))
            pot = Pot(web3, Address(conf['MCD_POT']))
            mkr = DSToken(web3, Address(conf['MCD_GOV']))
            spotter = Spotter(web3, Address(conf['MCD_SPOT']))
            ds_chief = DSChief(web3, Address(conf['MCD_ADM']))
            esm = ShutdownModule(web3, Address(conf['MCD_ESM']))
            end = End(web3, Address(conf['MCD_END']))
            proxy_registry = ProxyRegistry(web3,
                                           Address(conf['PROXY_REGISTRY']))
            dss_proxy_actions = DssProxyActionsDsr(
                web3, Address(conf['PROXY_ACTIONS_DSR']))
            cdp_manager = CdpManager(web3, Address(conf['CDP_MANAGER']))

            collaterals = {}
            for name in DssDeployment.Config._infer_collaterals_from_addresses(
                    conf.keys()):
                ilk = Ilk(name[0].replace('_', '-'))
                if name[1] == "ETH":
                    gem = DSEthToken(web3, Address(conf[name[1]]))
                else:
                    gem = DSToken(web3, Address(conf[name[1]]))

                if name[1] in ['USDC', 'WBTC']:
                    adapter = GemJoin5(web3,
                                       Address(conf[f'MCD_JOIN_{name[0]}']))
                else:
                    adapter = GemJoin(web3,
                                      Address(conf[f'MCD_JOIN_{name[0]}']))

                # PIP contract may be a DSValue, OSM, or bogus address.
                pip_address = Address(conf[f'PIP_{name[1]}'])
                network = DssDeployment.NETWORKS.get(web3.net.version,
                                                     "testnet")
                if network == "testnet":
                    pip = DSValue(web3, pip_address)
                else:
                    pip = OSM(web3, pip_address)

                collateral = Collateral(
                    ilk=ilk,
                    gem=gem,
                    adapter=adapter,
                    flipper=Flipper(web3,
                                    Address(conf[f'MCD_FLIP_{name[0]}'])),
                    pip=pip)
                collaterals[ilk.name] = collateral

            return DssDeployment.Config(pause, vat, vow, jug, cat, flapper,
                                        flopper, pot, dai, dai_adapter, mkr,
                                        spotter, ds_chief, esm, end,
                                        proxy_registry, dss_proxy_actions,
                                        cdp_manager, collaterals)
예제 #23
0
def other_address(web3):
    return Address(web3.eth.accounts[2])