From aefe90011bc9fbc1fa1014ccf1a5ca1f474d3b28 Mon Sep 17 00:00:00 2001 From: Fraz Arshad Date: Thu, 29 Feb 2024 16:56:41 +0500 Subject: [PATCH 1/2] feat(ci): Included ui/ and contract/ from offer-up-dapp (with changes) --- contract/.gitignore | 3 + contract/Makefile | 121 +++++++++ contract/jsconfig.json | 19 ++ contract/package.json | 85 +++++++ contract/scripts/build-contract-deployer.js | 42 ++++ contract/scripts/build-proposal.sh | 11 + contract/scripts/install-bundles.sh | 24 ++ contract/scripts/parseProposals.mjs | 41 ++++ contract/scripts/propose-start-contract.sh | 22 ++ contract/scripts/run-chain.sh | 71 ++++++ contract/scripts/voteLatestProposalAndWait.sh | 5 + contract/src/debug.js | 41 ++++ contract/src/offer-up-proposal.js | 126 ++++++++++ contract/src/offer-up.contract.js | 141 +++++++++++ contract/test/mintStable.js | 77 ++++++ contract/test/prepare-test-env-ava.js | 13 + contract/test/test-build-proposal.js | 29 +++ contract/test/test-bundle-source.js | 28 +++ contract/test/test-contract.js | 229 ++++++++++++++++++ contract/test/utils.js | 15 ++ ui/.eslintrc.cjs | 20 ++ ui/index.html | 13 + ui/package.json | 52 ++++ ui/public/agoric.svg | 10 + ui/public/vite.svg | 1 + ui/src/App.css | 109 +++++++++ ui/src/App.spec.tsx | 22 ++ ui/src/App.tsx | 160 ++++++++++++ ui/src/assets/IST.svg | 19 ++ ui/src/assets/map.png | Bin 0 -> 12257 bytes ui/src/assets/potionBlue.png | Bin 0 -> 10614 bytes ui/src/assets/react.svg | 1 + ui/src/assets/scroll.png | Bin 0 -> 10195 bytes ui/src/components/Inventory.tsx | 49 ++++ ui/src/components/Logos.tsx | 19 ++ ui/src/components/Trade.tsx | 141 +++++++++++ ui/src/index.css | 72 ++++++ ui/src/index.d.ts | 16 ++ ui/src/installSesLockdown.ts | 13 + ui/src/main.tsx | 11 + ui/src/vite-env.d.ts | 9 + ui/test/App.e2e.ts | 28 +++ ui/tsconfig.json | 27 +++ ui/tsconfig.node.json | 10 + ui/vite.config.ts | 19 ++ 45 files changed, 1964 insertions(+) create mode 100644 contract/.gitignore create mode 100644 contract/Makefile create mode 100644 contract/jsconfig.json create mode 100644 contract/package.json create mode 100644 contract/scripts/build-contract-deployer.js create mode 100755 contract/scripts/build-proposal.sh create mode 100755 contract/scripts/install-bundles.sh create mode 100755 contract/scripts/parseProposals.mjs create mode 100755 contract/scripts/propose-start-contract.sh create mode 100755 contract/scripts/run-chain.sh create mode 100755 contract/scripts/voteLatestProposalAndWait.sh create mode 100644 contract/src/debug.js create mode 100644 contract/src/offer-up-proposal.js create mode 100644 contract/src/offer-up.contract.js create mode 100644 contract/test/mintStable.js create mode 100644 contract/test/prepare-test-env-ava.js create mode 100644 contract/test/test-build-proposal.js create mode 100644 contract/test/test-bundle-source.js create mode 100644 contract/test/test-contract.js create mode 100644 contract/test/utils.js create mode 100644 ui/.eslintrc.cjs create mode 100644 ui/index.html create mode 100644 ui/package.json create mode 100644 ui/public/agoric.svg create mode 100644 ui/public/vite.svg create mode 100644 ui/src/App.css create mode 100644 ui/src/App.spec.tsx create mode 100644 ui/src/App.tsx create mode 100644 ui/src/assets/IST.svg create mode 100644 ui/src/assets/map.png create mode 100644 ui/src/assets/potionBlue.png create mode 100644 ui/src/assets/react.svg create mode 100644 ui/src/assets/scroll.png create mode 100644 ui/src/components/Inventory.tsx create mode 100644 ui/src/components/Logos.tsx create mode 100644 ui/src/components/Trade.tsx create mode 100644 ui/src/index.css create mode 100644 ui/src/index.d.ts create mode 100644 ui/src/installSesLockdown.ts create mode 100644 ui/src/main.tsx create mode 100644 ui/src/vite-env.d.ts create mode 100644 ui/test/App.e2e.ts create mode 100644 ui/tsconfig.json create mode 100644 ui/tsconfig.node.json create mode 100644 ui/vite.config.ts diff --git a/contract/.gitignore b/contract/.gitignore new file mode 100644 index 000000000..eb994f6c6 --- /dev/null +++ b/contract/.gitignore @@ -0,0 +1,3 @@ +start-offer-up-permit.json +start-offer-up.js +bundles/ diff --git a/contract/Makefile b/contract/Makefile new file mode 100644 index 000000000..f2b850691 --- /dev/null +++ b/contract/Makefile @@ -0,0 +1,121 @@ +CHAINID=agoriclocal +USER1ADDR=$(shell agd keys show user1 -a --keyring-backend="test") +ACCT_ADDR=$(USER1ADDR) +BLD=000000ubld + +ATOM_DENOM=ibc/BA313C4A19DFBF943586C0387E6B11286F9E416B4DD27574E6909CABE0E342FA +ATOM=000000$(ATOM_DENOM) + +.PHONY: list +# https://stackoverflow.com/a/73159833/7963 +list: + @make -npq : 2> /dev/null | grep -v PHONY |\ + awk -v RS= -F: '$$1 ~ /^[^#%]+$$/ { print $$1 }' + +balance-q: + agd keys show user1 -a --keyring-backend="test" + agd query bank balances $(ACCT_ADDR) + +GAS_ADJUSTMENT=1.2 +SIGN_BROADCAST_OPTS=--keyring-backend=test --chain-id=$(CHAINID) \ + --gas=auto --gas-adjustment=$(GAS_ADJUSTMENT) \ + --yes -b block + +mint100: + make FUNDS=1000$(ATOM) fund-acct + cd /usr/src/agoric-sdk && \ + yarn --silent agops vaults open --wantMinted 100 --giveCollateral 100 >/tmp/want-ist.json && \ + yarn --silent agops perf satisfaction --executeOffer /tmp/want-ist.json --from user1 --keyring-backend=test + +# https://agoric.explorers.guru/proposal/61 +lower-bundle-cost: bundles/lower-bundle-cost.json ./scripts/voteLatestProposalAndWait.sh + agd tx gov submit-proposal param-change bundles/lower-bundle-cost.json \ + $(SIGN_BROADCAST_OPTS) \ + --from user1 + ./scripts/voteLatestProposalAndWait.sh + # agd query swingset params + + +bundles/swingset-params.json: + mkdir -p bundles/ + agd query swingset params -o json >$@ + +.ONESHELL: +bundles/lower-bundle-cost.json: bundles/swingset-params.json + @read PARAMS < bundles/swingset-params.json; export PARAMS + node - <<- EOF >$@ + const storageByte = '20000000'; + const paramChange = { + title: 'Lower Bundle Cost to 0.02 IST/Kb (a la mainnet 61)', + description: '0.02 IST/Kb', + deposit: '10000000ubld', + changes: [{ + subspace: 'swingset', + key: 'beans_per_unit', + value: '...', + }], + }; + const params = JSON.parse(process.env.PARAMS); + const ix = params.beans_per_unit.findIndex(({key}) => key === 'storageByte'); + params.beans_per_unit[ix].beans = storageByte; + paramChange.changes[0].value = params.beans_per_unit; + console.log(JSON.stringify(paramChange, null, 2)); + EOF + +# Keep mint4k around a while for compatibility +mint4k: + make FUNDS=1000$(ATOM) fund-acct + cd /usr/src/agoric-sdk && \ + yarn --silent agops vaults open --wantMinted 4000 --giveCollateral 1000 >/tmp/want4k.json && \ + yarn --silent agops perf satisfaction --executeOffer /tmp/want4k.json --from user1 --keyring-backend=test + +FUNDS=321$(BLD) +fund-acct: + agd tx bank send validator $(ACCT_ADDR) $(FUNDS) \ + $(SIGN_BROADCAST_OPTS) \ + -o json >,tx.json + jq '{code: .code, height: .height}' ,tx.json + +gov-q: + agd query gov proposals --output json | \ + jq -c '.proposals[] | [.proposal_id,.voting_end_time,.status]' + +gov-voting-q: + agd query gov proposals --status=voting_period --output json | \ + jq -c '.proposals[].proposal_id' + +PROPOSAL=1 +VOTE_OPTION=yes +vote: + agd tx gov vote $(PROPOSAL) $(VOTE_OPTION) --from=validator \ + $(SIGN_BROADCAST_OPTS) \ + -o json >,tx.json + jq '{code: .code, height: .height}' ,tx.json + +instance-q: + agd query vstorage data published.agoricNames.instance -o json + +print-key: /root/.agoric/user1.key + @echo Import the following mnemonic into Keplr: + @cat $< + @echo + @echo -n 'The resulting address should be: ' + @agd keys show user1 -a --keyring-backend="test" + @echo + +SCRIPT=start-offer-up.js +PERMIT=start-offer-up-permit.json +start-contract: $(SCRIPT) $(PERMIT) install-bundles + scripts/propose-start-contract.sh + +install-bundles: bundles/bundle-list + ./scripts/install-bundles.sh + +build-proposal: bundles/bundle-list + +bundles/bundle-list $(SCRIPT) $(PERMIT): + ./scripts/build-proposal.sh + + +clean: + @rm -rf $(SCRIPT) $(PERMIT) bundles/ diff --git a/contract/jsconfig.json b/contract/jsconfig.json new file mode 100644 index 000000000..e7dca0046 --- /dev/null +++ b/contract/jsconfig.json @@ -0,0 +1,19 @@ +// This file can contain .js-specific Typescript compiler config. +{ + "compilerOptions": { + "target": "esnext", + "module": "ES2022", + + "noEmit": true, + /* + // The following flags are for creating .d.ts files: + "noEmit": false, + "declaration": true, + "emitDeclarationOnly": true, +*/ + "downlevelIteration": true, + "strictNullChecks": true, + "moduleResolution": "node" + }, + "include": ["src/**/*.js", "test/**/*.js", "exported.js", "globals.d.ts"] +} diff --git a/contract/package.json b/contract/package.json new file mode 100644 index 000000000..3a53aca50 --- /dev/null +++ b/contract/package.json @@ -0,0 +1,85 @@ +{ + "name": "demo2-contract", + "version": "0.1.0", + "private": true, + "description": "Offer Up Contract", + "type": "module", + "scripts": { + "start:docker": "docker compose up -d", + "docker:logs": "docker compose logs --tail 200 -f", + "docker:bash": "docker compose exec agd bash", + "docker:make": "docker compose exec agd make -C /workspace/contract", + "make:help": "make list", + "start": "yarn docker:make clean start-contract print-key", + "build": "agoric run scripts/build-contract-deployer.js", + "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-offer-up" + }, + "author": "Agoric", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/Agoric/dapp-offer-up/issues" + }, + "homepage": "https://github.com/Agoric/dapp-offer-up#readme", + "eslintConfig": { + "parserOptions": { + "sourceType": "module", + "ecmaVersion": 2021 + }, + "extends": [ + "@agoric" + ] + }, + "prettier": { + "trailingComma": "all", + "arrowParens": "avoid", + "singleQuote": true + } +} \ No newline at end of file diff --git a/contract/scripts/build-contract-deployer.js b/contract/scripts/build-contract-deployer.js new file mode 100644 index 000000000..f9732f1a3 --- /dev/null +++ b/contract/scripts/build-contract-deployer.js @@ -0,0 +1,42 @@ +/** + * @file Permission Contract Deployment builder + * + * Creates files for starting an instance of the contract: + * * contract source and instantiation proposal bundles to be published via + * `agd tx swingset install-bundle` + * * start-offer-up-permit.json and start-offer-up.js to submit the + * instantiation proposal via `agd tx gov submit-proposal swingset-core-eval` + * + * Usage: + * agoric run build-contract-deployer.js + */ + +import { makeHelpers } from '@agoric/deploy-script-support'; +import { getManifestForOfferUp } from '../src/offer-up-proposal.js'; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */ +export const offerUpProposalBuilder = async ({ publishRef, install }) => { + return harden({ + sourceSpec: '../src/offer-up-proposal.js', + getManifestCall: [ + getManifestForOfferUp.name, + { + offerUpRef: publishRef( + install( + '../src/offer-up.contract.js', + '../bundles/bundle-offer-up.js', + { + persist: true, + }, + ), + ), + }, + ], + }); +}; + +/** @type {DeployScriptFunction} */ +export default async (homeP, endowments) => { + const { writeCoreProposal } = await makeHelpers(homeP, endowments); + await writeCoreProposal('start-offer-up', offerUpProposalBuilder); +}; diff --git a/contract/scripts/build-proposal.sh b/contract/scripts/build-proposal.sh new file mode 100755 index 000000000..68bdcadb5 --- /dev/null +++ b/contract/scripts/build-proposal.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# NOTE: intended to run _inside_ the agd container + +cd /workspace/contract + +mkdir -p bundles +(agoric run ./scripts/build-contract-deployer.js )>/tmp/,run.log +./scripts/parseProposals.mjs bundles/bundle-list + + diff --git a/contract/scripts/install-bundles.sh b/contract/scripts/install-bundles.sh new file mode 100755 index 000000000..717257bb2 --- /dev/null +++ b/contract/scripts/install-bundles.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# NOTE: intended to run _inside_ the agd container + +set -xueo pipefail + +cd /workspace/contract + +# TODO: try `agoric publish` to better track outcome +install_bundle() { + ls -sh "$1" + agd tx swingset install-bundle --compress "@$1" \ + --from user1 --keyring-backend=test --gas=auto --gas-adjustment=1.2 \ + --chain-id=agoriclocal -bblock --yes -o json +} + +# exit fail if bundle-list is emtpy +[ -s bundles/bundle-list ] || exit 1 + +make balance-q # do we have enough IST? + +for b in $(cat bundles/bundle-list); do + echo installing $b + install_bundle $b +done diff --git a/contract/scripts/parseProposals.mjs b/contract/scripts/parseProposals.mjs new file mode 100755 index 000000000..daab1be36 --- /dev/null +++ b/contract/scripts/parseProposals.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +import fs from 'fs'; + +const Fail = (template, ...args) => { + throw Error(String.raw(template, ...args.map(val => String(val)))); +}; + +/** + * Parse output of `agoric run proposal-builder.js` + * + * @param {string} txt + * + * adapted from packages/boot/test/bootstrapTests/supports.js + */ +const parseProposalParts = txt => { + const evals = [ + ...txt.matchAll(/swingset-core-eval (?\S+) (? + + diff --git a/ui/package.json b/ui/package.json new file mode 100644 index 000000000..19111626b --- /dev/null +++ b/ui/package.json @@ -0,0 +1,52 @@ +{ + "name": "demo2-ui", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "test": "vitest spec", + "test:e2e": "vitest e2e", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint:fix": "yarn lint --fix", + "preview": "vite preview" + }, + "dependencies": { + "@endo/eventual-send": "0.17.2", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@agoric/eventual-send": "^0.14.1", + "@agoric/notifier": "^0.6.2", + "@agoric/rpc": "0.9.1-dev-f471a83.0", + "@agoric/store": "^0.9.2", + "@agoric/ui-components": "^0.9.0", + "@agoric/web-components": "^0.15.0", + "@testing-library/react": "^14.1.2", + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "happy-dom": "^13.3.1", + "prettier": "^3.2.4", + "puppeteer": "^21.9.0", + "ses": "1.3.0", + "typescript": "^5.0.2", + "vite": "^4.4.5", + "vitest": "^1.2.1", + "zustand": "^4.4.1" + }, + "prettier": { + "trailingComma": "all", + "arrowParens": "avoid", + "singleQuote": true + } +} \ No newline at end of file diff --git a/ui/public/agoric.svg b/ui/public/agoric.svg new file mode 100644 index 000000000..28822fa58 --- /dev/null +++ b/ui/public/agoric.svg @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ui/public/vite.svg b/ui/public/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/ui/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/App.css b/ui/src/App.css new file mode 100644 index 000000000..b4d6ec708 --- /dev/null +++ b/ui/src/App.css @@ -0,0 +1,109 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 1rem; + text-align: center; +} + +.logo { + height: 2em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} +.logo.agoric:hover { + filter: drop-shadow(0 0 2em #fa4a49aa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 1em; +} + +.read-the-docs { + color: #888; +} + +.piece { + width: 6em; + border-radius: 10%; +} + +.coin { + width: 2em; + margin: 10px; +} + +.trade { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background: #171717; + border-radius: 25px; + margin-bottom: 15px; +} + +.item-col { + display: flex; + flex-direction: column; + align-items: center; + padding: 0 15px 25px 15px; + margin: 5px; +} + +.row-center { + display: flex; + flex-direction: row; + align-items: center; +} + +input { + border: none; + background: #242424; + text-align: center; + padding: 5px 10px; + border-radius: 15px; + font-size: 1.2rem; + width: 75px; +} + +@media (prefers-color-scheme: light) { + .trade { + background: #fafafa; + border: 1px solid #e5e5e5; + } + input { + background: #e5e5e5; + } +} + +.error { + background-color: #E11D48; + color: #fff; +} + +/* increment/decrement arrows always visible */ +input[type=number]::-webkit-inner-spin-button { + opacity: 1 +} diff --git a/ui/src/App.spec.tsx b/ui/src/App.spec.tsx new file mode 100644 index 000000000..aa6bf43c8 --- /dev/null +++ b/ui/src/App.spec.tsx @@ -0,0 +1,22 @@ +import './installSesLockdown'; +import { render, screen } from '@testing-library/react'; +import App from './App'; + +describe('App.tsx', () => { + it('renders app title', async () => { + render(); + + const titleElement = await screen.findByText('Items Listed on Offer Up', { + selector: 'h1', + }); + expect(titleElement).toBeTruthy(); + }); + + it('renders the wallet connection button', async () => { + render(); + const buttonEl = await screen.findByRole('button', { + name: 'Connect Wallet', + }); + expect(buttonEl).toBeTruthy(); + }); +}); diff --git a/ui/src/App.tsx b/ui/src/App.tsx new file mode 100644 index 000000000..a43c4760b --- /dev/null +++ b/ui/src/App.tsx @@ -0,0 +1,160 @@ +import { useEffect } from 'react'; + +import './App.css'; +import { + makeAgoricChainStorageWatcher, + AgoricChainStoragePathKind as Kind, +} from '@agoric/rpc'; +import { create } from 'zustand'; +import { + makeAgoricWalletConnection, + suggestChain, +} from '@agoric/web-components'; +import { subscribeLatest } from '@agoric/notifier'; +import { makeCopyBag } from '@agoric/store'; +import { Logos } from './components/Logos'; +import { Inventory } from './components/Inventory'; +import { Trade } from './components/Trade'; + +const { entries, fromEntries } = Object; + +const RUN_ENV = import.meta.env.VITE_RUN_ENV || 'localhost'; +if (!['localhost', 'agoric_chain'].includes(RUN_ENV)) + throw new Error("VITE_RUN_ENV can only be 'agoric_chain' or 'localhost'"); + +type Wallet = Awaited>; + +const ENDPOINTS = { + RPC: `http://${RUN_ENV}:26657`, + API: `http://${RUN_ENV}:1317`, +}; + +const watcher = makeAgoricChainStorageWatcher(ENDPOINTS.API, 'agoriclocal'); + +interface AppState { + wallet?: Wallet; + offerUpInstance?: unknown; + brands?: Record; + purses?: Array; +} + +const useAppStore = create(() => ({})); + +const setup = async () => { + watcher.watchLatest>( + [Kind.Data, 'published.agoricNames.instance'], + instances => { + console.log('got instances', instances); + useAppStore.setState({ + offerUpInstance: instances.find(([name]) => name === 'offerUp')!.at(1), + }); + }, + ); + + watcher.watchLatest>( + [Kind.Data, 'published.agoricNames.brand'], + brands => { + console.log('Got brands', brands); + useAppStore.setState({ + brands: fromEntries(brands), + }); + }, + ); +}; + +const connectWallet = async () => { + await suggestChain(`http://localhost:3004/${RUN_ENV}`); + const wallet = await makeAgoricWalletConnection(watcher, ENDPOINTS.RPC); + useAppStore.setState({ wallet }); + const { pursesNotifier } = wallet; + for await (const purses of subscribeLatest(pursesNotifier)) { + console.log('got purses', purses); + useAppStore.setState({ purses }); + } +}; + +const makeOffer = (giveValue: bigint, wantChoices: Record) => { + const { wallet, offerUpInstance, brands } = useAppStore.getState(); + if (!offerUpInstance) throw Error('no contract instance'); + if (!(brands && brands.IST && brands.Item)) + throw Error('brands not available'); + + const value = makeCopyBag(entries(wantChoices)); + const want = { Items: { brand: brands.Item, value } }; + const give = { Price: { brand: brands.IST, value: giveValue } }; + + wallet?.makeOffer( + { + source: 'contract', + instance: offerUpInstance, + publicInvitationMaker: 'makeTradeInvitation', + }, + { give, want }, + undefined, + (update: { status: string; data?: unknown }) => { + if (update.status === 'error') { + alert(`Offer error: ${update.data}`); + } + if (update.status === 'accepted') { + alert('Offer accepted'); + } + if (update.status === 'refunded') { + alert('Offer rejected'); + } + }, + ); +}; + +function App() { + useEffect(() => { + setup(); + }, []); + + const { wallet, purses } = useAppStore(({ wallet, purses }) => ({ + wallet, + purses, + })); + const istPurse = purses?.find(p => p.brandPetname === 'IST'); + const itemsPurse = purses?.find(p => p.brandPetname === 'Item'); + + const tryConnectWallet = () => { + connectWallet().catch(err => { + switch (err.message) { + case 'KEPLR_CONNECTION_ERROR_NO_SMART_WALLET': + alert( + 'no smart wallet at that address; try: yarn docker:make print-key', + ); + break; + default: + alert(err.message); + } + }); + }; + + return ( + <> + +

