From a3660bdcda1f6b096163b0c353778b0b4df303e4 Mon Sep 17 00:00:00 2001 From: Sophie Date: Mon, 18 Mar 2024 13:59:55 -0700 Subject: [PATCH 01/29] Add selectable example contracts --- README.md | 9 +- app/src/App.tsx | 2 +- app/src/constants.ts | 204 +++++++++++++++++- .../editor/components/ActionOverlay.tsx | 29 +-- .../editor/components/ExampleDropdown.tsx | 74 +++++++ .../editor/components/SolidityEditor.tsx | 3 +- .../features/editor/components/SwayEditor.tsx | 4 +- app/src/features/editor/hooks/useCompile.tsx | 2 +- app/src/utils/localStorage.ts | 6 +- 9 files changed, 293 insertions(+), 40 deletions(-) create mode 100644 app/src/features/editor/components/ExampleDropdown.tsx diff --git a/README.md b/README.md index 71a4200..1de1539 100644 --- a/README.md +++ b/README.md @@ -79,13 +79,10 @@ npm start This will open http://localhost:3000 in your browser. By default, it will use the production backend endpoint. -To test against the backend running locally, make this change in `app/src/constants.ts`: +To test against the backend running locally, you can use the environment variable `REACT_APP_LOCAL_SERVER` when you start the app, like this: -```diff -- export const SERVER_URI = 'https://api.sway-playground.org'; -- // export const SERVER_URI = 'http://0.0.0.0:8080'; -+ // export const SERVER_URI = 'https://api.sway-playground.org'; -+ export const SERVER_URI = 'http://0.0.0.0:8080'; +```sh +REACT_APP_LOCAL_SERVER=true npm start ``` ## Contributing to Sway diff --git a/app/src/App.tsx b/app/src/App.tsx index 15d6f0c..34a7bfa 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -133,7 +133,7 @@ function App() { style={{ marginRight: drawerOpen ? DRAWER_WIDTH : 0, transition: 'margin 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms', - height: 'calc(100vh - 90px)', + height: 'calc(100vh - 95px)', display: 'flex', flexDirection: 'column', }}> diff --git a/app/src/constants.ts b/app/src/constants.ts index 0fb808f..4efb904 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -1,10 +1,70 @@ +import { EditorLanguage } from './features/editor/components/ActionOverlay'; +import { ExampleMenuItem } from './features/editor/components/ExampleDropdown'; + export const FUEL_GREEN = '#00f58c'; -// TODO: Determine the URL based on the NODE_ENV. -export const SERVER_URI = 'https://api.sway-playground.org'; -// export const SERVER_URI = 'http://0.0.0.0:8080'; +export const SERVER_URI = process.env.REACT_APP_LOCAL_SERVER + ? 'http://0.0.0.0:8080' + : 'https://api.sway-playground.org'; + +export const EXAMPLE_SWAY_CONTRACT_COUNTER = `contract; + +abi Counter { + #[storage(read, write)] + fn increment(amount: u64) -> u64; + + #[storage(read)] + fn get() -> u64; +} + +storage { + counter: u64 = 0, +} + +impl Counter for Contract { + #[storage(read, write)] + fn increment(amount: u64) -> u64 { + let incremented = storage.counter.read() + amount; + storage.counter.write(incremented); + incremented + } + + #[storage(read)] + fn get() -> u64 { + storage.counter.read() + } +}`; + +// TODO +export const EXAMPLE_SWAY_CONTRACT_2 = `contract; + +abi Counter { + #[storage(read, write)] + fn increment(amount: u64) -> u64; + + #[storage(read)] + fn get() -> u64; +} + +storage { + counter: u64 = 0, +} + +impl Counter for Contract { + #[storage(read, write)] + fn increment(amount: u64) -> u64 { + let incremented = storage.counter.read() + amount; + storage.counter.write(incremented); + incremented + } + + #[storage(read)] + fn get() -> u64 { + storage.counter.read() + } +}`; -export const DEFAULT_SWAY_CONTRACT = `contract; +export const EXAMPLE_SWAY_CONTRACT_3 = `contract; abi Counter { #[storage(read, write)] @@ -32,7 +92,124 @@ impl Counter for Contract { } }`; -export const DEFAULT_SOLIDITY_CONTRACT = `pragma solidity ^0.8.24; +export const EXAMPLE_SOLIDITY_CONTRACT_COUNTER = `pragma solidity ^0.8.24; + +contract Counter { + uint64 count; + + function get() public view returns (uint64) { + return count; + } + + function increment() public { + count += 1; + } +}`; + +export const EXAMPLE_SOLIDITY_CONTRACT_ERC20_TOKEN = `pragma solidity ^0.8.24; +/// token.sol -- ERC20 implementation with minting and burning. +/// Based on DSToken contract with many modifications. + +contract ERC20 { + address public owner; + bool public stopped; + uint256 public totalSupply; + mapping (address => uint256) public balanceOf; + mapping (address => mapping (address => uint256)) public allowance; + string public symbol; + uint8 public decimals = 18; // standard token precision. override to customize + string public name = ""; // Optional token name + + constructor(string memory symbol_) public { + symbol = symbol_; + owner = msg.sender; + } + + event Approval(address indexed src, address indexed guy, uint wad); + event Transfer(address indexed src, address indexed dst, uint wad); + event Mint(address indexed guy, uint wad); + event Burn(address indexed guy, uint wad); + event Stop(); + event Start(); + + function approve(address guy, uint wad) public returns (bool) { + require(!stopped, "ds-stop-is-stopped"); + allowance[msg.sender][guy] = wad; + + emit Approval(msg.sender, guy, wad); + + return true; + } + + function transfer(address dst, uint wad) external returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint wad) + public + returns (bool) + { + require(!stopped, "ds-stop-is-stopped"); + if (src != msg.sender && allowance[src][msg.sender] != 0xFFFFFFFFFFFFFFFF) { + require(allowance[src][msg.sender] >= wad, "ds-token-insufficient-approval"); + allowance[src][msg.sender] = allowance[src][msg.sender] - wad; + } + + require(balanceOf[src] >= wad, "ds-token-insufficient-balance"); + balanceOf[src] = balanceOf[src] - wad; + balanceOf[dst] = balanceOf[dst] + wad; + + emit Transfer(src, dst, wad); + + return true; + } + + function mint(address guy, uint wad) public { + require(!stopped, "ds-stop-is-stopped"); + require(msg.sender == owner, "ds-auth"); + balanceOf[guy] = balanceOf[guy] + wad; + totalSupply = totalSupply + wad; + emit Mint(guy, wad); + } + + function burn(address guy, uint wad) public { + require(!stopped, "ds-stop-is-stopped"); + require(msg.sender == owner, "ds-auth"); + if (guy != msg.sender && allowance[guy][msg.sender] != 0xFFFFFFFFFFFFFFFF) { + require(allowance[guy][msg.sender] >= wad, "ds-token-insufficient-approval"); + allowance[guy][msg.sender] = allowance[guy][msg.sender] - wad; + } + + require(balanceOf[guy] >= wad, "ds-token-insufficient-balance"); + balanceOf[guy] = balanceOf[guy] - wad; + totalSupply = totalSupply - wad; + emit Burn(guy, wad); + } + + function stop() public { + require(msg.sender == owner, "ds-auth"); + stopped = true; + emit Stop(); + } + + function start() public { + require(msg.sender == owner, "ds-auth"); + stopped = false; + emit Start(); + } + + function setName(string memory name_) public { + require(msg.sender == owner, "ds-auth"); + name = name_; + } + + function transferOwnership(address owner_) public { + require(msg.sender == owner, "ds-auth"); + owner = owner_; + } +}`; + +export const EXAMPLE_SOLIDITY_CONTRACT_3 = `pragma solidity ^0.8.24; contract Counter { uint64 count; @@ -45,3 +222,20 @@ contract Counter { count += 1; } }`; + +export const EXAMPLE_SWAY_CONTRACTS: ExampleMenuItem[] = [ + { label: 'Counter', code: EXAMPLE_SWAY_CONTRACT_COUNTER }, + { label: 'Example 2', code: EXAMPLE_SWAY_CONTRACT_2 }, + { label: 'Example 3', code: EXAMPLE_SWAY_CONTRACT_3 }, +]; + +export const EXAMPLE_SOLIDITY_CONTRACTS: ExampleMenuItem[] = [ + { label: 'Counter', code: EXAMPLE_SOLIDITY_CONTRACT_COUNTER }, + { label: 'ERC20 Token', code: EXAMPLE_SOLIDITY_CONTRACT_ERC20_TOKEN }, + { label: 'Example 3', code: EXAMPLE_SOLIDITY_CONTRACT_3 }, +]; + +export const EXAMPLE_CONTRACTS: Record = { + sway: EXAMPLE_SWAY_CONTRACTS, + solidity: EXAMPLE_SOLIDITY_CONTRACTS, +}; diff --git a/app/src/features/editor/components/ActionOverlay.tsx b/app/src/features/editor/components/ActionOverlay.tsx index 07554b4..b088f38 100644 --- a/app/src/features/editor/components/ActionOverlay.tsx +++ b/app/src/features/editor/components/ActionOverlay.tsx @@ -1,19 +1,22 @@ import React from 'react'; -import IconButton from '@mui/material/IconButton'; -import Delete from '@mui/icons-material/Delete'; import ToolchainDropdown, { Toolchain } from './ToolchainDropdown'; -import Tooltip from '@mui/material/Tooltip'; +import ExampleDropdown from './ExampleDropdown'; +import { EXAMPLE_CONTRACTS } from '../../../constants'; + +export type EditorLanguage = 'sway' | 'solidity'; export interface ActionOverlayProps { - handleReset: () => void; + handleSelectExample: (example: string) => void; toolchain?: Toolchain; setToolchain?: (toolchain: Toolchain) => void; + editorLanguage: EditorLanguage; } function ActionOverlay({ - handleReset, + handleSelectExample, toolchain, setToolchain, + editorLanguage }: ActionOverlayProps) { return (
@@ -35,21 +38,7 @@ function ActionOverlay({ toolchain={toolchain} setToolchain={setToolchain} />} -
- - - - - -
+
); diff --git a/app/src/features/editor/components/ExampleDropdown.tsx b/app/src/features/editor/components/ExampleDropdown.tsx new file mode 100644 index 0000000..0d3766e --- /dev/null +++ b/app/src/features/editor/components/ExampleDropdown.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import Tooltip from '@mui/material/Tooltip'; +import MenuItem from '@mui/material/MenuItem'; +import IconButton from '@mui/material/IconButton'; +import FileOpen from '@mui/icons-material/FileOpen'; +import { Dropdown } from '@mui/base/Dropdown/Dropdown'; +import Menu from '@mui/material/Menu/Menu'; + +export interface ExampleMenuItem { + label: string; + code: string; +} + +export interface ExampleDropdownProps { + handleSelect: (example: string) => void; + examples: ExampleMenuItem[]; + style?: React.CSSProperties; +} + +function ExampleDropdown({ handleSelect, examples, style }: ExampleDropdownProps) { + const [anchorEl, setAnchorEl] = React.useState(null); + + const onButtonClick = (event: any ) => { // todo + console.log('click', event.target); + setAnchorEl(event.target); + }; + + const onItemClick = (code: string) => { // todo + console.log(code); + // console.log('item click', event.target.value); + handleClose(); + // handleSelect(event.target.value); + handleSelect(code); + }; + + + const handleClose = () => { + console.log('close'); + setAnchorEl(null); + } + + return ( +
+ + + + + + + + + + + + {examples.map(({label, code}: ExampleMenuItem, index) => ( onItemClick(code)} >{label})) } + {/* Profile + Language settings + Log out */} + + +
+ ); +} + +export default ExampleDropdown; diff --git a/app/src/features/editor/components/SolidityEditor.tsx b/app/src/features/editor/components/SolidityEditor.tsx index 7d4ab45..a22b3aa 100644 --- a/app/src/features/editor/components/SolidityEditor.tsx +++ b/app/src/features/editor/components/SolidityEditor.tsx @@ -6,7 +6,6 @@ import 'ace-builds/src-noconflict/theme-chrome'; import { StyledBorder } from '../../../components/shared'; import 'ace-mode-solidity/build/remix-ide/mode-solidity'; import ActionOverlay from './ActionOverlay'; -import { DEFAULT_SOLIDITY_CONTRACT } from '../../../constants'; import { useIsMobile } from '../../../hooks/useIsMobile'; export interface SolidityEditorProps { @@ -24,7 +23,7 @@ function SolidityEditor({ code, onChange }: SolidityEditorProps) { marginRight: isMobile ? 0 : '1rem', marginBottom: isMobile ? '1rem' : 0, }}> - onChange(DEFAULT_SOLIDITY_CONTRACT)} /> + onChange(DEFAULT_SWAY_CONTRACT)} + handleSelectExample={onChange} toolchain={toolchain} setToolchain={setToolchain} + editorLanguage='sway' /> Date: Mon, 18 Mar 2024 14:55:46 -0700 Subject: [PATCH 02/29] Update solidity examples --- app/src/constants.ts | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/app/src/constants.ts b/app/src/constants.ts index 4efb904..c6907bc 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -92,7 +92,8 @@ impl Counter for Contract { } }`; -export const EXAMPLE_SOLIDITY_CONTRACT_COUNTER = `pragma solidity ^0.8.24; +export const EXAMPLE_SOLIDITY_CONTRACT_COUNTER = `// no support for delegatecall, this, strings & ASM blocks. +pragma solidity ^0.8.24; contract Counter { uint64 count; @@ -106,7 +107,7 @@ contract Counter { } }`; -export const EXAMPLE_SOLIDITY_CONTRACT_ERC20_TOKEN = `pragma solidity ^0.8.24; +export const EXAMPLE_SOLIDITY_CONTRACT_ERC20 = `pragma solidity ^0.8.24; /// token.sol -- ERC20 implementation with minting and burning. /// Based on DSToken contract with many modifications. @@ -209,30 +210,41 @@ contract ERC20 { } }`; -export const EXAMPLE_SOLIDITY_CONTRACT_3 = `pragma solidity ^0.8.24; +export const EXAMPLE_SOLIDITY_CONTRACT_VOTING = `pragma solidity ^0.8.24; -contract Counter { - uint64 count; - - function get() public view returns (uint64) { - return count; +contract Voting { + uint64 public numVoters; + mapping(bytes32 => uint256) public votes; + mapping(address => mapping(bytes32 => bool)) public hasVoted; + mapping(address => bool) public voters; + + constructor(address[] memory voters_) public { + for (numVoters = 0; numVoters < voters_.length; numVoters++) { + voters[voters_[numVoters]] = true; + } } + + function vote(bytes32 topic) public returns (bool) { + require(voters[msg.sender], "is-voter"); + require(hasVoted[msg.sender][topic] == false, "has-voted"); + + votes[topic] += 1; + hasVoted[msg.sender][topic] = true; - function increment() public { - count += 1; + return true; } }`; export const EXAMPLE_SWAY_CONTRACTS: ExampleMenuItem[] = [ - { label: 'Counter', code: EXAMPLE_SWAY_CONTRACT_COUNTER }, + { label: 'counter.sw', code: EXAMPLE_SWAY_CONTRACT_COUNTER }, { label: 'Example 2', code: EXAMPLE_SWAY_CONTRACT_2 }, { label: 'Example 3', code: EXAMPLE_SWAY_CONTRACT_3 }, ]; export const EXAMPLE_SOLIDITY_CONTRACTS: ExampleMenuItem[] = [ - { label: 'Counter', code: EXAMPLE_SOLIDITY_CONTRACT_COUNTER }, - { label: 'ERC20 Token', code: EXAMPLE_SOLIDITY_CONTRACT_ERC20_TOKEN }, - { label: 'Example 3', code: EXAMPLE_SOLIDITY_CONTRACT_3 }, + { label: 'Counter.sol', code: EXAMPLE_SOLIDITY_CONTRACT_COUNTER }, + { label: 'ERC20.sol', code: EXAMPLE_SOLIDITY_CONTRACT_ERC20 }, + { label: 'Voting.sol', code: EXAMPLE_SOLIDITY_CONTRACT_VOTING }, ]; export const EXAMPLE_CONTRACTS: Record = { From fe1a445c5370ff195700ae4b7d16e8ffed0842fe Mon Sep 17 00:00:00 2001 From: Sophie Date: Mon, 18 Mar 2024 17:06:18 -0700 Subject: [PATCH 03/29] src20 example --- app/src/constants.ts | 140 ++++++++++++++++-- .../editor/components/ExampleDropdown.tsx | 80 +++++----- projects/swaypad/Forc.lock | 26 +++- projects/swaypad/Forc.toml | 3 + 4 files changed, 191 insertions(+), 58 deletions(-) diff --git a/app/src/constants.ts b/app/src/constants.ts index c6907bc..0b77aea 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -36,31 +36,141 @@ impl Counter for Contract { }`; // TODO -export const EXAMPLE_SWAY_CONTRACT_2 = `contract; +export const EXAMPLE_SWAY_CONTRACT_SRC20 = `contract; + +use src3::SRC3; +use src5::{SRC5, State, AccessError}; +use src20::SRC20; +use std::{ + asset::{ + burn, + mint_to, + }, + call_frames::{ + contract_id, + msg_asset_id, + }, + constants::DEFAULT_SUB_ID, + context::msg_amount, + string::String, +}; -abi Counter { +abi Constructor { #[storage(read, write)] - fn increment(amount: u64) -> u64; + fn constructor(owner_: Identity); +} - #[storage(read)] - fn get() -> u64; +configurable { + /// The decimals of the asset minted by this contract. + DECIMALS: u8 = 9u8, + /// The name of the asset minted by this contract. + NAME: str[7] = __to_str_array("MyAsset"), + /// The symbol of the asset minted by this contract. + SYMBOL: str[5] = __to_str_array("MYTKN"), } storage { - counter: u64 = 0, + /// The total supply of the asset minted by this contract. + total_supply: u64 = 0, + + /// Owner. + owner: State = State::Uninitialized, } -impl Counter for Contract { +impl SRC20 for Contract { + #[storage(read)] + fn total_assets() -> u64 { + 1 + } + + #[storage(read)] + fn total_supply(asset: AssetId) -> Option { + if asset == AssetId::default() { + Some(storage.total_supply.read()) + } else { + None + } + } + + #[storage(read)] + fn name(asset: AssetId) -> Option { + if asset == AssetId::default() { + Some(String::from_ascii_str(from_str_array(NAME))) + } else { + None + } + } + + #[storage(read)] + fn symbol(asset: AssetId) -> Option { + if asset == AssetId::default() { + Some(String::from_ascii_str(from_str_array(SYMBOL))) + } else { + None + } + } + + #[storage(read)] + fn decimals(asset: AssetId) -> Option { + if asset == AssetId::default() { + Some(DECIMALS) + } else { + None + } + } +} + +#[storage(read)] +fn is_owner() { + require( + storage.owner.read() == State::Initialized(msg_sender().unwrap()), + AccessError::NotOwner, + ); +} + +impl Constructor for Contract { #[storage(read, write)] - fn increment(amount: u64) -> u64 { - let incremented = storage.counter.read() + amount; - storage.counter.write(incremented); - incremented + fn constructor(owner_: Identity) { + require(storage.owner.read() == State::Uninitialized, "owner-initialized"); + storage.owner.write(State::Initialized(owner_)); } +} +impl SRC5 for Contract { #[storage(read)] - fn get() -> u64 { - storage.counter.read() + fn owner() -> State { + storage.owner.read() + } +} + +impl SRC3 for Contract { + #[storage(read, write)] + fn mint(recipient: Identity, sub_id: SubId, amount: u64) { + require(sub_id == DEFAULT_SUB_ID, "Incorrect Sub Id"); + is_owner(); + + // Increment total supply of the asset and mint to the recipient. + storage + .total_supply + .write(amount + storage.total_supply.read()); + mint_to(recipient, DEFAULT_SUB_ID, amount); + } + + #[storage(read, write)] + fn burn(sub_id: SubId, amount: u64) { + require(sub_id == DEFAULT_SUB_ID, "Incorrect Sub Id"); + require(msg_amount() >= amount, "Incorrect amount provided"); + require( + msg_asset_id() == AssetId::default(), + "Incorrect asset provided", + ); + is_owner(); + + // Decrement total supply of the asset and burn. + storage + .total_supply + .write(storage.total_supply.read() - amount); + burn(DEFAULT_SUB_ID, amount); } }`; @@ -236,8 +346,8 @@ contract Voting { }`; export const EXAMPLE_SWAY_CONTRACTS: ExampleMenuItem[] = [ - { label: 'counter.sw', code: EXAMPLE_SWAY_CONTRACT_COUNTER }, - { label: 'Example 2', code: EXAMPLE_SWAY_CONTRACT_2 }, + { label: 'Counter.sw', code: EXAMPLE_SWAY_CONTRACT_COUNTER }, + { label: 'SRC20.sw', code: EXAMPLE_SWAY_CONTRACT_SRC20 }, { label: 'Example 3', code: EXAMPLE_SWAY_CONTRACT_3 }, ]; diff --git a/app/src/features/editor/components/ExampleDropdown.tsx b/app/src/features/editor/components/ExampleDropdown.tsx index 0d3766e..2f22e6d 100644 --- a/app/src/features/editor/components/ExampleDropdown.tsx +++ b/app/src/features/editor/components/ExampleDropdown.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import Tooltip from '@mui/material/Tooltip'; import MenuItem from '@mui/material/MenuItem'; import IconButton from '@mui/material/IconButton'; @@ -17,56 +17,56 @@ export interface ExampleDropdownProps { style?: React.CSSProperties; } -function ExampleDropdown({ handleSelect, examples, style }: ExampleDropdownProps) { +function ExampleDropdown({ + handleSelect, + examples, + style, +}: ExampleDropdownProps) { const [anchorEl, setAnchorEl] = React.useState(null); - const onButtonClick = (event: any ) => { // todo - console.log('click', event.target); + const onButtonClick = useCallback((event: any) => { setAnchorEl(event.target); - }; + }, [setAnchorEl]); - const onItemClick = (code: string) => { // todo - console.log(code); - // console.log('item click', event.target.value); + const handleClose = useCallback(() => { + setAnchorEl(null); + }, [setAnchorEl]); + + const onItemClick = useCallback((code: string) => { handleClose(); - // handleSelect(event.target.value); handleSelect(code); - }; - - - const handleClose = () => { - console.log('close'); - setAnchorEl(null); - } + }, [handleSelect, handleClose]); return (
- - - - - - - - + + + + + - - {examples.map(({label, code}: ExampleMenuItem, index) => ( onItemClick(code)} >{label})) } - {/* Profile - Language settings - Log out */} - - + + {examples.map(({ label, code }: ExampleMenuItem, index) => ( + onItemClick(code)}> + {label} + + ))} + +
); } diff --git a/projects/swaypad/Forc.lock b/projects/swaypad/Forc.lock index 970f608..97e0647 100644 --- a/projects/swaypad/Forc.lock +++ b/projects/swaypad/Forc.lock @@ -1,13 +1,33 @@ [[package]] name = "core" -source = "path+from-root-63B5D95B29B128A3" +source = "path+from-root-4F1FAAE4510FEB23" + +[[package]] +name = "src20" +source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.3.3#4198b4b07449ad16104cc8a0501f3013670fdcfd" +dependencies = ["std"] + +[[package]] +name = "src3" +source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.3.3#4198b4b07449ad16104cc8a0501f3013670fdcfd" +dependencies = ["std"] + +[[package]] +name = "src5" +source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.3.3#4198b4b07449ad16104cc8a0501f3013670fdcfd" +dependencies = ["std"] [[package]] name = "std" -source = "git+https://github.com/fuellabs/sway?tag=v0.45.0#92dc9f361a9508a940c0d0708130f26fa044f6b3" +source = "git+https://github.com/fuellabs/sway?tag=v0.51.1#d1e8f019c1da46273c3d3a72b385ce356ba2bc20" dependencies = ["core"] [[package]] name = "swaypad" source = "member" -dependencies = ["std"] +dependencies = [ + "src20", + "src3", + "src5", + "std", +] diff --git a/projects/swaypad/Forc.toml b/projects/swaypad/Forc.toml index bf41688..0c20011 100644 --- a/projects/swaypad/Forc.toml +++ b/projects/swaypad/Forc.toml @@ -5,3 +5,6 @@ license = "Apache-2.0" name = "swaypad" [dependencies] +src3 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } +src5 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } +src20 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } From 2a6fc64488eae333a296339a47f2d85bf87b5416 Mon Sep 17 00:00:00 2001 From: Sophie Date: Mon, 18 Mar 2024 17:47:24 -0700 Subject: [PATCH 04/29] update src20 example --- app/src/constants.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/src/constants.ts b/app/src/constants.ts index 0b77aea..8d45d56 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -120,14 +120,6 @@ impl SRC20 for Contract { } } -#[storage(read)] -fn is_owner() { - require( - storage.owner.read() == State::Initialized(msg_sender().unwrap()), - AccessError::NotOwner, - ); -} - impl Constructor for Contract { #[storage(read, write)] fn constructor(owner_: Identity) { From 35702ec9d00c6dcf64e736ac3d23cca952733809 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 13:27:00 -0700 Subject: [PATCH 05/29] update examples --- app/package-lock.json | 26 +- app/package.json | 1 + app/src/constants.ts | 347 ------------------ .../editor/components/ActionOverlay.tsx | 2 +- app/src/features/editor/examples/index.ts | 10 + .../editor/examples/solidity/counter.ts | 14 + .../editor/examples/solidity/erc20.ts | 102 +++++ .../editor/examples/solidity/index.ts | 11 + .../editor/examples/solidity/voting.ts | 24 ++ .../features/editor/examples/sway/counter.ts | 27 ++ .../features/editor/examples/sway/index.ts | 9 + .../features/editor/examples/sway/src20.ts | 134 +++++++ app/src/utils/localStorage.ts | 7 +- projects/swaypad/Forc.lock | 14 + projects/swaypad/Forc.toml | 1 + 15 files changed, 375 insertions(+), 354 deletions(-) create mode 100644 app/src/features/editor/examples/index.ts create mode 100644 app/src/features/editor/examples/solidity/counter.ts create mode 100644 app/src/features/editor/examples/solidity/erc20.ts create mode 100644 app/src/features/editor/examples/solidity/index.ts create mode 100644 app/src/features/editor/examples/solidity/voting.ts create mode 100644 app/src/features/editor/examples/sway/counter.ts create mode 100644 app/src/features/editor/examples/sway/index.ts create mode 100644 app/src/features/editor/examples/sway/src20.ts diff --git a/app/package-lock.json b/app/package-lock.json index 25c54fa..e16f1e4 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -30,6 +30,7 @@ "web-vitals": "^2.1.4" }, "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -856,9 +857,17 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, "engines": { "node": ">=6.9.0" }, @@ -2101,6 +2110,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-env/node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", diff --git a/app/package.json b/app/package.json index 6e0e4b4..b69cdb7 100644 --- a/app/package.json +++ b/app/package.json @@ -25,6 +25,7 @@ "web-vitals": "^2.1.4" }, "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/app/src/constants.ts b/app/src/constants.ts index 8d45d56..3d95fdf 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -6,350 +6,3 @@ export const FUEL_GREEN = '#00f58c'; export const SERVER_URI = process.env.REACT_APP_LOCAL_SERVER ? 'http://0.0.0.0:8080' : 'https://api.sway-playground.org'; - -export const EXAMPLE_SWAY_CONTRACT_COUNTER = `contract; - -abi Counter { - #[storage(read, write)] - fn increment(amount: u64) -> u64; - - #[storage(read)] - fn get() -> u64; -} - -storage { - counter: u64 = 0, -} - -impl Counter for Contract { - #[storage(read, write)] - fn increment(amount: u64) -> u64 { - let incremented = storage.counter.read() + amount; - storage.counter.write(incremented); - incremented - } - - #[storage(read)] - fn get() -> u64 { - storage.counter.read() - } -}`; - -// TODO -export const EXAMPLE_SWAY_CONTRACT_SRC20 = `contract; - -use src3::SRC3; -use src5::{SRC5, State, AccessError}; -use src20::SRC20; -use std::{ - asset::{ - burn, - mint_to, - }, - call_frames::{ - contract_id, - msg_asset_id, - }, - constants::DEFAULT_SUB_ID, - context::msg_amount, - string::String, -}; - -abi Constructor { - #[storage(read, write)] - fn constructor(owner_: Identity); -} - -configurable { - /// The decimals of the asset minted by this contract. - DECIMALS: u8 = 9u8, - /// The name of the asset minted by this contract. - NAME: str[7] = __to_str_array("MyAsset"), - /// The symbol of the asset minted by this contract. - SYMBOL: str[5] = __to_str_array("MYTKN"), -} - -storage { - /// The total supply of the asset minted by this contract. - total_supply: u64 = 0, - - /// Owner. - owner: State = State::Uninitialized, -} - -impl SRC20 for Contract { - #[storage(read)] - fn total_assets() -> u64 { - 1 - } - - #[storage(read)] - fn total_supply(asset: AssetId) -> Option { - if asset == AssetId::default() { - Some(storage.total_supply.read()) - } else { - None - } - } - - #[storage(read)] - fn name(asset: AssetId) -> Option { - if asset == AssetId::default() { - Some(String::from_ascii_str(from_str_array(NAME))) - } else { - None - } - } - - #[storage(read)] - fn symbol(asset: AssetId) -> Option { - if asset == AssetId::default() { - Some(String::from_ascii_str(from_str_array(SYMBOL))) - } else { - None - } - } - - #[storage(read)] - fn decimals(asset: AssetId) -> Option { - if asset == AssetId::default() { - Some(DECIMALS) - } else { - None - } - } -} - -impl Constructor for Contract { - #[storage(read, write)] - fn constructor(owner_: Identity) { - require(storage.owner.read() == State::Uninitialized, "owner-initialized"); - storage.owner.write(State::Initialized(owner_)); - } -} - -impl SRC5 for Contract { - #[storage(read)] - fn owner() -> State { - storage.owner.read() - } -} - -impl SRC3 for Contract { - #[storage(read, write)] - fn mint(recipient: Identity, sub_id: SubId, amount: u64) { - require(sub_id == DEFAULT_SUB_ID, "Incorrect Sub Id"); - is_owner(); - - // Increment total supply of the asset and mint to the recipient. - storage - .total_supply - .write(amount + storage.total_supply.read()); - mint_to(recipient, DEFAULT_SUB_ID, amount); - } - - #[storage(read, write)] - fn burn(sub_id: SubId, amount: u64) { - require(sub_id == DEFAULT_SUB_ID, "Incorrect Sub Id"); - require(msg_amount() >= amount, "Incorrect amount provided"); - require( - msg_asset_id() == AssetId::default(), - "Incorrect asset provided", - ); - is_owner(); - - // Decrement total supply of the asset and burn. - storage - .total_supply - .write(storage.total_supply.read() - amount); - burn(DEFAULT_SUB_ID, amount); - } -}`; - -export const EXAMPLE_SWAY_CONTRACT_3 = `contract; - -abi Counter { - #[storage(read, write)] - fn increment(amount: u64) -> u64; - - #[storage(read)] - fn get() -> u64; -} - -storage { - counter: u64 = 0, -} - -impl Counter for Contract { - #[storage(read, write)] - fn increment(amount: u64) -> u64 { - let incremented = storage.counter.read() + amount; - storage.counter.write(incremented); - incremented - } - - #[storage(read)] - fn get() -> u64 { - storage.counter.read() - } -}`; - -export const EXAMPLE_SOLIDITY_CONTRACT_COUNTER = `// no support for delegatecall, this, strings & ASM blocks. -pragma solidity ^0.8.24; - -contract Counter { - uint64 count; - - function get() public view returns (uint64) { - return count; - } - - function increment() public { - count += 1; - } -}`; - -export const EXAMPLE_SOLIDITY_CONTRACT_ERC20 = `pragma solidity ^0.8.24; -/// token.sol -- ERC20 implementation with minting and burning. -/// Based on DSToken contract with many modifications. - -contract ERC20 { - address public owner; - bool public stopped; - uint256 public totalSupply; - mapping (address => uint256) public balanceOf; - mapping (address => mapping (address => uint256)) public allowance; - string public symbol; - uint8 public decimals = 18; // standard token precision. override to customize - string public name = ""; // Optional token name - - constructor(string memory symbol_) public { - symbol = symbol_; - owner = msg.sender; - } - - event Approval(address indexed src, address indexed guy, uint wad); - event Transfer(address indexed src, address indexed dst, uint wad); - event Mint(address indexed guy, uint wad); - event Burn(address indexed guy, uint wad); - event Stop(); - event Start(); - - function approve(address guy, uint wad) public returns (bool) { - require(!stopped, "ds-stop-is-stopped"); - allowance[msg.sender][guy] = wad; - - emit Approval(msg.sender, guy, wad); - - return true; - } - - function transfer(address dst, uint wad) external returns (bool) { - return transferFrom(msg.sender, dst, wad); - } - - function transferFrom(address src, address dst, uint wad) - public - returns (bool) - { - require(!stopped, "ds-stop-is-stopped"); - if (src != msg.sender && allowance[src][msg.sender] != 0xFFFFFFFFFFFFFFFF) { - require(allowance[src][msg.sender] >= wad, "ds-token-insufficient-approval"); - allowance[src][msg.sender] = allowance[src][msg.sender] - wad; - } - - require(balanceOf[src] >= wad, "ds-token-insufficient-balance"); - balanceOf[src] = balanceOf[src] - wad; - balanceOf[dst] = balanceOf[dst] + wad; - - emit Transfer(src, dst, wad); - - return true; - } - - function mint(address guy, uint wad) public { - require(!stopped, "ds-stop-is-stopped"); - require(msg.sender == owner, "ds-auth"); - balanceOf[guy] = balanceOf[guy] + wad; - totalSupply = totalSupply + wad; - emit Mint(guy, wad); - } - - function burn(address guy, uint wad) public { - require(!stopped, "ds-stop-is-stopped"); - require(msg.sender == owner, "ds-auth"); - if (guy != msg.sender && allowance[guy][msg.sender] != 0xFFFFFFFFFFFFFFFF) { - require(allowance[guy][msg.sender] >= wad, "ds-token-insufficient-approval"); - allowance[guy][msg.sender] = allowance[guy][msg.sender] - wad; - } - - require(balanceOf[guy] >= wad, "ds-token-insufficient-balance"); - balanceOf[guy] = balanceOf[guy] - wad; - totalSupply = totalSupply - wad; - emit Burn(guy, wad); - } - - function stop() public { - require(msg.sender == owner, "ds-auth"); - stopped = true; - emit Stop(); - } - - function start() public { - require(msg.sender == owner, "ds-auth"); - stopped = false; - emit Start(); - } - - function setName(string memory name_) public { - require(msg.sender == owner, "ds-auth"); - name = name_; - } - - function transferOwnership(address owner_) public { - require(msg.sender == owner, "ds-auth"); - owner = owner_; - } -}`; - -export const EXAMPLE_SOLIDITY_CONTRACT_VOTING = `pragma solidity ^0.8.24; - -contract Voting { - uint64 public numVoters; - mapping(bytes32 => uint256) public votes; - mapping(address => mapping(bytes32 => bool)) public hasVoted; - mapping(address => bool) public voters; - - constructor(address[] memory voters_) public { - for (numVoters = 0; numVoters < voters_.length; numVoters++) { - voters[voters_[numVoters]] = true; - } - } - - function vote(bytes32 topic) public returns (bool) { - require(voters[msg.sender], "is-voter"); - require(hasVoted[msg.sender][topic] == false, "has-voted"); - - votes[topic] += 1; - hasVoted[msg.sender][topic] = true; - - return true; - } -}`; - -export const EXAMPLE_SWAY_CONTRACTS: ExampleMenuItem[] = [ - { label: 'Counter.sw', code: EXAMPLE_SWAY_CONTRACT_COUNTER }, - { label: 'SRC20.sw', code: EXAMPLE_SWAY_CONTRACT_SRC20 }, - { label: 'Example 3', code: EXAMPLE_SWAY_CONTRACT_3 }, -]; - -export const EXAMPLE_SOLIDITY_CONTRACTS: ExampleMenuItem[] = [ - { label: 'Counter.sol', code: EXAMPLE_SOLIDITY_CONTRACT_COUNTER }, - { label: 'ERC20.sol', code: EXAMPLE_SOLIDITY_CONTRACT_ERC20 }, - { label: 'Voting.sol', code: EXAMPLE_SOLIDITY_CONTRACT_VOTING }, -]; - -export const EXAMPLE_CONTRACTS: Record = { - sway: EXAMPLE_SWAY_CONTRACTS, - solidity: EXAMPLE_SOLIDITY_CONTRACTS, -}; diff --git a/app/src/features/editor/components/ActionOverlay.tsx b/app/src/features/editor/components/ActionOverlay.tsx index b088f38..00a3c2f 100644 --- a/app/src/features/editor/components/ActionOverlay.tsx +++ b/app/src/features/editor/components/ActionOverlay.tsx @@ -1,7 +1,7 @@ import React from 'react'; import ToolchainDropdown, { Toolchain } from './ToolchainDropdown'; import ExampleDropdown from './ExampleDropdown'; -import { EXAMPLE_CONTRACTS } from '../../../constants'; +import { EXAMPLE_CONTRACTS } from '../examples'; export type EditorLanguage = 'sway' | 'solidity'; diff --git a/app/src/features/editor/examples/index.ts b/app/src/features/editor/examples/index.ts new file mode 100644 index 0000000..0dbb0a3 --- /dev/null +++ b/app/src/features/editor/examples/index.ts @@ -0,0 +1,10 @@ +import { EditorLanguage } from "../components/ActionOverlay"; +import { ExampleMenuItem } from "../components/ExampleDropdown"; +import { EXAMPLE_SOLIDITY_CONTRACTS } from "./solidity"; +import { EXAMPLE_SWAY_CONTRACTS } from "./sway"; + +export const EXAMPLE_CONTRACTS: Record = { + sway: EXAMPLE_SWAY_CONTRACTS, + solidity: EXAMPLE_SOLIDITY_CONTRACTS, + }; + \ No newline at end of file diff --git a/app/src/features/editor/examples/solidity/counter.ts b/app/src/features/editor/examples/solidity/counter.ts new file mode 100644 index 0000000..f6ca479 --- /dev/null +++ b/app/src/features/editor/examples/solidity/counter.ts @@ -0,0 +1,14 @@ +export const EXAMPLE_SOLIDITY_CONTRACT_COUNTER = `// no support for delegatecall, this, strings & ASM blocks. +pragma solidity ^0.8.24; + +contract Counter { + uint64 count; + + function get() public view returns (uint64) { + return count; + } + + function increment() public { + count += 1; + } +}`; \ No newline at end of file diff --git a/app/src/features/editor/examples/solidity/erc20.ts b/app/src/features/editor/examples/solidity/erc20.ts new file mode 100644 index 0000000..55f6464 --- /dev/null +++ b/app/src/features/editor/examples/solidity/erc20.ts @@ -0,0 +1,102 @@ +export const EXAMPLE_SOLIDITY_CONTRACT_ERC20 = `pragma solidity ^0.8.24; +/// token.sol -- ERC20 implementation with minting and burning. +/// Based on DSToken contract with many modifications. + +contract ERC20 { + address public owner; + bool public stopped; + uint256 public totalSupply; + mapping (address => uint256) public balanceOf; + mapping (address => mapping (address => uint256)) public allowance; + string public symbol; + uint8 public decimals = 18; // standard token precision. override to customize + string public name = ""; // Optional token name + + constructor(string memory symbol_) public { + symbol = symbol_; + owner = msg.sender; + } + + event Approval(address indexed src, address indexed guy, uint wad); + event Transfer(address indexed src, address indexed dst, uint wad); + event Mint(address indexed guy, uint wad); + event Burn(address indexed guy, uint wad); + event Stop(); + event Start(); + + function approve(address guy, uint wad) public returns (bool) { + require(!stopped, "ds-stop-is-stopped"); + allowance[msg.sender][guy] = wad; + + emit Approval(msg.sender, guy, wad); + + return true; + } + + function transfer(address dst, uint wad) external returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom(address src, address dst, uint wad) + public + returns (bool) + { + require(!stopped, "ds-stop-is-stopped"); + if (src != msg.sender && allowance[src][msg.sender] != 0xFFFFFFFFFFFFFFFF) { + require(allowance[src][msg.sender] >= wad, "ds-token-insufficient-approval"); + allowance[src][msg.sender] = allowance[src][msg.sender] - wad; + } + + require(balanceOf[src] >= wad, "ds-token-insufficient-balance"); + balanceOf[src] = balanceOf[src] - wad; + balanceOf[dst] = balanceOf[dst] + wad; + + emit Transfer(src, dst, wad); + + return true; + } + + function mint(address guy, uint wad) public { + require(!stopped, "ds-stop-is-stopped"); + require(msg.sender == owner, "ds-auth"); + balanceOf[guy] = balanceOf[guy] + wad; + totalSupply = totalSupply + wad; + emit Mint(guy, wad); + } + + function burn(address guy, uint wad) public { + require(!stopped, "ds-stop-is-stopped"); + require(msg.sender == owner, "ds-auth"); + if (guy != msg.sender && allowance[guy][msg.sender] != 0xFFFFFFFFFFFFFFFF) { + require(allowance[guy][msg.sender] >= wad, "ds-token-insufficient-approval"); + allowance[guy][msg.sender] = allowance[guy][msg.sender] - wad; + } + + require(balanceOf[guy] >= wad, "ds-token-insufficient-balance"); + balanceOf[guy] = balanceOf[guy] - wad; + totalSupply = totalSupply - wad; + emit Burn(guy, wad); + } + + function stop() public { + require(msg.sender == owner, "ds-auth"); + stopped = true; + emit Stop(); + } + + function start() public { + require(msg.sender == owner, "ds-auth"); + stopped = false; + emit Start(); + } + + function setName(string memory name_) public { + require(msg.sender == owner, "ds-auth"); + name = name_; + } + + function transferOwnership(address owner_) public { + require(msg.sender == owner, "ds-auth"); + owner = owner_; + } +}`; \ No newline at end of file diff --git a/app/src/features/editor/examples/solidity/index.ts b/app/src/features/editor/examples/solidity/index.ts new file mode 100644 index 0000000..6f84e11 --- /dev/null +++ b/app/src/features/editor/examples/solidity/index.ts @@ -0,0 +1,11 @@ +import { ExampleMenuItem } from "../../components/ExampleDropdown"; +import { EXAMPLE_SOLIDITY_CONTRACT_COUNTER } from "./counter"; +import { EXAMPLE_SOLIDITY_CONTRACT_ERC20 } from "./erc20"; +import { EXAMPLE_SOLIDITY_CONTRACT_VOTING } from "./voting"; + +export const EXAMPLE_SOLIDITY_CONTRACTS: ExampleMenuItem[] = [ + { label: 'Counter.sol', code: EXAMPLE_SOLIDITY_CONTRACT_COUNTER }, + { label: 'ERC20.sol', code: EXAMPLE_SOLIDITY_CONTRACT_ERC20 }, + { label: 'Voting.sol', code: EXAMPLE_SOLIDITY_CONTRACT_VOTING }, + ]; + \ No newline at end of file diff --git a/app/src/features/editor/examples/solidity/voting.ts b/app/src/features/editor/examples/solidity/voting.ts new file mode 100644 index 0000000..3aa3e41 --- /dev/null +++ b/app/src/features/editor/examples/solidity/voting.ts @@ -0,0 +1,24 @@ +export const EXAMPLE_SOLIDITY_CONTRACT_VOTING = `pragma solidity ^0.8.24; + +contract Voting { + uint64 public numVoters; + mapping(bytes32 => uint256) public votes; + mapping(address => mapping(bytes32 => bool)) public hasVoted; + mapping(address => bool) public voters; + + constructor(address[] memory voters_) public { + for (numVoters = 0; numVoters < voters_.length; numVoters++) { + voters[voters_[numVoters]] = true; + } + } + + function vote(bytes32 topic) public returns (bool) { + require(voters[msg.sender], "is-voter"); + require(hasVoted[msg.sender][topic] == false, "has-voted"); + + votes[topic] += 1; + hasVoted[msg.sender][topic] = true; + + return true; + } +}`; \ No newline at end of file diff --git a/app/src/features/editor/examples/sway/counter.ts b/app/src/features/editor/examples/sway/counter.ts new file mode 100644 index 0000000..b691ec6 --- /dev/null +++ b/app/src/features/editor/examples/sway/counter.ts @@ -0,0 +1,27 @@ +export const EXAMPLE_SWAY_CONTRACT_COUNTER = `contract; + +abi Counter { + #[storage(read, write)] + fn increment(amount: u64) -> u64; + + #[storage(read)] + fn get() -> u64; +} + +storage { + counter: u64 = 0, +} + +impl Counter for Contract { + #[storage(read, write)] + fn increment(amount: u64) -> u64 { + let incremented = storage.counter.read() + amount; + storage.counter.write(incremented); + incremented + } + + #[storage(read)] + fn get() -> u64 { + storage.counter.read() + } +}`; diff --git a/app/src/features/editor/examples/sway/index.ts b/app/src/features/editor/examples/sway/index.ts new file mode 100644 index 0000000..5448bb5 --- /dev/null +++ b/app/src/features/editor/examples/sway/index.ts @@ -0,0 +1,9 @@ +import { ExampleMenuItem } from "../../components/ExampleDropdown"; +import { EXAMPLE_SWAY_CONTRACT_COUNTER } from "./counter"; +import { EXAMPLE_SWAY_CONTRACT_SRC20 } from "./src20"; + +export const EXAMPLE_SWAY_CONTRACTS: ExampleMenuItem[] = [ + { label: 'Counter.sw', code: EXAMPLE_SWAY_CONTRACT_COUNTER }, + { label: 'SRC20.sw', code: EXAMPLE_SWAY_CONTRACT_SRC20 }, + ]; + \ No newline at end of file diff --git a/app/src/features/editor/examples/sway/src20.ts b/app/src/features/editor/examples/sway/src20.ts new file mode 100644 index 0000000..ef3c57d --- /dev/null +++ b/app/src/features/editor/examples/sway/src20.ts @@ -0,0 +1,134 @@ +export const EXAMPLE_SWAY_CONTRACT_SRC20 = `contract; + +use ownership; +use src3::SRC3; +use src5::SRC5; +use src20::SRC20; +use std::{ + asset::{ + burn, + mint_to, + }, + call_frames::{ + msg_asset_id, + }, + constants::DEFAULT_SUB_ID, + context::msg_amount, + string::String, +}; + +abi NativeAsset { + #[storage(read, write)] + fn constructor(owner_: Identity); + + #[storage(read, write)] + fn transferOwnership(owner_: Identity); +} + +configurable { + /// The decimals of the asset minted by this contract. + DECIMALS: u8 = 9u8, + /// The name of the asset minted by this contract. + NAME: str[7] = __to_str_array("MyAsset"), + /// The symbol of the asset minted by this contract. + SYMBOL: str[5] = __to_str_array("MYTKN"), +} + +storage { + /// The total supply of the asset minted by this contract. + total_supply: u64 = 0, +} + +impl SRC20 for Contract { + #[storage(read)] + fn total_assets() -> u64 { + 1 + } + + #[storage(read)] + fn total_supply(asset: AssetId) -> Option { + if asset == AssetId::default() { + Some(storage.total_supply.read()) + } else { + None + } + } + + #[storage(read)] + fn name(asset: AssetId) -> Option { + if asset == AssetId::default() { + Some(String::from_ascii_str(from_str_array(NAME))) + } else { + None + } + } + + #[storage(read)] + fn symbol(asset: AssetId) -> Option { + if asset == AssetId::default() { + Some(String::from_ascii_str(from_str_array(SYMBOL))) + } else { + None + } + } + + #[storage(read)] + fn decimals(asset: AssetId) -> Option { + if asset == AssetId::default() { + Some(DECIMALS) + } else { + None + } + } +} + +impl NativeAsset for Contract { + #[storage(read, write)] + fn constructor(owner_: Identity) { + require(storage.owner.read() == State::Uninitialized, "owner-initialized"); + ownership::initialize_ownership(owner_); + } + + #[storage(read, write)] + fn transferOwner(owner_: Identity) { + ownership::transfer_ownership(owner_); + } +} + +impl SRC5 for Contract { + #[storage(read)] + fn owner() -> State { + _owner() + } +} + +impl SRC3 for Contract { + #[storage(read, write)] + fn mint(recipient: Identity, sub_id: SubId, amount: u64) { + require(sub_id == DEFAULT_SUB_ID, "Incorrect Sub Id"); + ownership::only_owner(); + + // Increment total supply of the asset and mint to the recipient. + storage + .total_supply + .write(amount + storage.total_supply.read()); + mint_to(recipient, DEFAULT_SUB_ID, amount); + } + + #[storage(read, write)] + fn burn(sub_id: SubId, amount: u64) { + require(sub_id == DEFAULT_SUB_ID, "Incorrect Sub Id"); + require(msg_amount() >= amount, "Incorrect amount provided"); + require( + msg_asset_id() == AssetId::default(), + "Incorrect asset provided", + ); + ownership::only_owner(); + + // Decrement total supply of the asset and burn. + storage + .total_supply + .write(storage.total_supply.read() - amount); + burn(DEFAULT_SUB_ID, amount); + } +}`; \ No newline at end of file diff --git a/app/src/utils/localStorage.ts b/app/src/utils/localStorage.ts index 9212afb..3656a53 100644 --- a/app/src/utils/localStorage.ts +++ b/app/src/utils/localStorage.ts @@ -1,4 +1,5 @@ -import { EXAMPLE_SWAY_CONTRACT_COUNTER, EXAMPLE_SOLIDITY_CONTRACT_COUNTER } from '../constants'; +import { EXAMPLE_SOLIDITY_CONTRACTS } from "../features/editor/examples/solidity"; +import { EXAMPLE_SWAY_CONTRACTS } from "../features/editor/examples/sway"; const STORAGE_ABI_KEY = 'playground_abi'; const STORAGE_SLOTS_KEY = 'playground_slots'; @@ -39,9 +40,9 @@ export function saveSolidityCode(code: string) { } export function loadSwayCode() { - return localStorage.getItem(STORAGE_CONTRACT_KEY) ?? EXAMPLE_SWAY_CONTRACT_COUNTER; + return localStorage.getItem(STORAGE_CONTRACT_KEY) ?? EXAMPLE_SWAY_CONTRACTS[0].code; } export function loadSolidityCode() { - return localStorage.getItem(STORAGE_SOLIDITY_CONTRACT_KEY) ?? EXAMPLE_SOLIDITY_CONTRACT_COUNTER; + return localStorage.getItem(STORAGE_SOLIDITY_CONTRACT_KEY) ?? EXAMPLE_SOLIDITY_CONTRACTS[0].code; } diff --git a/projects/swaypad/Forc.lock b/projects/swaypad/Forc.lock index 97e0647..53eafd7 100644 --- a/projects/swaypad/Forc.lock +++ b/projects/swaypad/Forc.lock @@ -2,6 +2,14 @@ name = "core" source = "path+from-root-4F1FAAE4510FEB23" +[[package]] +name = "ownership" +source = "git+https://github.com/FuelLabs/sway-libs?tag=v0.19.0#dcbbec000a9051e71c0c21f4b720eebbb55c199a" +dependencies = [ + "src_5", + "std", +] + [[package]] name = "src20" source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.3.3#4198b4b07449ad16104cc8a0501f3013670fdcfd" @@ -17,6 +25,11 @@ name = "src5" source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.3.3#4198b4b07449ad16104cc8a0501f3013670fdcfd" dependencies = ["std"] +[[package]] +name = "src_5" +source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.2.2#6989cf8224b0d8aabea62f3d3c648fc754948705" +dependencies = ["std"] + [[package]] name = "std" source = "git+https://github.com/fuellabs/sway?tag=v0.51.1#d1e8f019c1da46273c3d3a72b385ce356ba2bc20" @@ -26,6 +39,7 @@ dependencies = ["core"] name = "swaypad" source = "member" dependencies = [ + "ownership", "src20", "src3", "src5", diff --git a/projects/swaypad/Forc.toml b/projects/swaypad/Forc.toml index 0c20011..8b88172 100644 --- a/projects/swaypad/Forc.toml +++ b/projects/swaypad/Forc.toml @@ -5,6 +5,7 @@ license = "Apache-2.0" name = "swaypad" [dependencies] +ownership = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.19.0" } src3 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } src5 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } src20 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } From fdd54914b3b85b030e06730513f6dcc97761164e Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 13:32:39 -0700 Subject: [PATCH 06/29] unused deps --- app/src/constants.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/constants.ts b/app/src/constants.ts index 3d95fdf..179507d 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -1,6 +1,3 @@ -import { EditorLanguage } from './features/editor/components/ActionOverlay'; -import { ExampleMenuItem } from './features/editor/components/ExampleDropdown'; - export const FUEL_GREEN = '#00f58c'; export const SERVER_URI = process.env.REACT_APP_LOCAL_SERVER From 3563b81f236f2f7b659b840539bba23c6c1e0ee8 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 14:34:40 -0700 Subject: [PATCH 07/29] integ test in ci --- .github/workflows/frontend-build-and-test.yml | 8 +++ app/src/constants.ts | 3 +- .../features/editor/examples/examples.test.ts | 65 +++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 app/src/features/editor/examples/examples.test.ts diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index e8ab410..2eab169 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -21,3 +21,11 @@ jobs: run: | cd app && npm ci && npm run build cp -r build ../build + + steps: + - uses: actions/checkout@v2 + - name: npm test + run: | + cargo run + cd app && npm run test + diff --git a/app/src/constants.ts b/app/src/constants.ts index 179507d..f4ba3c0 100644 --- a/app/src/constants.ts +++ b/app/src/constants.ts @@ -1,5 +1,6 @@ export const FUEL_GREEN = '#00f58c'; +export const LOCAL_SERVER_URI = 'http://0.0.0.0:8080'; export const SERVER_URI = process.env.REACT_APP_LOCAL_SERVER - ? 'http://0.0.0.0:8080' + ? LOCAL_SERVER_URI : 'https://api.sway-playground.org'; diff --git a/app/src/features/editor/examples/examples.test.ts b/app/src/features/editor/examples/examples.test.ts new file mode 100644 index 0000000..8e03a13 --- /dev/null +++ b/app/src/features/editor/examples/examples.test.ts @@ -0,0 +1,65 @@ +import { EXAMPLE_CONTRACTS } from '.'; +import { LOCAL_SERVER_URI } from '../../../constants'; +import { ExampleMenuItem } from '../components/ExampleDropdown'; + +describe(`test examples`, () => { + beforeAll(() => { + // Start server + // require('../../../server'); + }); + + describe(`transpile solidity examples`, () => { + const uri = `${LOCAL_SERVER_URI}/transpile`; + + it.each( + EXAMPLE_CONTRACTS['solidity'].map(({ label, code }: ExampleMenuItem) => [ + label, + code, + ]) + )('%s', async (_label, code) => { + // Call server + const request = new Request(uri, { + method: 'POST', + body: JSON.stringify({ + contract: code, + lanaguage: 'solidity', + }), + }); + + const response = await fetch(request); + const {error, swayContract} = await response.json(); + + expect(error).toBeUndefined(); + expect(swayContract).toContain('contract;'); + }); + }); + + describe(`compile sway examples`, () => { + const uri = `${LOCAL_SERVER_URI}/compile`; + + it.each( + EXAMPLE_CONTRACTS['sway'].map(({ label, code }: ExampleMenuItem) => [ + label, + code, + ]) + )('%s', async (_label, code) => { + // Call server + const request = new Request(uri, { + method: 'POST', + body: JSON.stringify({ + contract: code, + toolchain: 'latest' + }), + }); + + const response = await fetch(request); + const { error, abi, bytecode, storageSlots, forcVersion } = await response.json(); + + expect(error).toBeUndefined(); + expect(abi).toBeDefined(); + expect(bytecode).toBeDefined(); + expect(storageSlots).toBeDefined(); + expect(forcVersion).toBeDefined(); + }, 10000); + }); +}); From e93d93e67486369119cd043373c59fe1ec6a864a Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 14:50:47 -0700 Subject: [PATCH 08/29] cargo gh action --- .github/workflows/frontend-build-and-test.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index 2eab169..ccf0048 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -14,7 +14,6 @@ jobs: steps: - uses: actions/checkout@v2 - - name: npm ci and build env: CI: true @@ -24,6 +23,9 @@ jobs: steps: - uses: actions/checkout@v2 + - uses: actions-rs/cargo@v1 + with: + command: run - name: npm test run: | cargo run From 0163f6cbbb4c0499898ccde209711431e7ab4200 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 15:04:11 -0700 Subject: [PATCH 09/29] fix typo --- .github/workflows/frontend-build-and-test.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index ccf0048..ba18bf6 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -21,13 +21,10 @@ jobs: cd app && npm ci && npm run build cp -r build ../build - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/cargo@v1 + - name: npm test + uses: actions-rs/cargo@v1 with: command: run - - name: npm test run: | cargo run cd app && npm run test - From a0ace0536fb6e66e6b53bda2f42d4cc3b1934c1c Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 15:20:04 -0700 Subject: [PATCH 10/29] split up steps --- .github/workflows/frontend-build-and-test.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index ba18bf6..72858ac 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -21,10 +21,9 @@ jobs: cd app && npm ci && npm run build cp -r build ../build - - name: npm test - uses: actions-rs/cargo@v1 + - uses: actions-rs/cargo@v1 with: command: run + - name: npm test run: | - cargo run cd app && npm run test From 277e229aefb2a109b071286452fbac4e0828face Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 15:36:40 -0700 Subject: [PATCH 11/29] run in background --- .github/workflows/frontend-build-and-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index 72858ac..50221c1 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -20,10 +20,10 @@ jobs: run: | cd app && npm ci && npm run build cp -r build ../build - - - uses: actions-rs/cargo@v1 + - uses: actions-rs/toolchain@v1 with: - command: run + toolchain: stable + - run: cargo run & - name: npm test run: | cd app && npm run test From 0216017601621278467a5820edf61ec1e44fae09 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 15:41:18 -0700 Subject: [PATCH 12/29] cargo install --- .github/workflows/frontend-build-and-test.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index 50221c1..4c26d08 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -20,10 +20,11 @@ jobs: run: | cd app && npm ci && npm run build cp -r build ../build - - uses: actions-rs/toolchain@v1 + - uses: actions-rs/cargo@v1 with: - toolchain: stable - - run: cargo run & + command: install + args: --path . - name: npm test run: | + sway-playground & # Start sway-playground in the background cd app && npm run test From e37ff64c1cae2a83b1942527742ed77f67ab11a5 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 15:42:24 -0700 Subject: [PATCH 13/29] remove broken test --- app/src/App.test.tsx | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 app/src/App.test.tsx diff --git a/app/src/App.test.tsx b/app/src/App.test.tsx deleted file mode 100644 index 1c82ad8..0000000 --- a/app/src/App.test.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders compile and reset buttons', () => { - render(); - const compileButton = screen.getByLabelText('Compile sway code'); - const resetButton = screen.getByLabelText('reset the editor'); - expect(compileButton).toBeInTheDocument(); - expect(resetButton).toBeInTheDocument(); -}); From ce7fbbca82d2f8e07ae489c604d8d2ffa9f93107 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 16:46:24 -0700 Subject: [PATCH 14/29] run with docker --- .github/workflows/frontend-build-and-test.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index 4c26d08..81d67a6 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -13,6 +13,16 @@ jobs: runs-on: ubuntu-latest steps: + - name: Docker meta + id: meta + uses: docker/metadata-action@v3 + with: + images: | + ghcr.io/fuellabs/sway-playground + tags: | + type=ref,event=branch + type=sha,prefix= + type=semver,pattern={{raw}} - uses: actions/checkout@v2 - name: npm ci and build env: @@ -20,11 +30,9 @@ jobs: run: | cd app && npm ci && npm run build cp -r build ../build - - uses: actions-rs/cargo@v1 + - uses: addnab/docker-run-action@v3 with: - command: install - args: --path . + image: ${{ steps.meta.outputs.tags }} - name: npm test run: | - sway-playground & # Start sway-playground in the background cd app && npm run test From ad6a77426f63db52745e9c797663cc82b969919c Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 16:55:35 -0700 Subject: [PATCH 15/29] metadata --- .github/workflows/frontend-build-and-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index 81d67a6..856628b 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -30,6 +30,7 @@ jobs: run: | cd app && npm ci && npm run build cp -r build ../build + - run: echo steps.meta.outputs - uses: addnab/docker-run-action@v3 with: image: ${{ steps.meta.outputs.tags }} From 8856a4ba25d5d907b94a18871f1c5cdc7d00c898 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 16:55:55 -0700 Subject: [PATCH 16/29] meta output --- .github/workflows/frontend-build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index 856628b..649506e 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -30,7 +30,7 @@ jobs: run: | cd app && npm ci && npm run build cp -r build ../build - - run: echo steps.meta.outputs + - run: echo ${{ steps.meta.outputs }} - uses: addnab/docker-run-action@v3 with: image: ${{ steps.meta.outputs.tags }} From d29e7b46e970792a99daee5d2757529780f75006 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 16:59:36 -0700 Subject: [PATCH 17/29] toJson --- .github/workflows/frontend-build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index 649506e..4d3653d 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -30,7 +30,7 @@ jobs: run: | cd app && npm ci && npm run build cp -r build ../build - - run: echo ${{ steps.meta.outputs }} + - run: echo ${{ toJson(steps.meta.outputs) }} - uses: addnab/docker-run-action@v3 with: image: ${{ steps.meta.outputs.tags }} From 8d7f1b7a4447ea8213a7e83c21946d09b3011ded Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 17:24:28 -0700 Subject: [PATCH 18/29] service --- .github/workflows/frontend-build-and-test.yml | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index 4d3653d..5caff39 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -11,18 +11,12 @@ env: jobs: build-and-test: runs-on: ubuntu-latest - + services: + fuel-core: + image: ghcr.io/fuellabs/sway-playground + ports: + - 8080:8080 steps: - - name: Docker meta - id: meta - uses: docker/metadata-action@v3 - with: - images: | - ghcr.io/fuellabs/sway-playground - tags: | - type=ref,event=branch - type=sha,prefix= - type=semver,pattern={{raw}} - uses: actions/checkout@v2 - name: npm ci and build env: @@ -30,10 +24,6 @@ jobs: run: | cd app && npm ci && npm run build cp -r build ../build - - run: echo ${{ toJson(steps.meta.outputs) }} - - uses: addnab/docker-run-action@v3 - with: - image: ${{ steps.meta.outputs.tags }} - name: npm test run: | cd app && npm run test From 407bca9e3a7db3959e1225b99edfde7ffe19ce6b Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 17:24:45 -0700 Subject: [PATCH 19/29] service name --- .github/workflows/frontend-build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml index 5caff39..b92702f 100644 --- a/.github/workflows/frontend-build-and-test.yml +++ b/.github/workflows/frontend-build-and-test.yml @@ -12,7 +12,7 @@ jobs: build-and-test: runs-on: ubuntu-latest services: - fuel-core: + sway-playground: image: ghcr.io/fuellabs/sway-playground ports: - 8080:8080 From 19befc6a6496d03b0ba88044104bd03f56c70100 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 17:45:22 -0700 Subject: [PATCH 20/29] combine workflows --- .github/workflows/{backend-ci.yml => ci.yml} | 22 ++++++++++++++ .github/workflows/frontend-build-and-test.yml | 29 ------------------- 2 files changed, 22 insertions(+), 29 deletions(-) rename .github/workflows/{backend-ci.yml => ci.yml} (88%) delete mode 100644 .github/workflows/frontend-build-and-test.yml diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/ci.yml similarity index 88% rename from .github/workflows/backend-ci.yml rename to .github/workflows/ci.yml index 15124b0..c42e012 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,7 @@ env: RUSTFLAGS: -D warnings REGISTRY: ghcr.io RUST_VERSION: 1.70.0 + NODE_VERSION: '16' jobs: cancel-previous-runs: @@ -124,6 +125,27 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max + frontend-build-and-test: + if: github.ref != 'refs/heads/master' + needs: + - build-and-publish-image + runs-on: ubuntu-latest + services: + sway-playground: + image: ${{ steps.meta.outputs.tags }} + ports: + - 8080:8080 + steps: + - uses: actions/checkout@v2 + - name: NPM build + env: + CI: true + run: | + cd app && npm ci && npm run build + - name: NPM test + run: | + cd app && npm run test + deploy: if: github.ref == 'refs/heads/master' needs: diff --git a/.github/workflows/frontend-build-and-test.yml b/.github/workflows/frontend-build-and-test.yml deleted file mode 100644 index b92702f..0000000 --- a/.github/workflows/frontend-build-and-test.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Frontend - Build and Test - -on: - push: - branches-ignore: - - master - -env: - NODE_VERSION: '16' - -jobs: - build-and-test: - runs-on: ubuntu-latest - services: - sway-playground: - image: ghcr.io/fuellabs/sway-playground - ports: - - 8080:8080 - steps: - - uses: actions/checkout@v2 - - name: npm ci and build - env: - CI: true - run: | - cd app && npm ci && npm run build - cp -r build ../build - - name: npm test - run: | - cd app && npm run test From 69487cb68e92e6f25e57db5b0842c31068f01077 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 17:55:05 -0700 Subject: [PATCH 21/29] with docker-run-action --- .github/workflows/ci.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c42e012..6030bef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,11 +130,6 @@ jobs: needs: - build-and-publish-image runs-on: ubuntu-latest - services: - sway-playground: - image: ${{ steps.meta.outputs.tags }} - ports: - - 8080:8080 steps: - uses: actions/checkout@v2 - name: NPM build @@ -142,6 +137,10 @@ jobs: CI: true run: | cd app && npm ci && npm run build + - uses: addnab/docker-run-action@v3 + with: + image: ${{ steps.meta.outputs.tags }} + options: -p 8080:8080 - name: NPM test run: | cd app && npm run test From d1da8da82123407ecf3bdce3eb871425d3a563ee Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 18:02:58 -0700 Subject: [PATCH 22/29] docker run --- .github/workflows/ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6030bef..2e0b017 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,10 +137,9 @@ jobs: CI: true run: | cd app && npm ci && npm run build - - uses: addnab/docker-run-action@v3 - with: - image: ${{ steps.meta.outputs.tags }} - options: -p 8080:8080 + - name: Run the service in docker + run: | + docker run ${{ steps.meta.outputs.tags }} -p 8080:8080 - name: NPM test run: | cd app && npm run test From cfe64ea4ac1c46117271caaec3413d9f435c83ae Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 18:13:33 -0700 Subject: [PATCH 23/29] tags output --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e0b017..f2bf490 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,6 +89,8 @@ jobs: - cargo-check - cargo-test runs-on: ubuntu-latest + outputs: + tags: ${{ steps.meta.outputs.tags }} steps: - name: Checkout repository uses: actions/checkout@v2 @@ -139,7 +141,7 @@ jobs: cd app && npm ci && npm run build - name: Run the service in docker run: | - docker run ${{ steps.meta.outputs.tags }} -p 8080:8080 + docker run ${{ needs.build-and-publish-image.outputs.tags }} -p 8080:8080 - name: NPM test run: | cd app && npm run test From 8633c51ffaa1597aea84b62049111b23a5b416a0 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 18:16:52 -0700 Subject: [PATCH 24/29] rename ci workflow --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2bf490..ff791af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Backend CI +name: CI on: push: branches: @@ -141,7 +141,7 @@ jobs: cd app && npm ci && npm run build - name: Run the service in docker run: | - docker run ${{ needs.build-and-publish-image.outputs.tags }} -p 8080:8080 + docker run -p 8080:8080 ${{ needs.build-and-publish-image.outputs.tags }} - name: NPM test run: | cd app && npm run test From 503a3ca9819cc53c5b642f4f39da4ce446d4a6f1 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 18:24:13 -0700 Subject: [PATCH 25/29] run in detached mode --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff791af..1f0656f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -141,7 +141,7 @@ jobs: cd app && npm ci && npm run build - name: Run the service in docker run: | - docker run -p 8080:8080 ${{ needs.build-and-publish-image.outputs.tags }} + docker run -d -p 8080:8080 ${{ needs.build-and-publish-image.outputs.tags }} - name: NPM test run: | cd app && npm run test From a44a59115538b32ce1a33df40b802021b047da2d Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 18:31:22 -0700 Subject: [PATCH 26/29] debug response --- app/src/features/editor/examples/examples.test.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/features/editor/examples/examples.test.ts b/app/src/features/editor/examples/examples.test.ts index 8e03a13..58a0975 100644 --- a/app/src/features/editor/examples/examples.test.ts +++ b/app/src/features/editor/examples/examples.test.ts @@ -3,11 +3,6 @@ import { LOCAL_SERVER_URI } from '../../../constants'; import { ExampleMenuItem } from '../components/ExampleDropdown'; describe(`test examples`, () => { - beforeAll(() => { - // Start server - // require('../../../server'); - }); - describe(`transpile solidity examples`, () => { const uri = `${LOCAL_SERVER_URI}/transpile`; @@ -53,6 +48,10 @@ describe(`test examples`, () => { }); const response = await fetch(request); + + // TODO: remove + expect(response.body).toBe({}); + const { error, abi, bytecode, storageSlots, forcVersion } = await response.json(); expect(error).toBeUndefined(); From bd183ac56d5ae4044aafb1881df736e0653d8495 Mon Sep 17 00:00:00 2001 From: Sophie Date: Thu, 21 Mar 2024 19:06:41 -0700 Subject: [PATCH 27/29] increase test timeout --- app/src/features/editor/examples/examples.test.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/features/editor/examples/examples.test.ts b/app/src/features/editor/examples/examples.test.ts index 58a0975..651ace3 100644 --- a/app/src/features/editor/examples/examples.test.ts +++ b/app/src/features/editor/examples/examples.test.ts @@ -48,10 +48,6 @@ describe(`test examples`, () => { }); const response = await fetch(request); - - // TODO: remove - expect(response.body).toBe({}); - const { error, abi, bytecode, storageSlots, forcVersion } = await response.json(); expect(error).toBeUndefined(); @@ -59,6 +55,6 @@ describe(`test examples`, () => { expect(bytecode).toBeDefined(); expect(storageSlots).toBeDefined(); expect(forcVersion).toBeDefined(); - }, 10000); + }, 20000); }); }); From 5358cb77a5888a33b1301949d356e08c3fadbd0d Mon Sep 17 00:00:00 2001 From: Sophie Date: Fri, 22 Mar 2024 18:03:23 -0700 Subject: [PATCH 28/29] Fix error for src20 example --- README.md | 3 ++- app/src/features/editor/examples/examples.test.ts | 4 ++-- deployment/Dockerfile | 6 +++++- projects/swaypad/Forc.lock | 6 +++--- projects/swaypad/Forc.toml | 2 +- src/compilation/mod.rs | 2 +- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1de1539..dfac5f7 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,8 @@ cargo run Alternatively, it can be run locally with Docker, as it is in the deployed environment. ```sh -docker build -f deployment/Dockerfile . +# forc is not fully supported on arm linux, see https://github.com/FuelLabs/sway/issues/5760 +docker buildx build --platform linux/amd64 -f deployment/Dockerfile . docker run -p 8080:8080 -d ``` diff --git a/app/src/features/editor/examples/examples.test.ts b/app/src/features/editor/examples/examples.test.ts index 651ace3..efe7962 100644 --- a/app/src/features/editor/examples/examples.test.ts +++ b/app/src/features/editor/examples/examples.test.ts @@ -43,7 +43,7 @@ describe(`test examples`, () => { method: 'POST', body: JSON.stringify({ contract: code, - toolchain: 'latest' + toolchain: 'beta-5' }), }); @@ -55,6 +55,6 @@ describe(`test examples`, () => { expect(bytecode).toBeDefined(); expect(storageSlots).toBeDefined(); expect(forcVersion).toBeDefined(); - }, 20000); + }, 10000); }); }); diff --git a/deployment/Dockerfile b/deployment/Dockerfile index 13c3d50..0fa7660 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -35,7 +35,7 @@ RUN cargo build FROM ubuntu:22.04 as run RUN apt-get update -y \ - && apt-get install -y --no-install-recommends ca-certificates curl git \ + && apt-get install -y --no-install-recommends ca-certificates curl git pkg-config libssl-dev \ # Clean up && apt-get autoremove -y \ && apt-get clean -y \ @@ -62,6 +62,10 @@ RUN fuelup toolchain install beta-3 RUN fuelup toolchain install beta-4 RUN fuelup toolchain install beta-5 +# Install the forc dependencies +RUN fuelup default beta-5 +RUN forc build --path projects/swaypad + EXPOSE 8080 CMD ["./sway-playground"] diff --git a/projects/swaypad/Forc.lock b/projects/swaypad/Forc.lock index 53eafd7..25a0b03 100644 --- a/projects/swaypad/Forc.lock +++ b/projects/swaypad/Forc.lock @@ -1,10 +1,10 @@ [[package]] name = "core" -source = "path+from-root-4F1FAAE4510FEB23" +source = "path+from-root-B672DC9E3D340CD2" [[package]] name = "ownership" -source = "git+https://github.com/FuelLabs/sway-libs?tag=v0.19.0#dcbbec000a9051e71c0c21f4b720eebbb55c199a" +source = "git+https://github.com/FuelLabs/sway-libs?tag=v0.18.0#8d196e9379463d4596ac582a20a84ed52ff58c69" dependencies = [ "src_5", "std", @@ -32,7 +32,7 @@ dependencies = ["std"] [[package]] name = "std" -source = "git+https://github.com/fuellabs/sway?tag=v0.51.1#d1e8f019c1da46273c3d3a72b385ce356ba2bc20" +source = "git+https://github.com/fuellabs/sway?tag=v0.49.2#a70c746d27b3300beef896ccd1dcce1299836192" dependencies = ["core"] [[package]] diff --git a/projects/swaypad/Forc.toml b/projects/swaypad/Forc.toml index 8b88172..5dccd06 100644 --- a/projects/swaypad/Forc.toml +++ b/projects/swaypad/Forc.toml @@ -5,7 +5,7 @@ license = "Apache-2.0" name = "swaypad" [dependencies] -ownership = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.19.0" } +ownership = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.18.0" } src3 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } src5 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } src20 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } diff --git a/src/compilation/mod.rs b/src/compilation/mod.rs index f0fa64a..cf14a91 100644 --- a/src/compilation/mod.rs +++ b/src/compilation/mod.rs @@ -67,7 +67,7 @@ pub fn build_and_destroy_project(contract: String, toolchain: String) -> Compile let error = std::str::from_utf8(&output.stderr).unwrap(); // Get the index of the main file. - let main_index = error.find("/main.sw:").unwrap(); + let main_index = error.find("/main.sw:").unwrap_or_default(); // Truncate the error message to only include the relevant content. let trunc = String::from(error).split_off(main_index); From 5ec6af3c25bdd28df3ab2c1dd19c8953490200da Mon Sep 17 00:00:00 2001 From: Sophie Date: Fri, 22 Mar 2024 18:42:56 -0700 Subject: [PATCH 29/29] update example --- .../features/editor/examples/examples.test.ts | 2 +- .../features/editor/examples/sway/src20.ts | 35 ++++++++++--------- projects/swaypad/Forc.lock | 14 -------- projects/swaypad/Forc.toml | 1 - 4 files changed, 20 insertions(+), 32 deletions(-) diff --git a/app/src/features/editor/examples/examples.test.ts b/app/src/features/editor/examples/examples.test.ts index efe7962..21d5db3 100644 --- a/app/src/features/editor/examples/examples.test.ts +++ b/app/src/features/editor/examples/examples.test.ts @@ -55,6 +55,6 @@ describe(`test examples`, () => { expect(bytecode).toBeDefined(); expect(storageSlots).toBeDefined(); expect(forcVersion).toBeDefined(); - }, 10000); + }, 40000); }); }); diff --git a/app/src/features/editor/examples/sway/src20.ts b/app/src/features/editor/examples/sway/src20.ts index ef3c57d..0336552 100644 --- a/app/src/features/editor/examples/sway/src20.ts +++ b/app/src/features/editor/examples/sway/src20.ts @@ -1,8 +1,7 @@ export const EXAMPLE_SWAY_CONTRACT_SRC20 = `contract; -use ownership; use src3::SRC3; -use src5::SRC5; +use src5::{SRC5, State, AccessError}; use src20::SRC20; use std::{ asset::{ @@ -10,6 +9,7 @@ use std::{ mint_to, }, call_frames::{ + contract_id, msg_asset_id, }, constants::DEFAULT_SUB_ID, @@ -17,12 +17,9 @@ use std::{ string::String, }; -abi NativeAsset { +abi Constructor { #[storage(read, write)] fn constructor(owner_: Identity); - - #[storage(read, write)] - fn transferOwnership(owner_: Identity); } configurable { @@ -37,6 +34,9 @@ configurable { storage { /// The total supply of the asset minted by this contract. total_supply: u64 = 0, + + /// Owner. + owner: State = State::Uninitialized, } impl SRC20 for Contract { @@ -82,23 +82,26 @@ impl SRC20 for Contract { } } -impl NativeAsset for Contract { +#[storage(read)] +fn is_owner() { + require( + storage.owner.read() == State::Initialized(msg_sender().unwrap()), + AccessError::NotOwner, + ); +} + +impl Constructor for Contract { #[storage(read, write)] fn constructor(owner_: Identity) { require(storage.owner.read() == State::Uninitialized, "owner-initialized"); - ownership::initialize_ownership(owner_); - } - - #[storage(read, write)] - fn transferOwner(owner_: Identity) { - ownership::transfer_ownership(owner_); + storage.owner.write(State::Initialized(owner_)); } } impl SRC5 for Contract { #[storage(read)] fn owner() -> State { - _owner() + storage.owner.read() } } @@ -106,7 +109,7 @@ impl SRC3 for Contract { #[storage(read, write)] fn mint(recipient: Identity, sub_id: SubId, amount: u64) { require(sub_id == DEFAULT_SUB_ID, "Incorrect Sub Id"); - ownership::only_owner(); + is_owner(); // Increment total supply of the asset and mint to the recipient. storage @@ -123,7 +126,7 @@ impl SRC3 for Contract { msg_asset_id() == AssetId::default(), "Incorrect asset provided", ); - ownership::only_owner(); + is_owner(); // Decrement total supply of the asset and burn. storage diff --git a/projects/swaypad/Forc.lock b/projects/swaypad/Forc.lock index 25a0b03..8e12bdf 100644 --- a/projects/swaypad/Forc.lock +++ b/projects/swaypad/Forc.lock @@ -2,14 +2,6 @@ name = "core" source = "path+from-root-B672DC9E3D340CD2" -[[package]] -name = "ownership" -source = "git+https://github.com/FuelLabs/sway-libs?tag=v0.18.0#8d196e9379463d4596ac582a20a84ed52ff58c69" -dependencies = [ - "src_5", - "std", -] - [[package]] name = "src20" source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.3.3#4198b4b07449ad16104cc8a0501f3013670fdcfd" @@ -25,11 +17,6 @@ name = "src5" source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.3.3#4198b4b07449ad16104cc8a0501f3013670fdcfd" dependencies = ["std"] -[[package]] -name = "src_5" -source = "git+https://github.com/FuelLabs/sway-standards?tag=v0.2.2#6989cf8224b0d8aabea62f3d3c648fc754948705" -dependencies = ["std"] - [[package]] name = "std" source = "git+https://github.com/fuellabs/sway?tag=v0.49.2#a70c746d27b3300beef896ccd1dcce1299836192" @@ -39,7 +26,6 @@ dependencies = ["core"] name = "swaypad" source = "member" dependencies = [ - "ownership", "src20", "src3", "src5", diff --git a/projects/swaypad/Forc.toml b/projects/swaypad/Forc.toml index 5dccd06..0c20011 100644 --- a/projects/swaypad/Forc.toml +++ b/projects/swaypad/Forc.toml @@ -5,7 +5,6 @@ license = "Apache-2.0" name = "swaypad" [dependencies] -ownership = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.18.0" } src3 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } src5 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" } src20 = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.3.3" }