Skip to content

Commit a528558

Browse files
authored
feat: add cardano summit token (#137)
* feat: add cardano summit token * feat: updated how to play section
1 parent 5dcc6b4 commit a528558

32 files changed

Lines changed: 2441 additions & 279 deletions

docker/Dockerfile.frontend

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
FROM --platform=linux/amd64 ubuntu:20.04 AS build
22

3-
ARG GODOT_VERSION="4.4"
3+
ARG GODOT_VERSION="4.5.1"
44

55
USER root
66

frontend/src/components/how-to-play/Menu.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ export default function Menu({ className }: MenuProps) {
4545
<ul className={clsx('fixed right-2 top-1/2 -translate-y-1/2 z-1 space-y-4 min-w-[168px] bg-[#0A0A0A]/80 p-2', className)}>
4646
<MenuItem href="#create-ship" isActive={activeHash === '#create-ship'} label="CREATE SHIP" />
4747
<MenuItem href="#move-ship" isActive={activeHash === '#move-ship'} label="MOVE SHIP" />
48-
{/* <MenuItem href="#gather-fuel" isActive={activeHash === '#gather-fuel'} label="GATHER FUEL" />
49-
<MenuItem href="#mine-tokens" isActive={activeHash === '#mine-tokens'} label="MINE TOKENS" />
48+
<MenuItem href="#gather-fuel" isActive={activeHash === '#gather-fuel'} label="GATHER FUEL" />
49+
<MenuItem href="#gather-token" isActive={activeHash === '#gather-token'} label="GATHER TOKEN" />
5050
<MenuItem href="#mine-asteria" isActive={activeHash === '#mine-asteria'} label="MINE ASTERIA" />
51-
<MenuItem href="#quit-game" isActive={activeHash === '#quit-game'} label="QUIT GAME" /> */}
51+
<MenuItem href="#quit-game" isActive={activeHash === '#quit-game'} label="QUIT GAME" />
5252
</ul>
5353
);
5454
}
Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,32 @@
11
import Layout from "../mdx/Layout";
22
export default ({ children }) => <Layout>{children}</Layout>;
33

