iamonuwa

6 min read - Posted 29 Aug 19

Remix IDE - Your first Vyper smart contract

Remix IDE - Your first Vyper smart contract

The easiest place to start writing smart contracts in Vyper in with the online Remix IDE.

As it's an online IDE, there's no need for installation or development environment setup, you can open the site and get started!

Remix provides tools for debugging, static analysis, and deployment all within the online environment. To use Remix with Vyper, you first need to enable the Vyper plugin from the Plugin Manager tab.

You can find the source code used in this tutorial on GitHub.

Before we get started, a quick reminder of what we are building:

  • A dapp which allows any user to issue a bounty in ETH
  • Any user with an Ethereum account can issue a bounty in ETH along with some requirements
  • Any user can submit a fulfilment of the bounty along with some evidence
  • The bounty issuer can accept a fulfilment which would result in the fulfiller being paid out

Clear the content of the editor, and name the file Bounty.vy.

article image

We first define structs for the contract.

Structs are custom defined types that can group several variables.

struct Bounty:
    issuer: address
    deadline: timestamp
    data: bytes32
    status: uint256
    amount: wei_value

struct Fulfillment:
    accepted: bool
    fulfiller_address: address
    data: bytes32

To test if everything is working, click the Compile button to compile the contract.

article image

If everything is ok, you should see output in the Bytecode, Runetime Bytecode and LLL tabs, this indicates the compilation was successful.

article image

If there is an error, you should see an error message when you open any of the tab links.

article image

Issuing a Bounty

Now that we have the basic skeleton of our smart contract, we can start adding functions. First we tackle allowing a user to issue a bounty.

Declare state variables

Just like in Solidity, Vyper has state variables. State variables are values which are permanently stored in a contract storage.

You can read a full list of Vyper types in the documentation

Next we define events for the contract. Vyper can log events caught during runtime and display it for the user.

BountyIssued: event({_id: int128, _issuer: indexed(address), _amount: wei_value, data: bytes32 })
BountyCancelled: event({ _id: int128, _issuer: indexed(address), _amount: uint256 })
BountyFulfilled: event({ _bountyId: int128, _issuer: indexed(address), _fulfiller: indexed(address), _fulfillmentId: int128, _amount: uint256})
FulfillmentAccepted: event({ _bountyId: int128, _issuer: indexed(address), _fulfiller: indexed(address), _fulfillmentId: int128, _amount: uint256 })

We have four events that have different fields. A client may want to listen to the events for changes on the contract.

Declare constant values which we use to keep track of a bounties state:

CREATED: constant(uint256) = 0
ACCEPTED: constant(uint256) = 1
CANCELLED: constant(uint256) = 2

Define 2 arrays where we store data about each issued bounty and the fulfillment:

bounties: map(int128, Bounty)
fulfillments: map(int128, Fulfillment)

Define indexes for each fulfillment and bounty. We need this to get the current position of the fulfillment and bounty that exists.

nextBountyIndex: int128
nextFulfillmentIndex: int128

Note: If you fail to define the indexes, you'll encounter this error at the end when you attempt test the contract.

Persistent variable undeclared: nextBountyIndex

Define indexes for each fulfillment and bounty. We need this to get the current position of the fulfillment and bounty that exists.

nextBountyIndex: int128
nextFulfillmentIndex: int128
Issue Bounty Function

Now that we have declared our state variables we can add functions to allow users to interact with our smart contract

Add the public decorator to the function so that external users can call it from the contract. In order to send ETH to our contract we need to add the payable keyword to the function. Without this payable keyword the contract rejects all attempts to send ETH to it via this function. Read more about decorators.

@public
@payable
def issueBounty(_data: bytes32, _deadline: timestamp):
    assert msg.value > 0
    assert _deadline > block.timestamp

    bIndex: int128 = self.nextBountyIndex

    self.bounties[bIndex] = Bounty({ issuer: msg.sender, deadline: _deadline, data: _data, status: 0, amount: msg.value })
    self.nextBountyIndex = bIndex + 1

    log.BountyIssued(bIndex, msg.sender, msg.value, _data)

The function issueBounty receives a bytes32 variable called _data and a timestamp _deadline as arguments.

assert msg.value > 0
assert _deadline > block.timestamp

Since Vyper does not support modifiers, we use the assert keyword check to ensure that the every condition is met. The function returns an error if any of the conditions are not met.

bIndex: int128 = self.nextBountyIndex

We define a variable of int128 to hold the current Index position of the bounty. This is necessary because we need to use it to store the new bounty on the bounties list.

The body of our function has two lines:

self.bounties[bIndex] = Bounty({ issuer: msg.sender, deadline: _deadline, data: _data, status: 0, amount: msg.value })
self.nextBountyIndex = bIndex + 1

First we insert a new Bounty into our bounties array using the bIndex, setting the BountyStatus to CREATED.

In Vyper, msg.sender is automatically set as the address of the sender, and msg.value is set to the amount of Wei (1 ETH = 1000000000000000000 Wei).

We set the msg.sender as the issuer and the msg.value as the bounty amount.

log.BountyIssued(bIndex, msg.sender, msg.value, _data)

Finally, we log the event BountyIssued for the user to subscribe to.

Try it yourself

Now that you have seen how to add a function to issue a bounty, try adding the following functions to the Bounties contract:

  • fulfilBounty(uint _bountyId, string _data) This function stores a fulfilment record attached to the given bounty. The msg.sender should be recorded as the fulfiller.
  • acceptFulfilment(uint _bountyId, uint _fulfilmentId) This function accepts the given fulfilment, and if a record of it exists against the given bounty. It should then pay the bounty to the fulfiller.
  • function cancelBounty(uint _bountyId) This function cancels the bounty, if it has not already been accepted, and send the funds back to the issuer

You can find the complete Bounties.vy file here for reference.

Next Steps

If you enjoyed this guide, or have any suggestions or questions, let me know in the comments.

If you found any errors, feel free to update this guide by selecting the 'Update Article' option in the right hand menu, and/or update the code

Created with Sketch.Content is"CC-BY-SA 4.0" licensed
Article On-chain
0 Comments
Related Articles
Truffle: Testing your smart contract

Truffle: Testing your smart contract Earlier in this series, we took a look at how to setup Truffle and use it to compile, deploy and interact with our Bounties.vy smart contract. This article covers the steps required to write tests for our smart contract within the Truffle framework. You can write tests in Truffle projects using Javascript thanks to the Mocha testing framework and Chai for assertions. This article focuses on the Javascript tests. You can find source code for this tutorial on G

Ethereum 101 - Part 5 - The Smart Contract

Quick Overview The term smart contract is an ubiquitous term with varying definitions across the greater blockchain ecosystem. Smart contracts are just computer programs. In the context of Ethereum, smart contracts refer to the source code of EVM-specific, special-purpose programming languages: Solidity, Vyper, LLL, Bamboo, and Serpent. There are good reasons for using built-for-purpose languages to write smart contracts, but they will not be discussed in this documentation. To make an attempt a

Wil Barnes

13 Feb 19