Back to Vyper

Company Stock

docs/vyper-by-example/company-stock.rst

0.5.0a16.9 KB
Original Source

.. index:: stock;company, company stock

Company Stock


.. _company_stock:

.. warning::

This is example code for learning purposes. Do not use in production without thorough review and testing.

This contract is just a tad bit more thorough than the ones we've previously encountered. In this example, we are going to look at a comprehensive contract that manages the holdings of all shares of a company. The contract allows for a person to buy, sell and transfer shares of a company as well as allowing for the company to pay a person in ether. The company, upon initialization of the contract, holds all shares of the company at first but can sell them all.

Let's get started.

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :linenos:

.. note:: Throughout this contract, we use a pattern where @external functions return data from @internal functions that have the same name prepended with an underscore. This is because Vyper does not allow calls between external functions within the same contract. The internal function handles the logic, while the external function acts as a getter to allow viewing.

The contract contains a number of methods that modify the contract state as well as a few 'getter' methods to read it. We first declare several events that the contract logs. We then declare our global variables, followed by function definitions.

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 3 :lines: 3-29

We initiate the company variable to be of type address that's public. The totalShares variable is of type uint256, which in this case represents the total available shares of the company. The price variable represents the wei value of a share and holdings is a mapping that maps an address to the number of shares the address owns.

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 31 :lines: 31-42

In the constructor, we set up the contract to check for valid inputs during the initialization of the contract via the two assert statements. If the inputs are valid, the contract variables are set accordingly and the company's address is initialized to hold all shares of the company in the holdings mapping.

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 44 :lines: 44-48

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 149 :lines: 149-153

We will be seeing a few @view decorators in this contract—which is used to decorate methods that simply read the contract state or return a simple calculation on the contract state without modifying it. When called externally (not as part of a transaction), view functions do not cost gas. Since Vyper is a statically typed language, we see an arrow following the definition of the _stockAvailable() method, which simply represents the data type which the function is expected to return. In the method, we simply key into self.holdings with the company's address and check its holdings. Because _stockAvailable() is an internal method, we also include the stockAvailable() method to allow external access.

Now, let's take a look at a method that lets a person buy stock from the company's holding.

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 50 :lines: 50-67

The buyStock() method is a @payable method which takes an amount of ether sent and calculates the buyOrder (the stock value equivalence at the time of call). The number of shares is deducted from the company's holdings and transferred to the sender's in the holdings mapping.

Now that people can buy shares, how do we check someone's holdings?

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 68 :lines: 68-72

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 155 :lines: 155-159

The _getHolding() is another @view method that takes an address and returns its corresponding stock holdings by keying into self.holdings. Again, an external function getHolding() is included to allow access.

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 74 :lines: 74-78

To check the ether balance of the company, we can simply call the getter method cash().

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 80 :lines: 80-97

To sell a stock, we have the sellStock() method which takes a number of stocks a person wishes to sell, and sends the equivalent value in ether to the seller's address. We first assert that the number of stocks the person wishes to sell is a value greater than 0. We also assert to see that the user can only sell as much as the user owns and that the company has enough ether to complete the sale. If all conditions are met, the holdings are deducted from the seller and given to the company. The ethers are then sent to the seller.

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 99 :lines: 99-112

A stockholder can also transfer their stock to another stockholder with the transferStock() method. The method takes a receiver address and the number of shares to send. It first asserts that the amount being sent is greater than 0 and asserts whether the sender has enough stocks to send. If both conditions are satisfied, the transfer is made.

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 114 :lines: 114-126

The company is also allowed to pay out an amount in ether to an address by calling the payBill() method. This method should only be callable by the company and thus first checks whether the method caller's address matches that of the company. Another important condition to check is that the company has enough funds to pay the amount. If both conditions satisfy, the contract sends its ether to an address.

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 129 :lines: 129-133

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 143 :lines: 143-147

We can also check how much the company has raised by multiplying the number of shares the company has sold and the price of each share. Internally, we get this value by calling the _debt() method. Externally it is accessed via debt().

.. literalinclude:: ../../examples/stock/company.vy :language: vyper :lineno-start: 135 :lines: 135-141

Finally, in this worth() method, we can check the worth of a company by subtracting its debt from its ether balance.

This contract has been the most thorough example so far in terms of its functionality and features. Yet despite the thoroughness of such a contract, the logic remained simple. Hopefully, by now, the Vyper language has convinced you of its capabilities and readability in writing smart contracts.