Items Listed on Offer Up

+ +
+ +
+ {wallet && istPurse ? ( + + ) : ( + + )} +
+ + ); +} + +export default App; diff --git a/ui/src/assets/IST.svg b/ui/src/assets/IST.svg new file mode 100644 index 000000000..1fcb75c89 --- /dev/null +++ b/ui/src/assets/IST.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui/src/assets/map.png b/ui/src/assets/map.png new file mode 100644 index 0000000000000000000000000000000000000000..278c0936b1da801c6bed6a2fd8a64c000aec272d GIT binary patch literal 12257 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 TIPmqKu?>)yR+g%R8i)K37K}5` literal 0 HcmV?d00001 diff --git a/ui/src/assets/react.svg b/ui/src/assets/react.svg new file mode 100644 index 000000000..6c87de9bb --- /dev/null +++ b/ui/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/assets/scroll.png b/ui/src/assets/scroll.png new file mode 100644 index 0000000000000000000000000000000000000000..b61fe085b48a10bd8dd17fca7135e2157b086cb3 GIT binary patch literal 10195 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 VJ`?TD{`)lrD9WkI)=8U%{~v@Jf5rd+ literal 0 HcmV?d00001 diff --git a/ui/src/components/Inventory.tsx b/ui/src/components/Inventory.tsx new file mode 100644 index 000000000..53dea99b3 --- /dev/null +++ b/ui/src/components/Inventory.tsx @@ -0,0 +1,49 @@ +import { stringifyAmountValue } from '@agoric/ui-components'; + +type InventoryProps = { + address: string; + istPurse: Purse; + itemsPurse: Purse; +}; + +const Inventory = ({ address, istPurse, itemsPurse }: InventoryProps) => ( +
+

My Wallet

+
+
+ + {address} + +
+ +
+
+ IST: + {stringifyAmountValue( + istPurse.currentAmount, + istPurse.displayInfo.assetKind, + istPurse.displayInfo.decimalPlaces, + )} +
+
+ Items: + {itemsPurse ? ( +
    + {(itemsPurse.currentAmount.value as CopyBag).payload.map( + ([name, number]) => ( +
  • + {String(number)} {name} +
  • + ), + )} +
+ ) : ( + 'None' + )} +
+
+
+
+); + +export { Inventory }; diff --git a/ui/src/components/Logos.tsx b/ui/src/components/Logos.tsx new file mode 100644 index 000000000..13e9e5c85 --- /dev/null +++ b/ui/src/components/Logos.tsx @@ -0,0 +1,19 @@ +import reactLogo from '../assets/react.svg'; +import viteLogo from '/vite.svg'; +import agoricLogo from '/agoric.svg'; + +const Logos = () => ( + +); + +export { Logos }; diff --git a/ui/src/components/Trade.tsx b/ui/src/components/Trade.tsx new file mode 100644 index 000000000..9ce8b8751 --- /dev/null +++ b/ui/src/components/Trade.tsx @@ -0,0 +1,141 @@ +import { FormEvent, useState } from 'react'; +import { stringifyAmountValue } from '@agoric/ui-components'; +import scrollIcon from '../assets/scroll.png'; +import istIcon from '../assets/IST.svg'; +import mapIcon from '../assets/map.png'; +import potionIcon from '../assets/potionBlue.png'; + +const { entries, values } = Object; +const sum = (xs: bigint[]) => xs.reduce((acc, next) => acc + next, 0n); + +const terms = { + price: 250000n, + maxItems: 3n, +}; +const nameToIcon = { + scroll: scrollIcon, + map: mapIcon, + potion: potionIcon, +} as const; +type ItemName = keyof typeof nameToIcon; +type ItemChoices = Partial>; + +const parseValue = (numeral: string, purse: Purse): bigint => { + const { decimalPlaces } = purse.displayInfo; + const num = Number(numeral) * 10 ** decimalPlaces; + return BigInt(num); +}; + +const Item = ({ + icon, + coinIcon, + label, + value, + onChange, + inputClassName, + inputStep, +}: { + icon?: string; + coinIcon?: string; + label: string; + value: number | string; + onChange: React.ChangeEventHandler; + inputClassName: string; + inputStep?: string; +}) => ( +
+ + {icon && } + {coinIcon && } + +
+); + +type TradeProps = { + makeOffer: (giveValue: bigint, wantChoices: Record) => void; + istPurse: Purse; + walletConnected: boolean; +}; + +// TODO: IST displayInfo is available in vbankAsset or boardAux +const Trade = ({ makeOffer, istPurse, walletConnected }: TradeProps) => { + const [giveValue, setGiveValue] = useState(terms.price); + const [choices, setChoices] = useState({ map: 1n, scroll: 2n }); + const changeChoice = (ev: FormEvent) => { + if (!ev.target) return; + const elt = ev.target as HTMLInputElement; + const title = elt.title as ItemName; + if (!title) return; + const qty = BigInt(elt.value); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { [title]: _old, ...rest }: ItemChoices = choices; + const newChoices = qty > 0 ? { ...rest, [title]: qty } : rest; + setChoices(newChoices); + }; + + return ( + <> +
+

Want: Choose up to 3 items

+
+ {entries(nameToIcon).map(([title, icon]) => ( + + ))} +
+
+
+

Give: Offer at least 0.25 IST

+
+ + setGiveValue(parseValue(ev?.target?.value, istPurse)) + } + inputClassName={giveValue >= terms.price ? 'ok' : 'error'} + inputStep="0.01" + /> +
+
+
+ {walletConnected && ( + + )} +
+ + ); +}; + +export { Trade }; diff --git a/ui/src/index.css b/ui/src/index.css new file mode 100644 index 000000000..d3f626a88 --- /dev/null +++ b/ui/src/index.css @@ -0,0 +1,72 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + min-width: 320px; + min-height: 100vh; +} + +h1 { + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 4px solid transparent; + padding: 12px 16px; + margin: 8px 2px; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + background: #171717; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #04aa6d; /* Green */ + color: #fff; + } + button:hover { + background: #039962; + } +} diff --git a/ui/src/index.d.ts b/ui/src/index.d.ts new file mode 100644 index 000000000..d2ee4b35d --- /dev/null +++ b/ui/src/index.d.ts @@ -0,0 +1,16 @@ +interface CopyBag { + payload: Array<[T, bigint]>; +} + +interface Purse { + brand: unknown; + brandPetname: string; + currentAmount: { + brand: unknown; + value: bigint | CopyBag; + }; + displayInfo: { + decimalPlaces: number; + assetKind: unknown; + }; +} diff --git a/ui/src/installSesLockdown.ts b/ui/src/installSesLockdown.ts new file mode 100644 index 000000000..2eb8e6e6b --- /dev/null +++ b/ui/src/installSesLockdown.ts @@ -0,0 +1,13 @@ +import 'ses'; // adds lockdown, harden, and Compartment +import '@endo/eventual-send/shim.js'; // adds support needed by E + +const consoleTaming = import.meta.env.DEV ? 'unsafe' : 'safe'; + +// @ts-expect-error global +lockdown({ + errorTaming: 'unsafe', + overrideTaming: 'severe', + consoleTaming, +}); + +Error.stackTraceLimit = Infinity; diff --git a/ui/src/main.tsx b/ui/src/main.tsx new file mode 100644 index 000000000..bfb9f34e7 --- /dev/null +++ b/ui/src/main.tsx @@ -0,0 +1,11 @@ +import './installSesLockdown.ts'; +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App.tsx'; +import './index.css'; + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +); diff --git a/ui/src/vite-env.d.ts b/ui/src/vite-env.d.ts new file mode 100644 index 000000000..11686fc78 --- /dev/null +++ b/ui/src/vite-env.d.ts @@ -0,0 +1,9 @@ +/// + +declare module '@agoric/ui-components' { + export const stringifyAmountValue; +} + +declare module '@agoric/store' { + export const makeCopyBag; +} diff --git a/ui/test/App.e2e.ts b/ui/test/App.e2e.ts new file mode 100644 index 000000000..5e7ca1cd5 --- /dev/null +++ b/ui/test/App.e2e.ts @@ -0,0 +1,28 @@ +import puppeteer from 'puppeteer'; +import { execSync, spawn } from 'node:child_process'; + +describe('Puppeteer E2E test', () => { + it('should load the webpage', async () => { + execSync('yarn build'); + const previewServer = spawn('yarn', ['preview'], { + detached: true, + stdio: 'ignore', + }); + // delay for preview server to start + await new Promise(resolve => setTimeout(resolve, 2000)); + + try { + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + await page.goto('http://localhost:4173'); + + const buttonText = await page.$eval('button', el => el.textContent); + expect(buttonText).toBe('Connect Wallet'); + + await browser.close(); + previewServer.kill(); + } catch (_) { + previewServer.kill(); + } + }); +}); diff --git a/ui/tsconfig.json b/ui/tsconfig.json new file mode 100644 index 000000000..7f545954a --- /dev/null +++ b/ui/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + + "types": ["vitest/globals"], + }, + "include": ["src", "test"], + "references": [{ "path": "./tsconfig.node.json" }], +} diff --git a/ui/tsconfig.node.json b/ui/tsconfig.node.json new file mode 100644 index 000000000..42872c59f --- /dev/null +++ b/ui/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/ui/vite.config.ts b/ui/vite.config.ts new file mode 100644 index 000000000..7fa039ff7 --- /dev/null +++ b/ui/vite.config.ts @@ -0,0 +1,19 @@ +/// + +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], + test: { + globals: true, + environment: 'happy-dom', + testTimeout: 20000, // 20 seconds for puppeteer CI + // modified import('vitest/dist/config.js').defaultInclude + include: '**/*.{spec,e2e}.?(c|m)[jt]s?(x)', + }, + server: { + port: 3000, + }, +}); From 60454e4e43ade249df75846d145db521d07b90a2 Mon Sep 17 00:00:00 2001 From: Fraz Arshad Date: Sun, 3 Mar 2024 23:00:07 +0500 Subject: [PATCH 2/2] refactor: moved ui/ and contract/ to tools/ folder --- {contract => tools/contract}/.gitignore | 0 {contract => tools/contract}/Makefile | 0 {contract => tools/contract}/jsconfig.json | 0 {contract => tools/contract}/package.json | 0 .../contract}/scripts/build-contract-deployer.js | 0 .../contract}/scripts/build-proposal.sh | 0 .../contract}/scripts/install-bundles.sh | 0 .../contract}/scripts/parseProposals.mjs | 0 .../contract}/scripts/propose-start-contract.sh | 0 {contract => tools/contract}/scripts/run-chain.sh | 0 .../contract}/scripts/voteLatestProposalAndWait.sh | 0 {contract => tools/contract}/src/debug.js | 0 .../contract}/src/offer-up-proposal.js | 0 .../contract}/src/offer-up.contract.js | 0 {contract => tools/contract}/test/mintStable.js | 0 .../contract}/test/prepare-test-env-ava.js | 0 .../contract}/test/test-build-proposal.js | 0 .../contract}/test/test-bundle-source.js | 0 {contract => tools/contract}/test/test-contract.js | 0 {contract => tools/contract}/test/utils.js | 0 {ui => tools/ui}/.eslintrc.cjs | 0 {ui => tools/ui}/index.html | 0 {ui => tools/ui}/package.json | 0 {ui => tools/ui}/public/agoric.svg | 0 {ui => tools/ui}/public/vite.svg | 0 {ui => tools/ui}/src/App.css | 0 {ui => tools/ui}/src/App.spec.tsx | 0 {ui => tools/ui}/src/App.tsx | 0 {ui => tools/ui}/src/assets/IST.svg | 0 {ui => tools/ui}/src/assets/map.png | Bin {ui => tools/ui}/src/assets/potionBlue.png | Bin {ui => tools/ui}/src/assets/react.svg | 0 {ui => tools/ui}/src/assets/scroll.png | Bin {ui => tools/ui}/src/components/Inventory.tsx | 0 {ui => tools/ui}/src/components/Logos.tsx | 0 {ui => tools/ui}/src/components/Trade.tsx | 0 {ui => tools/ui}/src/index.css | 0 {ui => tools/ui}/src/index.d.ts | 0 {ui => tools/ui}/src/installSesLockdown.ts | 0 {ui => tools/ui}/src/main.tsx | 0 {ui => tools/ui}/src/vite-env.d.ts | 0 {ui => tools/ui}/test/App.e2e.ts | 0 {ui => tools/ui}/tsconfig.json | 0 {ui => tools/ui}/tsconfig.node.json | 0 {ui => tools/ui}/vite.config.ts | 0 45 files changed, 0 insertions(+), 0 deletions(-) rename {contract => tools/contract}/.gitignore (100%) rename {contract => tools/contract}/Makefile (100%) rename {contract => tools/contract}/jsconfig.json (100%) rename {contract => tools/contract}/package.json (100%) rename {contract => tools/contract}/scripts/build-contract-deployer.js (100%) rename {contract => tools/contract}/scripts/build-proposal.sh (100%) rename {contract => tools/contract}/scripts/install-bundles.sh (100%) rename {contract => tools/contract}/scripts/parseProposals.mjs (100%) rename {contract => tools/contract}/scripts/propose-start-contract.sh (100%) rename {contract => tools/contract}/scripts/run-chain.sh (100%) rename {contract => tools/contract}/scripts/voteLatestProposalAndWait.sh (100%) rename {contract => tools/contract}/src/debug.js (100%) rename {contract => tools/contract}/src/offer-up-proposal.js (100%) rename {contract => tools/contract}/src/offer-up.contract.js (100%) rename {contract => tools/contract}/test/mintStable.js (100%) rename {contract => tools/contract}/test/prepare-test-env-ava.js (100%) rename {contract => tools/contract}/test/test-build-proposal.js (100%) rename {contract => tools/contract}/test/test-bundle-source.js (100%) rename {contract => tools/contract}/test/test-contract.js (100%) rename {contract => tools/contract}/test/utils.js (100%) rename {ui => tools/ui}/.eslintrc.cjs (100%) rename {ui => tools/ui}/index.html (100%) rename {ui => tools/ui}/package.json (100%) rename {ui => tools/ui}/public/agoric.svg (100%) rename {ui => tools/ui}/public/vite.svg (100%) rename {ui => tools/ui}/src/App.css (100%) rename {ui => tools/ui}/src/App.spec.tsx (100%) rename {ui => tools/ui}/src/App.tsx (100%) rename {ui => tools/ui}/src/assets/IST.svg (100%) rename {ui => tools/ui}/src/assets/map.png (100%) rename {ui => tools/ui}/src/assets/potionBlue.png (100%) rename {ui => tools/ui}/src/assets/react.svg (100%) rename {ui => tools/ui}/src/assets/scroll.png (100%) rename {ui => tools/ui}/src/components/Inventory.tsx (100%) rename {ui => tools/ui}/src/components/Logos.tsx (100%) rename {ui => tools/ui}/src/components/Trade.tsx (100%) rename {ui => tools/ui}/src/index.css (100%) rename {ui => tools/ui}/src/index.d.ts (100%) rename {ui => tools/ui}/src/installSesLockdown.ts (100%) rename {ui => tools/ui}/src/main.tsx (100%) rename {ui => tools/ui}/src/vite-env.d.ts (100%) rename {ui => tools/ui}/test/App.e2e.ts (100%) rename {ui => tools/ui}/tsconfig.json (100%) rename {ui => tools/ui}/tsconfig.node.json (100%) rename {ui => tools/ui}/vite.config.ts (100%) diff --git a/contract/.gitignore b/tools/contract/.gitignore similarity index 100% rename from contract/.gitignore rename to tools/contract/.gitignore diff --git a/contract/Makefile b/tools/contract/Makefile similarity index 100% rename from contract/Makefile rename to tools/contract/Makefile diff --git a/contract/jsconfig.json b/tools/contract/jsconfig.json similarity index 100% rename from contract/jsconfig.json rename to tools/contract/jsconfig.json diff --git a/contract/package.json b/tools/contract/package.json similarity index 100% rename from contract/package.json rename to tools/contract/package.json diff --git a/contract/scripts/build-contract-deployer.js b/tools/contract/scripts/build-contract-deployer.js similarity index 100% rename from contract/scripts/build-contract-deployer.js rename to tools/contract/scripts/build-contract-deployer.js diff --git a/contract/scripts/build-proposal.sh b/tools/contract/scripts/build-proposal.sh similarity index 100% rename from contract/scripts/build-proposal.sh rename to tools/contract/scripts/build-proposal.sh diff --git a/contract/scripts/install-bundles.sh b/tools/contract/scripts/install-bundles.sh similarity index 100% rename from contract/scripts/install-bundles.sh rename to tools/contract/scripts/install-bundles.sh diff --git a/contract/scripts/parseProposals.mjs b/tools/contract/scripts/parseProposals.mjs similarity index 100% rename from contract/scripts/parseProposals.mjs rename to tools/contract/scripts/parseProposals.mjs diff --git a/contract/scripts/propose-start-contract.sh b/tools/contract/scripts/propose-start-contract.sh similarity index 100% rename from contract/scripts/propose-start-contract.sh rename to tools/contract/scripts/propose-start-contract.sh diff --git a/contract/scripts/run-chain.sh b/tools/contract/scripts/run-chain.sh similarity index 100% rename from contract/scripts/run-chain.sh rename to tools/contract/scripts/run-chain.sh diff --git a/contract/scripts/voteLatestProposalAndWait.sh b/tools/contract/scripts/voteLatestProposalAndWait.sh similarity index 100% rename from contract/scripts/voteLatestProposalAndWait.sh rename to tools/contract/scripts/voteLatestProposalAndWait.sh diff --git a/contract/src/debug.js b/tools/contract/src/debug.js similarity index 100% rename from contract/src/debug.js rename to tools/contract/src/debug.js diff --git a/contract/src/offer-up-proposal.js b/tools/contract/src/offer-up-proposal.js similarity index 100% rename from contract/src/offer-up-proposal.js rename to tools/contract/src/offer-up-proposal.js diff --git a/contract/src/offer-up.contract.js b/tools/contract/src/offer-up.contract.js similarity index 100% rename from contract/src/offer-up.contract.js rename to tools/contract/src/offer-up.contract.js diff --git a/contract/test/mintStable.js b/tools/contract/test/mintStable.js similarity index 100% rename from contract/test/mintStable.js rename to tools/contract/test/mintStable.js diff --git a/contract/test/prepare-test-env-ava.js b/tools/contract/test/prepare-test-env-ava.js similarity index 100% rename from contract/test/prepare-test-env-ava.js rename to tools/contract/test/prepare-test-env-ava.js diff --git a/contract/test/test-build-proposal.js b/tools/contract/test/test-build-proposal.js similarity index 100% rename from contract/test/test-build-proposal.js rename to tools/contract/test/test-build-proposal.js diff --git a/contract/test/test-bundle-source.js b/tools/contract/test/test-bundle-source.js similarity index 100% rename from contract/test/test-bundle-source.js rename to tools/contract/test/test-bundle-source.js diff --git a/contract/test/test-contract.js b/tools/contract/test/test-contract.js similarity index 100% rename from contract/test/test-contract.js rename to tools/contract/test/test-contract.js diff --git a/contract/test/utils.js b/tools/contract/test/utils.js similarity index 100% rename from contract/test/utils.js rename to tools/contract/test/utils.js diff --git a/ui/.eslintrc.cjs b/tools/ui/.eslintrc.cjs similarity index 100% rename from ui/.eslintrc.cjs rename to tools/ui/.eslintrc.cjs diff --git a/ui/index.html b/tools/ui/index.html similarity index 100% rename from ui/index.html rename to tools/ui/index.html diff --git a/ui/package.json b/tools/ui/package.json similarity index 100% rename from ui/package.json rename to tools/ui/package.json diff --git a/ui/public/agoric.svg b/tools/ui/public/agoric.svg similarity index 100% rename from ui/public/agoric.svg rename to tools/ui/public/agoric.svg diff --git a/ui/public/vite.svg b/tools/ui/public/vite.svg similarity index 100% rename from ui/public/vite.svg rename to tools/ui/public/vite.svg diff --git a/ui/src/App.css b/tools/ui/src/App.css similarity index 100% rename from ui/src/App.css rename to tools/ui/src/App.css diff --git a/ui/src/App.spec.tsx b/tools/ui/src/App.spec.tsx similarity index 100% rename from ui/src/App.spec.tsx rename to tools/ui/src/App.spec.tsx diff --git a/ui/src/App.tsx b/tools/ui/src/App.tsx similarity index 100% rename from ui/src/App.tsx rename to tools/ui/src/App.tsx diff --git a/ui/src/assets/IST.svg b/tools/ui/src/assets/IST.svg similarity index 100% rename from ui/src/assets/IST.svg rename to tools/ui/src/assets/IST.svg diff --git a/ui/src/assets/map.png b/tools/ui/src/assets/map.png similarity index 100% rename from ui/src/assets/map.png rename to tools/ui/src/assets/map.png diff --git a/ui/src/assets/potionBlue.png b/tools/ui/src/assets/potionBlue.png similarity index 100% rename from ui/src/assets/potionBlue.png rename to tools/ui/src/assets/potionBlue.png diff --git a/ui/src/assets/react.svg b/tools/ui/src/assets/react.svg similarity index 100% rename from ui/src/assets/react.svg rename to tools/ui/src/assets/react.svg diff --git a/ui/src/assets/scroll.png b/tools/ui/src/assets/scroll.png similarity index 100% rename from ui/src/assets/scroll.png rename to tools/ui/src/assets/scroll.png diff --git a/ui/src/components/Inventory.tsx b/tools/ui/src/components/Inventory.tsx similarity index 100% rename from ui/src/components/Inventory.tsx rename to tools/ui/src/components/Inventory.tsx diff --git a/ui/src/components/Logos.tsx b/tools/ui/src/components/Logos.tsx similarity index 100% rename from ui/src/components/Logos.tsx rename to tools/ui/src/components/Logos.tsx diff --git a/ui/src/components/Trade.tsx b/tools/ui/src/components/Trade.tsx similarity index 100% rename from ui/src/components/Trade.tsx rename to tools/ui/src/components/Trade.tsx diff --git a/ui/src/index.css b/tools/ui/src/index.css similarity index 100% rename from ui/src/index.css rename to tools/ui/src/index.css diff --git a/ui/src/index.d.ts b/tools/ui/src/index.d.ts similarity index 100% rename from ui/src/index.d.ts rename to tools/ui/src/index.d.ts diff --git a/ui/src/installSesLockdown.ts b/tools/ui/src/installSesLockdown.ts similarity index 100% rename from ui/src/installSesLockdown.ts rename to tools/ui/src/installSesLockdown.ts diff --git a/ui/src/main.tsx b/tools/ui/src/main.tsx similarity index 100% rename from ui/src/main.tsx rename to tools/ui/src/main.tsx diff --git a/ui/src/vite-env.d.ts b/tools/ui/src/vite-env.d.ts similarity index 100% rename from ui/src/vite-env.d.ts rename to tools/ui/src/vite-env.d.ts diff --git a/ui/test/App.e2e.ts b/tools/ui/test/App.e2e.ts similarity index 100% rename from ui/test/App.e2e.ts rename to tools/ui/test/App.e2e.ts diff --git a/ui/tsconfig.json b/tools/ui/tsconfig.json similarity index 100% rename from ui/tsconfig.json rename to tools/ui/tsconfig.json diff --git a/ui/tsconfig.node.json b/tools/ui/tsconfig.node.json similarity index 100% rename from ui/tsconfig.node.json rename to tools/ui/tsconfig.node.json diff --git a/ui/vite.config.ts b/tools/ui/vite.config.ts similarity index 100% rename from ui/vite.config.ts rename to tools/ui/vite.config.ts