4-
Creates a `ShipState` UTxO locking min ada and a `ShipToken` (minted in this tx), specifying in the datum the initial `pos_x` and `pos_y` coordinates of the ship, and setting `fuel` to an initial amount. Also adds to the `AsteriaUTxO` value the `SHIP_MINT_FEE` paid by the user.
4+
#### This is the first step in order to play the game.
5+
6+
This transaction creates a `ShipState` UTxO locking min ada, a `ShipToken` (minted in this tx) and an `INITIAL_FUEL` amount of fuel tokens (minted in this tx), specifying in the datum the initial `pos_x` and `pos_y` coordinates of the ship, the ship and pilot token names and the `last_move_latest_time` as the upper bound of the transaction's validity range (posix time). Also adds to the `AsteriaUTxO` value the `SHIP_MINT_LOVELACE_FEE` paid by the user.
7+
8+
#### Rules to take into account:
9+
10+
- the ship number has to be exactly +1 of the last ship minted
11+
- the tip slot must be recent because it's used during validation of the tx
12+
- the start position has to be at a certain distance ([manhattan distance](https://en.wikipedia.org/wiki/Taxicab_geometry)) of coordinates (0,0). For the mainnet challenge the minimum distance is `50`.
13+
14+
#### You can query the next available ship and pilot token names using the following curl:
15+
16+
```
17+
curl --location 'https://8000-ethereal-audience-bb83g6.us1.demeter.run/graphql' \
18+
--header 'Content-Type: application/json' \
19+
--data '{"query":"query { nextShipTokenName(spacetimePolicyId: \"0291ae7aebaf064b785542093c2b13169effb34462301e68d4b44f43\", spacetimeAddress: \"addr1wypfrtn6awhsvjmc24pqj0ptzvtfalang33rq8ng6j6y7scnlkytx\") { shipName pilotName } }","variables":{}}'
20+
```
21+
22+
#### You can query the tip of the chain using the following curl:
23+
24+
```
25+
curl --location 'https://8000-ethereal-audience-bb83g6.us1.demeter.run/graphql' \
26+
--header 'Content-Type: application/json' \
27+
--data '{"query":"query { lastSlot { slot } }","variables":{}}'
28+
```
29+
30+
#### Diagram
531

632
![createShip diagram](/txs/build-ship.png)

frontend/src/components/how-to-play/create-ship/Section.tsx

Lines changed: 139 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -15,93 +15,116 @@ import type { ConnectedWallet } from '@/hooks/useCardano';
1515
import { useChallengeStore } from '@/stores/challenge';
1616

1717
const tx3File = `tx create_ship(
18-
p_pos_x: Int, // Ship Position X
19-
p_pos_y: Int, // Ship Position Y
20-
ship_name: Bytes, // Name of the ship
21-
pilot_name: Bytes, // Name of the pilot
22-
tip_slot: Int, // TODO: remove when tip_slot() implemented
18+
p_pos_x: Int, // Ship Position X
19+
p_pos_y: Int, // Ship Position Y
20+
ship_name: Bytes, // Name of the ship
21+
pilot_name: Bytes, // Name of the pilot
22+
tip_slot: Int, // TODO: remove when tip_slot() implemented
23+
last_move_timestamp: Int,
2324
) {
24-
locals {
25-
initial_fuel: 480, // Should be taken from spaceTime datum
26-
ship_mint_lovelace_fee: 1000000, // Should be taken from asteria script datum
27-
spacetime_policy_hash: 0x0291ae7aebaf064b785542093c2b13169effb34462301e68d4b44f43,
28-
spacetime_policy_ref: 0x3d308c0f3deb1eff764cbb765452c53d30704748681d7acd61c7775aeb8a8e46#1,
29-
asteria_policy_ref: 0x3d308c0f3deb1eff764cbb765452c53d30704748681d7acd61c7775aeb8a8e46#0,
30-
pellet_policy_ref: 0x3d308c0f3deb1eff764cbb765452c53d30704748681d7acd61c7775aeb8a8e46#2,
31-
}
25+
locals {
26+
initial_fuel: 5, // Should be taken from spaceTime datum
27+
ship_mint_lovelace_fee: 1000000, // Should be taken from asteria script datum
28+
spacetime_policy_hash: 0x0291ae7aebaf064b785542093c2b13169effb34462301e68d4b44f43,
29+
spacetime_policy_ref: 0x3d308c0f3deb1eff764cbb765452c53d30704748681d7acd61c7775aeb8a8e46#1,
30+
asteria_policy_ref: 0x3d308c0f3deb1eff764cbb765452c53d30704748681d7acd61c7775aeb8a8e46#0,
31+
pellet_policy_ref: 0x3d308c0f3deb1eff764cbb765452c53d30704748681d7acd61c7775aeb8a8e46#2,
32+
}
3233
33-
validity {
34-
until_slot: tip_slot, // tip_slot() + 300
35-
}
34+
validity {
35+
until_slot: tip_slot, // tip_slot() + 300
36+
}
3637
37-
reference SpacetimeRef {
38-
ref: spacetime_policy_ref,
39-
}
38+
reference SpacetimeRef {
39+
ref: spacetime_policy_ref,
40+
}
4041
41-
reference AsteriaRef {
42-
ref: asteria_policy_ref,
43-
}
42+
reference AsteriaRef {
43+
ref: asteria_policy_ref,
44+
}
4445
45-
reference PelletRef {
46-
ref: pellet_policy_ref,
47-
}
46+
reference PelletRef {
47+
ref: pellet_policy_ref,
48+
}
4849
49-
input source {
50-
from: Player,
51-
min_amount: fees + Ada(ship_mint_lovelace_fee),
52-
}
50+
input* gas {
51+
from: Player,
52+
min_amount: fees + Ada(ship_mint_lovelace_fee) + min_utxo(pilot_token) + min_utxo(new_ship) + min_utxo(gas_change),
53+
}
5354
54-
input asteria {
55-
from: AsteriaPolicy,
56-
min_amount: AdminToken(1),
57-
datum_is: AsteriaDatum,
58-
}
59-
60-
mint {
61-
amount: AnyAsset(spacetime_policy_hash, pilot_name, 1) + AnyAsset(spacetime_policy_hash, ship_name, 1),
62-
redeemer: (),
63-
}
55+
input asteria {
56+
from: AsteriaPolicy,
57+
min_amount: AdminToken(1),
58+
datum_is: AsteriaDatum,
59+
redeemer: AsteriaRedeemer::AddNewShip {},
60+
}
61+
62+
mint {
63+
amount: AnyAsset(spacetime_policy_hash, pilot_name, 1) + AnyAsset(spacetime_policy_hash, ship_name, 1),
64+
redeemer: ShipyardRedeemer::MintShip {},
65+
}
6466
65-
mint {
66-
amount: Fuel(initial_fuel),
67-
redeemer: (),
68-
}
67+
mint {
68+
amount: Fuel(initial_fuel),
69+
redeemer: FuelRedeemer::MintFuel {},
70+
}
6971
70-
output {
71-
to: Player,
72-
amount: source - fees - Ada(ship_mint_lovelace_fee) + AnyAsset(spacetime_policy_hash, pilot_name, 1),
73-
}
72+
output pilot_token {
73+
to: Player,
74+
amount: AnyAsset(spacetime_policy_hash, pilot_name, 1) + min_utxo(pilot_token),
75+
}
7476
75-
output {
76-
to: AsteriaPolicy,
77-
amount: asteria + Ada(ship_mint_lovelace_fee),
78-
datum: AsteriaDatum {
79-
ship_counter: asteria.ship_counter + 1,
80-
shipyard_policy: asteria.shipyard_policy,
81-
},
82-
}
77+
output updated_asteria {
78+
to: AsteriaPolicy,
79+
amount: asteria + Ada(ship_mint_lovelace_fee),
80+
datum: AsteriaDatum {
81+
ship_counter: asteria.ship_counter + 1,
82+
shipyard_policy: asteria.shipyard_policy,
83+
},
84+
}
8385
84-
output {
85-
to: SpacetimePolicy,
86-
amount: AnyAsset(spacetime_policy_hash, ship_name, 1) + Fuel(initial_fuel),
87-
datum: ShipDatum {
88-
pos_x: p_pos_x,
89-
pos_y: p_pos_y,
90-
ship_token_name: ship_name,
91-
pilot_token_name: pilot_name,
92-
last_move_latest_time: tip_slot,
93-
},
94-
}
86+
output new_ship {
87+
to: SpacetimePolicy,
88+
amount: AnyAsset(spacetime_policy_hash, ship_name, 1) + Fuel(initial_fuel) + min_utxo(new_ship),
89+
datum: ShipDatum {
90+
pos_x: p_pos_x,
91+
pos_y: p_pos_y,
92+
ship_token_name: ship_name,
93+
pilot_token_name: pilot_name,
94+
last_move_latest_time: last_move_timestamp,
95+
},
96+
}
97+
98+
output gas_change {
99+
to: Player,
100+
amount: gas - fees - Ada(ship_mint_lovelace_fee) - min_utxo(pilot_token) - min_utxo(new_ship),
101+
}
102+
103+
collateral {
104+
from: Player,
105+
min_amount: fees,
106+
}
95107
}`;
96108

97-
const jsFile = `const result = await protocol.createShipTx({
109+
const jsFile = `const playerAddress = process.env.PLAYER_ADDRESS;
110+
const positionX = 4; // Replace with your desired start X position
111+
const positionY = -50; // Replace with your desired start Y position
112+
const shipName = "SHIP0"; // Replace 0 with the next ship number
113+
const pilotName = "PILOT0"; // Replace 0 with the next ship number
114+
const tipSlot = 165246231; // Replace with the latest block slot
115+
const lastMoveTimestamp = Date.now() + 300_000;
116+
117+
const args: CreateShipParams = {
98118
player: playerAddress,
99119
pPosX: positionX,
100120
pPosY: positionY,
101-
pilotName: new TextEncoder().encode(\`PILOT\${shipNumber}\`),
102-
shipName: new TextEncoder().encode(\`SHIP\${shipNumber}\`),
103-
tipSlot: blockSlotValue + 300, // 5 minutes from last block
104-
});`;
121+
pilotName: new TextEncoder().encode(pilotName),
122+
shipName: new TextEncoder().encode(shipName),
123+
tipSlot: tipSlot + 300, // 5 minutes from last block
124+
lastMoveTimestamp,
125+
};
126+
127+
const response = await client.createShipTx(args);`;
105128

106129
interface CreateShipProps {
107130
isActive: boolean;
@@ -116,6 +139,8 @@ export default function CreateShip(props: CreateShipProps) {
116139
const [position, setPosition] = useState<{ x: number; y: number }|null>(null);
117140
const [wallet, setWallet] = useState<ConnectedWallet|null>(null);
118141
const [address, setAddress] = useState<string>('');
142+
const [shipNumber, setShipNumber] = useState<number|undefined>(undefined);
143+
const [tipSlot, setTipSlot] = useState<number|undefined>(undefined);
119144

120145
const errors = formState.errors || {};
121146
const dataTx = formState.data?.tx;
@@ -155,6 +180,36 @@ export default function CreateShip(props: CreateShipProps) {
155180
setAddress(connectedWallet?.changeAddress || '');
156181
}
157182

183+
const handleFetchShipNumber = async () => {
184+
const shipRes = await (await fetch("https://8000-ethereal-audience-bb83g6.us1.demeter.run/graphql", {
185+
"headers": { "content-type": "application/json" },
186+
"body": "{\"query\":\"query { nextShipTokenName(spacetimePolicyId: \\\"0291ae7aebaf064b785542093c2b13169effb34462301e68d4b44f43\\\", spacetimeAddress: \\\"addr1wypfrtn6awhsvjmc24pqj0ptzvtfalang33rq8ng6j6y7scnlkytx\\\") { shipName pilotName } }\",\"variables\":{}}",
187+
"method": "POST"
188+
})).json();
189+
190+
console.log(shipRes);
191+
192+
setShipNumber(parseInt(shipRes.data.nextShipTokenName.shipName.replace('SHIP', '')));
193+
}
194+
195+
const updateShipNumber = (event: ChangeEvent<HTMLInputElement>) => {
196+
setShipNumber(parseInt(event.target.value));
197+
}
198+
199+
const handleFetchLastSlot = async () => {
200+
const slotRes = await (await fetch("https://8000-ethereal-audience-bb83g6.us1.demeter.run/graphql", {
201+
"headers": { "content-type": "application/json" },
202+
"body": "{\"query\":\"query { lastSlot { slot } }\"}",
203+
"method": "POST"
204+
})).json();
205+
206+
setTipSlot(slotRes.data.lastSlot.slot);
207+
}
208+
209+
const updateTipSlot = (event: ChangeEvent<HTMLInputElement>) => {
210+
setTipSlot(parseInt(event.target.value));
211+
}
212+
158213
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
159214
event.preventDefault();
160215
setSubmitting(true);
@@ -179,9 +234,9 @@ export default function CreateShip(props: CreateShipProps) {
179234

180235
return (
181236
<Tabs className="w-full h-full overflow-hidden" contentClassName="overflow-auto">
182-
{/* <Tab label="Description">
237+
<Tab label="Description">
183238
<CreateShipDescription />
184-
</Tab> */}
239+
</Tab>
185240

186241
<Tab label="Tx Form">
187242
<form className="flex flex-col gap-8 justify-between h-full" onSubmit={handleSubmit}>
@@ -199,7 +254,7 @@ export default function CreateShip(props: CreateShipProps) {
199254

200255
<ConnectWallet onWalletConnected={handleWallet} />
201256

202-
<div className="w-full flex flex-row gap-x-4">
257+
<div className="w-full flex flex-row gap-x-4 mt-[-16px]">
203258
<Input
204259
name="shipNumber"
205260
type="number"
@@ -208,16 +263,25 @@ export default function CreateShip(props: CreateShipProps) {
208263
label="Ship Number"
209264
error={errors.shipNumber}
210265
disabled={submitting}
266+
button="fetch last"
267+
onClickButton={handleFetchShipNumber}
268+
value={shipNumber}
269+
onChange={updateShipNumber}
211270
required
212271
/>
272+
213273
<Input
214274
name="blockSlot"
215275
type="number"
216276
placeholder="Latest block slot"
217277
containerClassName="flex-1"
218278
label="Latest block slot"
219-
error={errors.shipNumber}
279+
error={errors.blockSlot}
220280
disabled={submitting}
281+
button="fetch last"
282+
onClickButton={handleFetchLastSlot}
283+
value={tipSlot}
284+
onChange={updateTipSlot}
221285
required
222286
/>
223287
</div>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Layout from "../mdx/Layout";
2+
export default ({ children }) => <Layout>{children}</Layout>;
3+
4+
#### With this transaction you can gather fuel from the fuel pellets for your ship.
5+
6+
Updates the amount of fuel tokens in both `ShipState` and `PelletState` UTxOs, adding the `amount` (specified in the redeemer) from the first and subtracting it from the latter. Also allows the ship owner to get any amount of the prize tokens held in the pellet.
7+
8+
#### Rules to take into account:
9+
10+
- the ship number has to be the one controlled by the pilot token in your wallet.
11+
- the ship has to be in the same position than the pellet
12+
- the ship can't have more than 5 fuel units
13+
14+
#### Diagram
15+
16+
![gatherFuel diagram](/txs/gather-fuel.png)

0 commit comments

Comments
 (0)