Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New first contract #22

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 4 additions & 136 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,141 +34,9 @@ Identified sources of complexity:
Progress can be followed [on this board](https://github.com/Agoric/Documentation/projects/1?fullscreen=true)


### Tutorial on creating and running the simplest Smart Contract
### Tutorial

This first contract will be a one-player game where the player tries to guess a number. If they guess right, they win, otherwise, they lose. The game can be played any number of times and there is no actual stake. For the purpose of keeping things simple, the number is hardcoded to be `37`
**Warning: The Agoric technology stack is currently being built with lots of moving pieces. As a consequence, the steps in this tutorial may seem unnecessarily complicated at times. It is a problem we are aware of and are working on**\
We will improve the Developer Experience over time and update these tutorials accordingly


#### Set up the environment for the contract

Clone the [cosmic-swingset repository](https://github.com/Agoric/cosmic-swingset):
```sh
git clone git@github.com:Agoric/cosmic-swingset.git
```

Follow the instruction to [build and setup cosmic-swingset](https://github.com/Agoric/cosmic-swingset#build-from-source) (~20 minutes)


#### Write a contract

```sh
# Make sure you're in the cosmic-swingset root directory
echo $PWD
# => /maybe/home/you/code-projects/cosmic-swingset

# (optional) create a git branch
# git branch -c my-contract-tutorial

# Create a directory for the contract
mkdir contracts
# Create the contract file
touch contracts/guess37.js
```

- copy/paste the following code in the file:
```js
function guess37Contract(terms, inviteMaker) {
const seat = harden({
guess(attempt) {
return attempt === 37 ? 'you win' : 'you lose';
},
});

return harden({
playerInvite: inviteMaker.make('player', seat),
});
}

export default guess37Contract.toString();

```

The contract is materialized by the source code of the `start` function. JavaScript makes it possible to retrieve the source code of a function by calling [toString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString) on the function

<small>
TODO a documentation of the contract format
- Explain the contract host
- Explain `terms` and `inviteMaker` arguments
- Explain the [provided top-level variables](https://github.com/Agoric/ERTP/blob/8f42af53581821bb16a9f4e3d62603833354ef8b/core/contractHost.js#L53-L63)
- Explain Jessie
</small>

Change the content of `lib/ag-solo/vats/vat-demo.js` to be the following:

```js
import harden from '@agoric/harden';
import guess37ContractSource from '../../contracts/guess37.js';

function build(E, log) {
let sharedGame;

async function startup(host) {
const guess37Installation = await E(host).install({
start: guess37ContractSource,
});
const { playerInvite } = await E(guess37Installation).spawn();
const game = await E(host).redeem(playerInvite);

sharedGame = game;
}

async function createDemoClientBundle() {
const chainBundle = {
game: sharedGame,
};
return harden(chainBundle);
}

return harden({ startup, createDemoClientBundle });
}

export default function setup(syscall, state, helpers) {
return helpers.makeLiveSlots(
syscall,
state,
E => build(E, helpers.log),
helpers.vatID,
);
}

```


#### Run the contract

[Run "scenario 3"](https://github.com/Agoric/cosmic-swingset#scenario-3--no-testnet-develop-off-chain-demo)

```sh
make scenario3-setup
make scenario3-run-client
```

Open a web browser to http://localhost:8000/

<small>
currently, this is the UI of the Pixel Gallery demo
TODO : provide a proper UI
</small>

In the REPL, try the following commands in the same order:
```
home
home!game!guess(14)
home!game!guess(37)
```

The first `home` command shows what was defined in `lib/ag-solo/vats/vat-demo.js` as `chainBundle`

`home!game!guess(14)` is a losing guess
`home!game!guess(37)` is a winning guess

As you have guessed (pun intended!), `guess` refers to the function with the same name defined in the contract

<small>
TODO : explain infix-bang syntax
</small>

**Congratulations!**\
You've successfully written a smart contract in JavaScript and run it in a solo node

In the next steps, we'll discover how to make **more interesting contracts** and **how to run them in a proper blockchain**
**[The tutorial starts here](./smart-contracts-tutorial/first-contract.md)**
158 changes: 158 additions & 0 deletions smart-contracts-tutorial/first-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Tutorial on creating and running the simplest Smart Contract

This first contract will be a one-player game where the player tries to guess a number. If they guess right, they win, otherwise, they lose. The game can be played any number of times and there is no actual stake. For the purpose of keeping things simple, the number is hardcoded to be `37`

**Warning: The Agoric technology stack is currently being built with lots of moving pieces. As a consequence, the steps to run the first contract in this tutorial may seem unnecessarily complicated at times. It is a problem we are aware of and are working on**\
We will improve the Developer Experience over time and update these tutorials accordingly


## Set up the environment for the contract

Clone the [cosmic-swingset repository](https://github.com/Agoric/cosmic-swingset):
```sh
git clone git@github.com:Agoric/cosmic-swingset.git
```

Follow the instruction to [build and setup cosmic-swingset for scenario 3](https://github.com/Agoric/cosmic-swingset#build-from-source) (~20 minutes)


## Write a contract

```sh
# Make sure you're in the cosmic-swingset root directory
echo $PWD
# => /maybe/home/you/code-projects/cosmic-swingset

# (optional) create a git branch
# git branch -c my-contract-tutorial

# Create the contract file
touch lib/ag-solo/vats/guess37-contract.js
```

**copy/paste the following code in the contract file:**
```js
function guess37Contract(terms, inviteMaker) {
const seat = harden({
guess(attempt) {
return attempt === 37 ? 'you win' : 'you lose';
},
});

return harden({
playerInvite: inviteMaker.make('player', seat),
});
}

export default guess37Contract.toString();
```

(**note**: in a later version of the Agoric stack, importing the contract code to the contract host will be simpler than this manual manipulation of files)

The contract is materialized by the source code of the `start` function. JavaScript makes it possible to retrieve the source code of a function by calling [toString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/toString) on the function

```md
TODO a documentation of the contract format
- Explain the contract host
- Explain `terms` and `inviteMaker` arguments
- Explain the [provided top-level variables](https://github.com/Agoric/ERTP/blob/8f42af53581821bb16a9f4e3d62603833354ef8b/core/contractHost.js#L53-L63)
- Explain Jessie
```

Replace the content of `lib/ag-solo/vats/vat-pixel.js` with the following:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a note that we're doing this to save a bunch of configuration work that isn't relevant to this exercise. Promise to document the proper way to install a new contract later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#24


```js
import harden from '@agoric/harden';
import { makeHandoffService } from '@agoric/ertp/more/handoff/handoff';
import guess37ContractSource from './guess37-contract.js';

function build(E, log) {
let contractHost

async function startup(host) {
contractHost = host
}

async function createPixelBundle() {
return harden({
contractHost,
handoffService: makeHandoffService(),
guess37ContractSource,
});
}

return harden({ startup, createPixelBundle });
}

export default function setup(syscall, state, helpers) {
return helpers.makeLiveSlots(
syscall,
state,
E => build(E, helpers.log),
helpers.vatID,
);
}
```


## Run the contract

[Run "scenario 3"](https://github.com/Agoric/cosmic-swingset#scenario-3--no-testnet-develop-off-chain-demo)

```sh
make scenario3-setup
make scenario3-run-client
```

Open a web browser to http://localhost:8000/

```
currently, this is the UI of the Pixel Gallery demo
TODO : provide a proper UI
```

In the REPL, try the following commands in the same order:
```
home
host = home~.contractHost
installation = host~.install({start: home.guess37ContractSource})
invites = installation~.spawn()
playerInvite = invites~.playerInvite
seat = host~.redeem(playerInvite)

seat~.guess(14)
seat~.guess(37)
```

### Commands breakdown

The first `home` command shows what was defined in `lib/ag-solo/vats/vat-pixel.js` as return value of `createPixelBundle`
It is a presence for an object containing the properties `contractHost`, `handoffService` and `guess37ContractSource`

`host = home.contractHost` retrieves the contractHost presence and stores it in the `host` variable for convenience

`installation = host~.install({start: home.guess37ContractSource})` installs the guess37 contract in the contract host

`invites = installation~.spawn()` runs the contract (here, the `guess37Contract` function) and store the invites bundle returned by the contract

`playerInvite = invites~.playerInvite` extracts the `playerInvite` from the invites bundle

`seat = host~.redeem(playerInvite)` redeems the invitation. This allows to participate to the contract. What is returned is stored in a `seat` variable as if the contract/game was a table and you were retrieving a seat at this table

`seat~.guess(14)` is a losing guess as defined by the `guess37` contract
`seat~.guess(37)` is a winning guess

As you have guessed (pun intended!), `guess` refers to the function with the same name defined in the contract

The contractHost/install/spawn/invites/redeem choregraphy is part of the [ERTP framework](https://github.com/Agoric/ERTP) developed by Agoric. You can read more about it [in the dedicated repo](https://github.com/Agoric/ERTP/blob/master/core/contractHost.chainmail). [More detailled documentation is coming soon](https://github.com/Agoric/Documentation/pull/17)


## Conclusion and next steps

**Congratulations!**\
You've successfully written a smart contract in JavaScript and run it in a solo node

In the next steps, we'll discover how to make someone else play the `guess37` game


`TODO : explain ~. syntax`