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

feat: initial 3 exercises #5

Closed
wants to merge 12 commits into from
22 changes: 22 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Lint and Test

on:
pull_request:

jobs:
all:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- name: Use Node.js 18.8.x
uses: actions/setup-node@v3
with:
# use node 18.8.x until Agoric/agoric-sdk#8636
node-version: "18.8.x"
- name: yarn install
run: yarn
- name: yarn lint
run: yarn lint
- name: yarn test
run: yarn test
24 changes: 24 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
Empty file added _agstate/.keep
Empty file.
16 changes: 16 additions & 0 deletions _agstate/agoric-servers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "agoric-basics-agservers",
"version": "0.0.1",
"description": "Agoric server instances for dapp-agoric-basics",
"private": true,
"type": "module",
"scripts": {
"build": "exit 0",
"test": "exit 0",
"lint": "exit 0",
"lint-check": "exit 0",
"lint-fix": "exit 0"
},
"author": "Agoric",
"license": "Apache-2.0"
}
1 change: 1 addition & 0 deletions api/README-no-api.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This dapp has no API.
5 changes: 5 additions & 0 deletions api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"$note": "@agoric/[email protected] expects an api/package.json",
"name": "agoric-basics-api",
"version": "0.1.0"
}
13 changes: 13 additions & 0 deletions contract/jsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This file can contain .js-specific Typescript compiler config.
{
"compilerOptions": {
"target": "esnext",
"module": "ES2022",

"noEmit": true,
"downlevelIteration": true,
"strictNullChecks": true,
"moduleResolution": "node"
},
"include": ["src/**/*.js", "test/**/*.js", "exported.js", "globals.d.ts"]
}
78 changes: 78 additions & 0 deletions contract/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"name": "agoric-basics-contract",
"version": "0.1.0",
"private": true,
"description": "Agoric Basics Contract",
"type": "module",
"scripts": {
"test": "ava --verbose",
"lint": "eslint '**/*.js'",
"lint:fix": "eslint --fix '**/*.js'"
},
"devDependencies": {
"@agoric/deploy-script-support": "^0.10.4-u12.0",
"@agoric/eslint-config": "dev",
"@endo/bundle-source": "^2.8.0",
"@endo/eslint-plugin": "^0.5.2",
"@endo/init": "^0.5.60",
"@endo/promise-kit": "0.2.56",
"@endo/ses-ava": "^0.2.44",
"@jessie.js/eslint-plugin": "^0.4.0",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",
"agoric": "^0.21.2-u12.0",
"ava": "^5.3.0",
"eslint": "^8.47.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-jessie": "^0.0.6",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-ava": "^14.0.0",
"eslint-plugin-github": "^4.10.0",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-jsdoc": "^46.4.3",
"eslint-plugin-prettier": "^5.0.0",
"import-meta-resolve": "^2.2.1",
"prettier": "^3.0.3",
"prettier-plugin-jsdoc": "^1.0.0",
"type-coverage": "^2.26.3",
"typescript": "~5.2.2"
},
"dependencies": {
"@agoric/ertp": "^0.16.3-u12.0",
"@agoric/zoe": "^0.26.3-u12.0",
"@endo/far": "^0.2.22",
"@endo/marshal": "^0.8.9",
"@endo/patterns": "^0.2.5"
},
"ava": {
"files": [
"test/**/test-*.js"
],
"timeout": "10m"
},
"keywords": [],
"repository": {
"type": "git",
"url": "git+https://github.com/Agoric/dapp-agoric-basics"
},
"author": "Agoric",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/Agoric/dapp-agoric-basics/issues"
},
"homepage": "https://github.com/Agoric/dapp-agoric-basics#readme",
"eslintConfig": {
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2021
},
"extends": [
"@agoric"
]
},
"prettier": {
"trailingComma": "all",
"arrowParens": "avoid",
"singleQuote": true
}
}
59 changes: 59 additions & 0 deletions contract/src/agoric-basics-contract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// @ts-check
import { AmountMath, AmountShape, AssetKind } from '@agoric/ertp';
import { Far } from '@endo/far';
import '@agoric/zoe/exported.js';
import { M } from '@endo/patterns';
import { atomicRearrange } from '@agoric/zoe/src/contractSupport/atomicTransfer.js';

const { Fail, quote: q } = assert;

/**
* @param {ZCF<{joinPrice: Amount}>} zcf
*/
export const start = async zcf => {
const gameSeat = zcf.makeEmptySeatKit().zcfSeat;
// TODO: convert to COPY_BAG
const ticketMint = await zcf.makeZCFMint('Ticket', AssetKind.NAT);

const joinShape = harden({
give: { Price: AmountShape },
want: { Tickets: AmountShape },
exit: M.any(),
});

/** @param {ZCFSeat} playerSeat */
const joinHandler = playerSeat => {
const { give, want } = playerSeat.getProposal();

// TODO: update the checks here once AssetKind is converted to COPY_BAG
AmountMath.isGTE(
AmountMath.make(ticketMint.getIssuerRecord().brand, 1n),
want.Tickets,
) || Fail`${q(want.Tickets)} exceeds 1n ticket`;

// mintGains mints all amounts in `want`
// For more, see reference:
// https://docs.agoric.com/reference/zoe-api/zcfmint.html#azcfmint-mintgains-gains-zcfseat
const tmp = ticketMint.mintGains(want);
// atomicRearrange rearrange the assets between seats
atomicRearrange(
zcf,
harden([
[playerSeat, gameSeat, give],
// TODO: add another TransferPart below to pass the exercise 3 test
[tmp, playerSeat, want],
]),
);

playerSeat.exit(true);
return 'welcome to the game';
};

const publicFacet = Far('API', {
makeJoinInvitation: () =>
zcf.makeInvitation(joinHandler, 'join', undefined, joinShape),
});

return { publicFacet };
};
harden(start);
101 changes: 101 additions & 0 deletions contract/test/test-agoric-basics-contract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* @file Tests for agoric-basics-contract.js
*/

// @ts-check

// eslint-disable-next-line import/no-unresolved -- https://github.com/avajs/ava/issues/2951
import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import bundleSource from '@endo/bundle-source';
import { makeZoeKitForTest } from '@agoric/zoe/tools/setup-zoe.js';
import { E, passStyleOf } from '@endo/far';
import { createRequire } from 'module';
import { AmountMath, makeIssuerKit } from '@agoric/ertp';

test('exercise 1: bundleSource bundles the contract for use with zoe', async t => {
const myRequire = createRequire(import.meta.url);
const contractPath = myRequire.resolve(`../src/agoric-basics-contract.js`);

// bundleSource() bundles contract and all of its modules into a single artifact
// and is the first thing you need to do in order to deploy a contract
// For more, see reference: https://docs.agoric.com/guides/zoe/#bundling-a-contract
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// For more, see reference: https://docs.agoric.com/guides/zoe/#bundling-a-contract
// For more discussion, see: https://docs.agoric.com/guides/zoe/#bundling-a-contract

reference doc for bundleSource is more like https://endojs.github.io/endo/modules/_endo_bundle_source.html


// TODO: initialize the const `bundle` using bundleSource() to pass the test below
// Tips 1: you can import bundleSource() from @endo/bundle-source
// Tips 2: don't forget to use `await` to get the fulfilled value
const bundle = await bundleSource(contractPath);

t.is(bundle.moduleFormat, 'endoZipBase64');
t.true(bundle.endoZipBase64.length > 10_000);
});

test('exercise 2: start zoe instance', async t => {
const myRequire = createRequire(import.meta.url);
const contractPath = myRequire.resolve(`../src/agoric-basics-contract.js`);
const bundle = await bundleSource(contractPath);
const { zoeService: zoe } = makeZoeKitForTest();
const installation = await E(zoe).install(bundle);
t.is(passStyleOf(installation), 'remotable');

const playMoney = makeIssuerKit('PlayMoney');
const playMoneyIssuer = { Price: playMoney.issuer };
const joinPrice = AmountMath.make(playMoney.brand, 5n);

// startInstance creates an instance of the installed smart contract, with terms speicific to the instance
// similar to how a JavaScript object is an instance of its class
const { instance } = await E(zoe).startInstance(
installation,
playMoneyIssuer,
{
// TODO: initialize the instance with the expected joinPrice to pass the test below
joinPrice,
},
);
const terms = await E(zoe).getTerms(instance);

t.is(terms.joinPrice, joinPrice);
});

test('exercise 3: atomicRearrange', async t => {
const myRequire = createRequire(import.meta.url);
const contractPath = myRequire.resolve(`../src/agoric-basics-contract.js`);
const bundle = await bundleSource(contractPath);
const { zoeService: zoe } = makeZoeKitForTest();
const installation = await E(zoe).install(bundle);

const playMoney = makeIssuerKit('PlayMoney');
const playMoneyIssuer = { Price: playMoney.issuer };

const { instance } = await E(zoe).startInstance(
installation,
playMoneyIssuer,
{
joinPrice: AmountMath.make(playMoney.brand, 5n),
},
);
const publicFacet = await E(zoe).getPublicFacet(instance);
const terms = await E(zoe).getTerms(instance);
const alicePurse = playMoney.issuer.makeEmptyPurse();
const amountOfMoney = AmountMath.make(playMoney.brand, 5n);
const moneyPayment = playMoney.mint.mintPayment(amountOfMoney);
alicePurse.deposit(moneyPayment);

const { issuers, brands, joinPrice } = terms;
const proposal = {
give: { Price: joinPrice },
want: { Tickets: AmountMath.make(brands.Ticket, 1n) },
};

const Price = await E(alicePurse).withdraw(joinPrice);

const toJoin = E(publicFacet).makeJoinInvitation();

const seat = E(zoe).offer(toJoin, proposal, { Price });
const tickets = await E(seat).getPayout('Tickets');
const actual = await E(issuers.Ticket).getAmountOf(tickets);
// How do we complete the trade, i.e deduct the amount from alice's purse and mint her a ticket?
// Hint: use atomicRearrange()
// TODO: update atomicRearrange in agoric-basics-contract.js
t.deepEqual(actual, proposal.want.Tickets);
});
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "agoric-basics",
"version": "0.1.0",
"license": "Apache-2.0",
"private": true,
"useWorkspaces": true,
"workspaces": [
"contract"
],
"scripts": {
"lint": "yarn workspaces run lint",
"test": "yarn workspaces run test"
}
}
Loading
Loading