diff --git a/playground/src/examples/index.ts b/playground/src/examples/index.ts
index a7e88d8d..1887acad 100644
--- a/playground/src/examples/index.ts
+++ b/playground/src/examples/index.ts
@@ -118,6 +118,44 @@ function second(pair: Pair) returns (b) {
function main() returns (word) {
return second(Pair(true, 42));
}
+`,
+ },
+ ],
+ },
+ {
+ id: "option",
+ name: "Option",
+ description: "A generic optional value: checked division returns an Option instead of reverting.",
+ entry: "main.sol",
+ files: [
+ {
+ path: "main.sol",
+ content: `import * from std;
+
+enum Option {
+ None,
+ Some(a)
+}
+
+// Division by zero yields Option.None instead of reverting.
+function checkedDiv(a: word, b: word) returns (Option) {
+ if (b == 0) {
+ return Option.None;
+ }
+ return Option.Some(a / b);
+}
+
+function unwrapOr(option: Option, orElse: word) returns (word) {
+ match (option) {
+ case Option.Some(value) { return value; }
+ default { return orElse; }
+ }
+}
+
+function main() returns (word) {
+ // 84 / 2 succeeds with Some(42); 84 / 0 would fall back to 0.
+ return unwrapOr(checkedDiv(84, 2), 0);
+}
`,
},
],
@@ -182,6 +220,96 @@ contract Escrow {
}
}
}
+`,
+ },
+ ],
+ },
+ {
+ id: "mini-nft",
+ name: "Mini NFT",
+ description: "An NFT with typed ownership: tokens either have an owner or do not exist, with no zero-address sentinels.",
+ entry: "main.sol",
+ files: [
+ {
+ path: "main.sol",
+ content: `import * from std;
+import * from std.dispatch;
+import * from std.Generic;
+import * from std.StorageGeneric;
+import {caller} from std.opcodes;
+
+// A token either has an owner or does not exist: unset mapping entries
+// read back as Option.None.
+enum Option {
+ None,
+ Some(a)
+}
+
+function contains(option: Option, value: a) returns (bool) where a: Eq {
+ match (option) {
+ case Option.Some(inner) { return inner == value; }
+ default { return false; }
+ }
+}
+
+// msg.sender: the CALLER opcode lifted from word into address.
+function sender() returns (address) {
+ return address(caller());
+}
+
+contract MiniNFT {
+ nextId : uint256;
+ owners : mapping(uint256 => Option);
+ approvals : mapping(uint256 => Option);
+ balances : mapping(address => uint256);
+
+ constructor() {}
+
+ function mint() public returns (uint256) {
+ let id = nextId;
+ nextId = nextId + uint256(1);
+ let to = sender();
+ owners[id] = Option.Some(to);
+ balances[to] = balances[to] + uint256(1);
+ return id;
+ }
+
+ function ownerOf(id: uint256) public returns (address) {
+ match (owners[id]) {
+ case Option.Some(owner) { return owner; }
+ default {
+ require(false, "no such token");
+ return address(0);
+ }
+ }
+ }
+
+ function approve(to: address, id: uint256) public {
+ require(sender() == ownerOf(id), "not the owner");
+ approvals[id] = Option.Some(to);
+ }
+
+ function transfer(to: address, id: uint256) public {
+ let owner = ownerOf(id);
+ let from = sender();
+ require(from == owner || contains(approvals[id], from), "not authorized");
+ approvals[id] = Option.None;
+ owners[id] = Option.Some(to);
+ balances[owner] = balances[owner] - uint256(1);
+ balances[to] = balances[to] + uint256(1);
+ }
+
+ function balanceOf(who: address) public returns (uint256) {
+ return balances[who];
+ }
+
+ function exists(id: uint256) public returns (bool) {
+ match (owners[id]) {
+ case Option.Some(_) { return true; }
+ default { return false; }
+ }
+ }
+}
`,
},
],