This workshop includes several activities:
- a scavenger hunt through several AssemblyScript contracts to get you quickly oriented
- a debugging challenge to fix several problems with broken contracts
- a design challenge to create new contracts and related models that satisfy a set of requirements
Prerequisites
If you're already comfortable with TypeScript then reading AssemblyScript should be a breeze. If you're coming from JavaScript, you'll have to get your head around types (since JavaScript doesn't have them) but reading through the samples here should not be too difficult. If you have no programming experience then this workshop will be challenging for you -- find someone to pair with so you can stay motivated and productive.
Orientation
If you're totally new to NEAR you can start here with a high level overview.
NEAR Protocol (aka "NEAR") is a public peer-to-peer key-value database. Public as in open to everyone for read anything and write what you are allowed to. The write permissions are defined by the access keys, so only the owner of the data can give permissions to modify the data they own.
The data manipulation gets defined by stored procedures (smart contracts) in Wasm, which means that those can be implemented in any programming language compilable to Wasm (e.g. Rust, AssemblyScript, Kotlin, C, C++, Nim, Zig, etc).
This workshop focuses on AssemblyScript as one of two currently supported languages for contract development.
We will not be deploying any of these contracts to the network since our focus is on learning AssemblyScript and almost all of the code presented in this workshop is also running on live examples where you will also find the frontend code that relies on these contracts.
- clone this repo locally (or open using Gitpod)
- run
yarnto install dependencies - run
yarn testto run tests - run
yarn buildto build contracts - run
yarn mockto deploy contracts to a local mock virtual machine for testing
See package.json for more detail about these scripts.
You will find the following folder structure in this repository under the assembly folder.
assembly
│
├── A.scavenger-hunt
│ ├── 01.greeting
│ ├── 02.wallet-example
│ ├── 03.counter
│ ├── 04.token-contract
│ ├── 05.guestbook
│ └── 06.chat
│
├── B.debugging-challenge
│ ├── 01.broken-greeting
│ ├── 03.broken-counter
│ └── 05.broken-guestbook
│
└── C.design-challenge
├── 01.PinkyPromise
├── 02.OpenPetition
└── 03.BucketListYou can filter tests using the following syntax
yarn test -f <contract name>
# for example
# yarn test -f greetingYou must specify the contract file and method when attempting to execute the contract in the local mock virtual machine
yarn mock --wasm-file <path to contract .wasm file> --method-name <contract method name>
# for example
# yarn mock --wasm-file ./out/counter.wasm --method-name incrementCounterNote the projects are ordered by increasing complexity so lower numbers roughly implies "easier to understand".
Keep your own notes. Time permitting, we will share and discuss your findings and answer questions at the end of the activity.
Find examples of the following.
Orientation
Note, some of these may only take you a few seconds to complete so don't overthink things. This activity is about massive exposure to several examples of smart contracts written using AssemblyScript for the NEAR platform.
- a contract method that takes no parameters
- a contract method that takes one parameter
- a model used by a contract method
- a failing unit test
- a passing unit test
- unit testing log output from the NEAR Virtual Machine (VM)
- unit testing the instantiation of a model (ie.
new ModelName()) - unit testing a contract method
- unit testing a method on a model
Storing Data
NEAR Protocol stores data in a key-value store called Storage which is also wrapped with a few persistent collections for developer convenience including PersistentVector, PersistentSet, PersistentMap and PersistentDeque. Reading and writing to Storage requires specifying the type of data to store, whether string, number or binary. Any custom data types (ie. custom data models) must be decorated with the @nearBindgen decorator so that the system knows to serialize them for storage.
- an example that includes the
@nearBindgendecorator (used to support serialization of custom data models) - an example that uses
Storageto read and / or write data from blockchain storage - an example that uses
PersistentVectorto store contract data in an array-like data structure - an example that uses
PersistentMapto store contract data in a map-like data structure - an example that uses
PersistentDequeto store contract data in a queue-like data structure - an example that uses
PersistentSetto store contract data in a set-like data structure - an example that uses the
getPrimitive<T>()method on theStorageclass - an example that uses the
getString()method on theStorageclass - an example that uses the
setString()method on theStorageclass
Contract Context
NEAR Protocol accounts are initially created without an associated contract. Each account can have a maximum of 1 contract deployed to its storage. The account maintains a copy of the contract code as well as any state storage consumed by the contract. You can read more about accounts on the NEAR platform here.
- an example of using
context.senderwhich represents the account that signed the current transaction - an example of a unit test where the test explicitly sets the
signer_account_idto controlcontext.sender - an example of using
context.contractNamewhich represents the account on which the contract lives - an example of a unit test where the test explicitly sets the
current_account_idto controlcontext.contractName - an example of using
context.attachedDepositto capture the tokens attached to a contract function call - an example of a unit test where the test explicitly sets the
attached_depositto controlcontext.attachedDeposit
Validation
- an example of using
assert()to guarantee that some value meets the necessary criteria
Debug as many of the following problems as you can. They are ordered by increasing difficulty.
Important Note:
None of the tests were altered, only the main.ts contract file and / or the model.ts model file were changed from the original to create the problems you see in these failing tests or failures to compile the code.
- run
yarn test -f broken-greetingand solve the issues (there are 4 of them)
Reveal hints
- try running this in your console from the root of the project
diff assembly/A.scavenger-hunt/01.greeting/main.ts assembly/B.debugging-challenge/01.broken-greeting/main.ts
- run
yarn test -f broken-counterand solve the issues (there are 5 of them)
Reveal hints
- one error is preventing the code from compiling so none of the other tests are running. solve the compiler error first so you can see the failing tests
- try running this in your console from the root of the project
diff assembly/A.scavenger-hunt/03.counter/main.ts assembly/B.debugging-challenge/03.broken-counter/main.ts
- run
yarn test -f broken-guestbookand solve the issues (there are several of them and many are preventing the code from compiling).
Reveal hints
@nearBindgen is a decorator added to custom models so they can be serialized and stored on chainPersistentVectorrequire a type parameter which will often be the model you are trying to store on chaincontext.senderdiff assembly/A.scavenger-hunt/05.guestbook/main.ts assembly/B.debugging-challenge/05.broken-guestbook/main.tsdiff assembly/A.scavenger-hunt/05.guestbook/model.ts assembly/B.debugging-challenge/05.broken-guestbook/model.tsChoose one of the following projects and write the model(s) and contract(s) that satisfy the following requirements. Include unit tests of course. Test everything locally using yarn mock.
Important Note: The design guidelines below are almost certainly incomplete. They are intended to inspire you to consider the design challenge on your own or with your pair or team. Feel free to run with these ideas and do not be constrained by what you see here.
(inspired by a 2019 hackathon project)
PinkyPromise is a system for recording promises on the blockchain for all to see, forever and ever. A promise is a piece of text that is made from someone to someone (possibly themselves). A promise may eventually be marked as kept or broken by the owner of the to account.
Models
PinkyPromise- Collects a commitment (as string) between two accounts (as strings). Consider whether to use
Storagedirectly (our on-chain key-value store) or one of the persistent collections that wrapsStorageto mimic a Vector, Map, Queue or Set.
- Collects a commitment (as string) between two accounts (as strings). Consider whether to use
Contracts
mainmakePromise(to: string, statement: string)
(inspired by Covid-19)
BucketList is a system that records things we wish we all could do as soon as it's safe to go back outside.
Models
Activityrepresents something we want to dodescriptionasstringcostasu8(let's keep it small since these are frugal times)friendsasPersistentVector<string>of account names of our friends, if we have any
Contracts
mainadd(item: string, friends: string[], cost: u8): boollist(): Activity[]
(inspired by an internal hackathon project)
OpenPetition is a system for managing the creation and support of petitions (ie. Change.org for blockchain). Models
Petition- Collects signatures (
context.sender) in aPersistentVector<string>for anyone that calls the main contract'ssignmethod, passing in the petition identifier. - The Petition model should include Petition metadata like
titleasstringbodyasstringandfundingasu128
- The Petition model should include methods like
sign(): boolsignWithFunds(amount: u128 = 0): bool
- Collects signatures (
Contracts
mainsign(petitionId: string): boolallows thecontext.senderto sign the petitionlist(): Array<string>returns a list of petition identifiersshow(petitionId: string): Petitionreturns the details of a petitioncontract.petitionscould be the collection of petitions stored as aPersistentMap<string, Petition>where the key is petition identifier and the value is the petition instance
Stretch Goals
- Consider how you would structure this project if each petition were its own contract instead of a model on a single contract. What could the benefits of this be?
If you find yourself stuck with any of this, feel free to reach out to us via the following links: