summaryrefslogtreecommitdiffhomepage
path: root/vyper.html.markdown
blob: bad0c31edd98695a8829bb3f7bb9797338487743 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
---
language: Vyper
filename: learnVyper.vy
contributors:
  - ["Kenny Peluso", "kennyp.herokuapp.com"]
---

> The content of this document is largely inspired by ["Learn Solidity in Y Minutes"](https:#github.com/adambard/learnxinyminutes-docs/blob/master/solidity.html.markdown)

Vyper lets you program on [Ethereum](https:#www.ethereum.org/), a
blockchain-based virtual machine that allows the creation and
execution of smart contracts, without requiring centralized or trusted parties. It was
designed to improve upon Solidity, another smart contract language for Ethereum, by
limiting unsafe practices and enhancing readability; Vyper seeks to optimize the
security and auditability of smart contracts.

Vyper is an experimental, statically typed, contract programming language meant to
resemble Python. Like objects in OOP, each contract contains state variables, functions,
and common data types. Contract-specific features include event notifiers for listeners,
and custom global variables, global constants.

Some Ethereum contract examples include crowdfunding, voting, and blind auctions.

---

## Table of Contents

- Intro
- Example
1. Data types and associated methods
2. Data structures
3. Simple operators
4. Global variables of note
5. Functions and more
    a. functions
    b. events
6. Branching and loops
7. Objects/contracts
    a. calling external contracts
    b. ERC20 built-in
    c. following an interface
8. Other keywords
    a. selfdestruct
9. Contract design notes
    a. obfuscation
    b. storage optimization
    c. data access in blockchain
    d. cron job
    e. observer pattern
10. Security
11. Style notes
12. Natspec comments
- Other documents

---

## Intro

From [the docs](https://media.readthedocs.org/pdf/vyper/latest/vyper.pdf)
the foundational tenants of Vyper are:

1. *Security*
2. *Language and compiler simplicity*
3. *Auditability*

This allows for the following features:

1. *Bounds and overflow checking*
   - On the arithmetic and array level
   - There are no dynamic arrays in Vyper
2. *Support for signed integers and decimal fixed point numbers*
3. *Decidability* - You can always compute precise upper bound on gas cost
4. *Strong typing* - for built-in and custom types
5. *Small and understandable compiler code*
6. *Limited support for pure functions*
    - Anything marked `@constant` is not allowed to change the state

Following the principles and goals, Vyper does not provide the following features:

1. *Modifiers* (defining parts of functions elsewhere)
2. *Class inheritance*
3. *Inline assembly*
4. *Function overloading*
5. *Operator overloading*
6. *Recursive calling*
7. *Infinite-length loops*
8. *Binary fixed point* (decimal fixed point is used for its exactness)

WITH THE RAPID CHANGES IN ETHEREUM, THIS DOCUMENT IS UNLIKELY TO STAY UP TO
DATE, SO YOU SHOULD FOLLOW THE LATEST VYPER DOCS AND ETHEREUM BLOG FOR THE LATEST.
ALL CODE HERE IS PROVIDED AS IS, WITH SUBSTANTIAL RISK OF ERRORS OR DEPRECATED CODE
PATTERNS.

This document primarily discusses syntax, and so excludes many
popular design patterns.

As Vyper and Ethereum are under active development, experimental or beta
features are typically marked, and subject to change. Pull requests welcome.

This document describes Vyper version `0.1.0-beta.8`.

*All of the following code exists for educational purposes only!*
*None of the following code should be used in production as-is!*

## Example

```python
# First, a simple todo list contract
# Implements CRUD operations for tasks

# todo.vy (note .vy extension)
### **** START EXAMPLE **** ###

# Start with Natspec comment
# used for documentation

# @title SimpleBank v1
# @author kennyp
# @notice This is a simple bank.

# Vyper contracts must obey a particular order:
#   struct -> interface -> events -> globals and constants -> functions
# Additionally, like Python, Vyper functions must be defined in the file
#    before they're called.

# Structs

struct Task:
    done: bool
    deleted: bool
    task: string[100]
    metadata: bytes32

# Interfaces

contract AnotherContract():
    def fetch() -> bytes32: constant
    def inform(_taskId: uint256, _status: uint256) -> bool: modifying

# Events

# Events - publicize actions to external listeners
# `indexed` means that it's easier to search/filter on this field
TaskStatus: event({_taskId: indexed(uint256), _status: uint256})

# Global Variables

# State variables are values which are permanently stored in contract storage
# State vars consist of any value persisting beyond any function's scope
#   and are permanently stored in contract storage

# You can define your own, custom, unmutable constants
CREATED: constant(uint256) = 0
COMPLETED: constant(uint256) = 1
DELETED: constant(uint256) = 2

# The `public` built-in allows for this address to be read externally
#   without defining a `get()` constant function
owner: public(address)
other: public(address)

# uint256 means "unsigned positive integer between 0 and 2^256 - 1"
# Overflow protection is built-in to Vyper
taskCount: uint256
tasks: map(uint256, Task) # dictionary: key=uint256, value: Task struct

# Private Functions

# Start each function with Pythonic decorators
# These decorators resemble Natspec but are actually enforced by Vyper's compiler
# These decorators are:
# @public XOR @private (either one or the other)
#   @public (if any contract/user can call it)
#   @private (if only internal functions can call it)
# @payable (if the function is payable i.e. accepting ETH)
# @constant (if the function is not modifying anything on-chain)
@private
def _changeTaskStatus( \
        _sender: address, \
        _taskId: uint256, \
        _status: uint256, \
    ):
    # backslashes (\) allow for multi-line code
    # Natspec comments are particularly helpful for documentation and readability
    # Natspec can be included using familiar Pythonic docstring syntax
    """
    @notice
    @dev `_sender` MUST be `self.owner`
    @param _sender Who is triggering this function
    @param _task The description of the task (only useful when task added)
    """
    # NOTE: Private functions do not have access to `msg.sender`
    # SIDE NOTE: `msg.sender` refers to whoever immediately called the function of
    #   immediate scope. In other words, if I call a function that calls another
    #   in-contract, public function, then `msg.sender` turns from my address to
    #   the address of the current contract.
    assert _sender == self.owner # failed assertions cause calls/transactions to fail
    # Note that unlike Solidity, `self.` is required to query the contract's state
    # Control flow is Pythonic, as is much of Vyper:
    _task: string[100] # initialized to default value
    _data: bytes32 = sha3(convert(_sender, bytes32)) # owner is obfuscated (but still visible in logs)
    if _status == CREATED: # control flow mimics python
        # How a new struct is instantiated:
        self.tasks[_taskId] = Task({ \
            done: False, deleted: False, task: _task, metadata: _data \
        })
    elif _status == COMPLETED:
        # Modifying an existing struct:
        self.tasks[_taskId].done = True
    elif _status == DELETED:
        self.tasks[_taskId].deleted = True
    AnotherContract(self.other).inform(_taskId, _status) # modifying external call
    log.TaskStatus(_taskId, _status) # emit an event

# Public Functions

# Pythonic constructor - can receive none or many arguments
@public
def __init__(_owner: address, _other_contract: address):
    """
    @dev Called once and only upon contract depoyment
    """
    self.owner = _owner
    self.other = _other_contract

# NOTE: Pythonic whitespace rules are mandated in Vyper

@public
def addTask(_task: string[100]) -> uint256:
    """
    @notice Adds a task to contract
    @param _task Description of task
    @return Id of newly minted task
    """
    # msg.sender gives the address of who/what contract is calling this function
    self._changeTaskStatus(msg.sender, self.taskCount, CREATED)
    self.tasks[self.taskCount].task = _task
    self.taskCount += 1
    return self.taskCount - 1

@public
def addSpecialTask(_task: string[100]) -> uint256:
    """
    @notice Adds a task with metadata pulled from elsewhere
    @param _task Description of task
    @return Id of newly minted task
    """
    self._changeTaskStatus(msg.sender, self.taskCount, CREATED)
    self.tasks[self.taskCount].task = _task
    self.tasks[self.taskCount].metadata = AnotherContract(self.other).fetch()
    self.taskCount += 1
    return self.taskCount - 1

@public
def completeTask(_taskId: uint256):
    """
    @notice Marks a task as "completed"
    @param _taskId Id of task to complete
    """
    self._changeTaskStatus(msg.sender, _taskId, COMPLETED)

@public
def deleteTask(_taskId: uint256):
    """
    @notice Adds a task to contract
    @param _taskId Id of task to delete
    """
    self._changeTaskStatus(msg.sender, _taskId, DELETED)

@public
@constant # allows function to run locally/off blockchain
def getTask(_taskId: uint256) -> string[100]:
    """
    @notice Getter for a task's description
    @param _taskId Id of task with desired description
    @return Description of task
    """
    return self.tasks[_taskId].task

### **** END EXAMPLE **** ###


# Now, the basics of Vyper


# ---


# 1. DATA TYPES AND ASSOCIATED METHODS
# uint256 used for currency amount and for dates (in unix time)
x: uint256

# int of 128 bits, cannot be changed after contract deployment
# with 'constant', compiler replaces each occurrence with actual value
a: constant(int128) = 5

# All state variables (those outside a function)
#   are by default 'internal' and accessible inside contract
# Need to explicitly set to 'public' to allow external contracts to access
#   A getter is automatically created, but NOT a setter
# Can only be called in the contract's scope (not within functions)
# Add 'public' field to indicate publicly/externally accessible
a: public(int128)

# No random functions built in, use other contracts for randomness

# Type casting is limited but exists
b: int128 = 5
x: uint256 = convert(b, uint256)

# Types of accounts:
# Contract Account: f(creator_addr, num_transactions)=address set on contract creation
# External Account: (person/external entity): f(public_key)=address

# Addresses - An address type can hold an Ethereum address which
#   equates to 20 bytes or 160 bits. It returns in hexadecimal notation
#   with a leading 0x. No arithmetic allowed
owner: public(address)

# Members can be invoked on all addresses:
owner.balance # returns balance of address as `wei_value`
owner.codesize # returns code size of address as `int128`
owner.is_contract # `True` if Contract Account

# All addresses can be sent ether via `send()` built-in
@public
@payable
def sendWei(any_addr: address):
    send(any_addr, msg.value)

# Bytes available
a: bytes[2]
b: bytes[32]
c: bytes32
# `b` and `c` are 2 different types

# Bytes are preferable to strings since Vyper currently offers better
#   support for bytes i.e. more built-ins to deal with `bytes32`, `bytes32`
#   can be returned from functions and strings[] can't be, UTF8 (string encoding)
#   uses more storage, etc.

# There are no dynamically sized bytes, similar to how there are no
#   dynamic arrays

# Fixed-size byte arrays (Strings)
a: string[100]
b: string[8]
c: string[108] = concat(a, b) # check the latest docs for more built-ins

# Time
t1: timedelta
t2: timestamp
# Both types are built-in "custom type" variants of `uint256`
# `timedelta` values can be added but not `timestamp` values

# Money
m: wei_value
# Also has the base type `uint256` like `timestamp` and `timedelta`
# 1 unit of WEI (a small amount of ETH i.e. ether)

# Custom types
# specify units used in the contract:
units: {
    cm: "centimeter",
    km: "kilometer"
}
# usage:
a: int128(cm)
b: uint256(km)

# BY DEFAULT: all values are set to 0 on instantiation

# `clear()` can be called on most types
#   Does NOT destroy value, but sets value to 0, the initial value


# ---


# 2. DATA STRUCTURES
# Arrays
bytes32[5] nicknames; # static array
bytes32[] names; # dynamic array
uint newLength = names.push("John"); # adding returns new length of the array
# Length
names.length; # get length
names.length = 1; # lengths can be set (for dynamic arrays in storage only)

# Multidimensional Arrays
# At initialization, array dimensions must be hard-coded or constants
# Initialize a 10-column by 3-row, multidimensional fixed array
ls: (uint256[10])[3] # parentheses are optional
@public
def setToThree():
    # Multidimensional Array Access and Write
    # access indices are reversed
    # set element in row 2 (3rd row) column 5 (6th column) to 3
    self.ls[2][5] = 3

# Dictionaries (any simple type to any other type including structs)
theMap: map(uint256, bytes32)
theMap[5] = sha3("charles")
# theMap[255] result is 0, all non-set key values return zeroes
# To make read public, make a getter that accesses the mapping
@public
def getMap(_idx: uint256) -> bytes32:
    """
    @notice Get the value of `theMap` at `_idx`
    """
    return self.theMap[_idx]

self.getMap(5) # returns sha3("charles") in bytes32

# Nested mappings
aMap: map(address, map(address, uint256))
# NOTE: Mappings are only allowed as state variables
# NOTE: Mappings are not iterable; can only be accessed

# To delete (reset the mapping's value to default at a key)
clear(balances["John"])
clear(balances); # sets all elements to 0

# Unlike other languages, CANNOT iterate through all elements in
#   mapping, without knowing source keys - can build data structure
#   on top to do this

# Structs
struct Struct:
    owner: address
    _balance: uint256 # balance is a reserved keyword, is a member for addresses

exampleStruct: Struct

@public
def foo() -> uint256:
    self.exampleStruct = Struct({owner: msg.sender, _balance: 5})
    self.exampleStruct._balance = 10
    self.exampleStruct._balance = 5 # set to new value
    clear(self.exampleStruct._balance)
    clear(self.exampleStruct)
    return self.exampleStruct._balance


# Data locations: Memory vs. storage vs. calldata - all complex types (arrays,
# structs) have a data location
# 'memory' does not persist, 'storage' does
# Default is 'storage' for local and state variables; 'memory' for func params
# stack holds small local variables

# for most types, can explicitly set which data location to use


# ---


# 3. SIMPLE OPERATORS
# Comparisons, bit operators and arithmetic operators are provided
# exponentiation: **
# modulo: %
# maximum: max(x, y)
# AND: bitwise_and(x, y)
# bitwise shift: shift(x, _shift)
#   where x,y are uint256
#         _shift is int128

# 4. GLOBAL VARIABLES OF NOTE
# ** self **
self # address of contract
# often used at end of contract life to transfer remaining balance to party:
self.balance # balance of current contract
self.someFunction() # calls func externally via call, not via internal jump

# ** msg - Current message received by the contract **
# Ethereum programmers take NOTE: this `msg` object is smaller than elsewhere
msg.sender # address of sender
msg.value # amount of ether provided to this contract in wei, the function should be marked `@payable`
msg.gas # remaining gas

# ** tx - This transaction **
# Ethereum programmers take NOTE: this `tx` object is smaller than elsewhere
tx.origin # address of sender of the transaction

# ** block - Information about current block **
block.timestamp # time at current block (uses Unix time)
# Note that `block.timestamp` can be manipulated by miners, so be careful
block.number # current block number
block.difficulty # current block difficulty

# ** storage - Persistent storage hash **
storage['abc'] = 'def'; # maps 256 bit words to 256 bit words


# ---


# 5. FUNCTIONS AND MORE

# A. FUNCTIONS
# Simple function
function increment(uint x) returns (uint) {
    x += 1;
    return x;
}

# Functions can return many arguments
@public
@constant
def increment(x: uint256, y: uint256) -> (uint256, uint256):
    x += 1
    y += 1
    return  (x, y)

# Call previous function
@public
@constant
def willCall() -> (uint256, uint256):
    return  self.increment(1,1)

# One should never have to call a function / hold any logic outside
#   outside the scope of a function in Vyper

# '@constant'
# indicates that function does not/cannot change persistent vars
# Constant function execute locally, not on blockchain
y: uint256
@public
@constant
def increment(x: uint256) -> uint256:
    x += 1
    y += 1 # this line would fail
    # y is a state variable => can't be changed in a constant function


# 'Function Decorators'
# Used like python decorators but are REQUIRED by Vyper
# @public - visible externally and internally (default for function)
# @private - only visible in the current contract
# @constant - doesn't change state
# @payable - receive ether/ETH
# @nonrentant(<unique_key>) - Function can only be called once, both externally
#                             and internally. Used to prevent reentrancy attacks

# Functions hare not hoisted
# Functions cannot be assigned to a variable
# Functions cannot be recursive

# All functions that receive ether must be marked 'payable'
@public
@payable
def depositEther():
    self.balances[msg.sender] += msg.value


# B. EVENTS
# Events are notify external parties; easy to search and
#   access events from outside blockchain (with lightweight clients)
#   typically declare after contract parameters

# Declare
LogSent: event({_from: indexed(address), address: indexed(_to), _amount: uint256})
# Call
log.LogSent(from, to, amount)

/**
For an external party (a contract or external entity), to watch using
the Web3 Javascript library:

# The following is Javascript code, not Vyper code
Coin.LogSent().watch({}, '', function(error, result) {
    if (!error) {
        console.log("Coin transfer: " + result.args.amount +
            " coins were sent from " + result.args.from +
            " to " + result.args.to + ".");
        console.log("Balances now:\n" +
            "Sender: " + Coin.balances.call(result.args.from) +
            "Receiver: " + Coin.balances.call(result.args.to));
    }
}
**/

# Common paradigm for one contract to depend on another (e.g., a
#   contract that depends on current exchange rate provided by another)


# ---


# 6. BRANCHING AND LOOPS

# All basic logic blocks from Python work - including if/elif/else, for,
#   while, break, continue, return - but no switch

# Syntax same as Python, but no type conversion from non-boolean
# to boolean (comparison operators must be used to get the boolean val)

# REMEMBER: Vyper does not allow resursive calls or infinite loops


# ---


# 7. OBJECTS/CONTRACTS
# REMEMBER: Vyper does not allow for inheritance or imports

# A. CALLING EXTERNAL CONTRACTS
# You must define an interface to an external contract in the current contract

contract InfoFeed():
    def getInfo() -> uint256: constant

info: uint256

@public
def __init__(_source: address):
    self.info = InfoFeed(_source).getInfo()


# B. ERC20 BUILT-IN
# Using the `ERC20` keyword implies that the contract at the address
#   follows the ERC20 token standard, allowing you to safely call
#   functions like `transfer()`, etc.

tokenAddress: address(ERC20)

@public
def transferIt(_to: address, _amt: uint256(wei)):
    self.tokenAddress.transfer(_to, _amt)


# C. FOLLOWING AN INTERFACE
# Vyper is experimenting with using the following syntax at the top of
#   a `.vy` file to specify what interfaces are followed by the contract
# This allows interfaces to be better organized, registered, and recognized

import interfaces.some_interface as SomeInterface
implements: SomeInterface
# <rest of contract>


# ---


# 8. OTHER KEYWORDS

# A. selfdestruct()
# selfdestruct current contract, sending funds to address (often creator)
selfdestruct(SOME_ADDRESS);

# removes storage/code from current/future blocks
# helps thin clients, but previous data persists in blockchain

# Common pattern, lets owner end the contract and receive remaining funds
@public
def endItAll() {
    assert msg.sender == self.creator # Only let the contract creator do this
    selfdestruct(self.creator) # Makes contract inactive, returns funds

# May want to deactivate contract manually, rather than selfdestruct
#   (ether sent to selfdestructed contract is lost)


# B. sha3()
# Encrypts strings and other data
# Very important on the blockchain
# Takes 1 argument, `concat()` can be called beforehand
# All strings passed are concatenated before hash action
sha3(concat("ab", "cd")) # returns bytes32


# ---


# 9. CONTRACT DESIGN NOTES

# A. Obfuscation
# All variables are publicly viewable on blockchain, so anything
#   that is private needs to be obfuscated (e.g., hashed w/secret)
# Oftentimes, a "commit-reveal" scheme is employed

# Step 1. Commit
# Place a commitment by sending output of `sha3()`
sha3("a secret"); # bytes32 commit
sha3(concat("secret", "other secret", "salt")); # commit multiple things
# The `sha3()` calculation should occur off-chain, only the bytes32
#   output should be inputted into some `commit()` function
commits: map(address, bytes32)
@public
def commit(commitment: bytes32):
    self.commits[msg.sender] = commitment

# Step 2. Reveal
# Send your previously committed data so the contract can check
#   if your commitment was honest
@public
def reveal(_secret: string[100], _salt: string[100]) -> bool:
    return sha3(concat(_secret, _salt)) == self.commits[msg.sender]


# B. Storage optimization
# Writing to blockchain can be expensive, as data stored forever; encourages
#   smart ways to use memory (eventually, compilation will be better, but for now
#   benefits to planning data structures - and storing min amount in blockchain)

# Cost can often be high for items like multidimensional arrays
#   (cost is for storing data - not declaring unfilled variables)


# C. Data access in blockchain
# Cannot restrict human or computer from reading contents of
#   transaction or transaction's state

# While 'private' prevents other *contracts* from reading data
#   directly - any other party can still read data in blockchain

# All data to start of time is stored in blockchain, so
#   anyone can observe all previous data and changes


# D. Cron Job
# Contracts must be manually called to handle time-based scheduling;
#   can create external code to regularly ping or provide incentives
#   (ether) for others to ping


# E. Observer Pattern
# An Observer Pattern lets you register as a subscriber and
#   register a function which is called by the oracle (note, the oracle
#   pays for this action to be run)
# Some similarities to subscription in Pub/sub

# This is an abstract contract, both client and server classes import,
#   the client should implement

### **** START EXAMPLE **** ###

contract SomeOracleCallback():
    def oracleCallback(_value: uint256, _time: timestamp, _info: bytes32): modifying

MAX_SUBS: constant(uint256) = 100
numSubs: public(uint256) # number of subscribers
subs: map(uint256, address) # enumerates subscribers

@public
def addSub(_sub: address) -> uint256:
    """
    @notice Add subscriber
    @param _sub Address to add
    @return Id of newly added subscriber
    """
    self.subs[self.numSubs] = _sub
    self.numSubs += 1
    return self.numSubs - 1

@private
def notify(_value: uint256, _time: timestamp, _info: bytes32) -> bool:
    """
    @notice Notify all subscribers
    @dev Check `numSubs` first; Watch out for gas costs!
    @param _value whatever
    @param _time what have you
    @param _info what else
    @return True upon successful completion
    """
    j: uint256
    for i in range(MAX_SUBS):
        j = convert(i, uint256) # `i` is int128 by default
        if j == self.numSubs:
            return True
        SomeOracleCallback(self.subs[j]).oracleCallback(_value, _time, _info)

@public
def doSomething():
    """
    @notice Do something and notify subscribers
    """
    # ...something...
    whatever: uint256 = 6
    what_have_you: timestamp
    what_else: bytes32 = sha3("6")
    self.notify(whatever, what_have_you, what_else)

# Now, your client contract can addSubscriber by importing SomeOracleCallback
# and registering with Some Oracle

### **** END EXAMPLE **** ###


# ---


# 10. SECURITY
# Bugs can be disastrous in Ethereum contracts - and even popular patterns in
#   Vyper may be found to be antipatterns

# See security links at the end of this doc


# ---


# 11. STYLE NOTES
# Based on Python's PEP8 style guide
# Full Style guide: http:#solidity.readthedocs.io/en/develop/style-guide.html

# Quick summary:
# 4 spaces for indentation
# Two lines separate contract declarations (and other top level declarations)
# Avoid extraneous spaces in parentheses
# Can omit curly braces for one line statement (if, for, etc)
# else should be placed on own line

# Specific to Vyper:
# arguments: snake_case
# events, interfaces, structs: PascalCase
# public functions: camelCase
# private functions: _prefaceWithUnderscore


# ---


# 12. NATSPEC COMMENTS
# used for documentation, commenting, and external UIs

# Contract natspec - always above contract definition
# @title Contract title
# @author Author name

# Function natspec
# Should include in docstring of functions in typical Pythonic fashion
# @notice Information about what function does; shown when function to execute
# @dev Function documentation for developer

# Function parameter/return value natspec
# @param someParam Some description of what the param does
# @return Description of the return value

```

## Additional resources
- [Installation](https://vyper.readthedocs.io/en/latest/installing-vyper.html)
- [Vyper Docs](https://media.readthedocs.org/pdf/vyper/latest/vyper.pdf)
- [Vyper GitHub (under active dev)](https://github.com/ethereum/vyper)
- [Tools and Resources](https://github.com/ethereum/vyper/wiki/Vyper-tools-and-resources)
- [Online Compiler](https://vyper.online/)

## Sample contracts
- [Uniswap](https://github.com/Uniswap/contracts-vyper)
- [Generalized Governance](https://github.com/kpeluso/gdg)
- [Dynamic Arrays](https://github.com/kpeluso/vyper-dynamic-array)

## Security
Vyper is secure by design, but it may be helpful to understand what Vyper is
protecting you from.
- [Thinking About Smart Contract Security](https:#blog.ethereum.org/2016/06/19/thinking-smart-contract-security/)
- [Smart Contract Security](https:#blog.ethereum.org/2016/06/10/smart-contract-security/)
- [Hacking Distributed Blog](http:#hackingdistributed.com/)

## Style
- [Vyper Style Guide WIP](https://github.com/ethereum/vyper/issues/905)
  - Heavily derived from [Solidity's style guide](http:#solidity.readthedocs.io/en/latest/style-guide.html) ...
  - ... which, in turn, is heavily derived from Python's [PEP 8](https:#www.python.org/dev/peps/pep-0008/) style guide.

## Editors
- [Vyper for VS Code (alpha)](https://github.com/p-/vscode-vyper)

## Future To Dos
- Update to current Vyper release
- List of common design patterns

*Feel free to send a pull request with any edits - or email* `pelusoken -/at-/ gmail`