diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 671998b46..9b3dcaba3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,8 +23,8 @@ dont-merge-to-master: only: - master -.build_binary: &build_binary - # To run in sequence and save CPU usage, use stage: build_and_package_binaries +.build_binary: + &build_binary # To run in sequence and save CPU usage, use stage: build_and_package_binaries stage: test script: - $build_binary_script "$target_os_family" "$target_os" "$target_os_version" @@ -71,8 +71,6 @@ dont-merge-to-master: # move internal odoc documentation to the website folder - mkdir -p build/ligo/ - mv ../../_build/default/_doc/_html/ build/ligo/odoc - - pwd # for debug - - ls build/ligo/ # for debug after_script: - cp -r gitlab-pages/website/build/ligo public artifacts: @@ -84,7 +82,6 @@ dont-merge-to-master: services: - docker:19.03.5-dind - .before_script: &before_script before_script: # Install dependencies @@ -237,6 +234,7 @@ build-publish-ide-image: - find dist/ - find dist/package/ -name '*ligo_*deb' - mv $(realpath dist/package/debian-10/*.deb) tools/webide/ligo_deb10.deb + - cp -r src/test/examples tools/webide/packages/client/examples - cd tools/webide - echo "${CI_BUILD_TOKEN}" | docker login -u gitlab-ci-token --password-stdin registry.gitlab.com - > @@ -244,6 +242,7 @@ build-publish-ide-image: -t "${WEBIDE_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}" --build-arg GIT_TAG="${CI_COMMIT_SHA}" --build-arg GIT_COMMIT="${CI_COMMIT_SHORT_SHA}" + --build-arg EXAMPLES_DIR_SRC=packages/client/examples . - docker push "${WEBIDE_IMAGE_NAME}:${CI_COMMIT_SHORT_SHA}" rules: diff --git a/tools/webide/packages/client/examples/cameligo/arithmetic-contract.ligo b/src/test/examples/cameligo/arithmetic-contract.ligo similarity index 100% rename from tools/webide/packages/client/examples/cameligo/arithmetic-contract.ligo rename to src/test/examples/cameligo/arithmetic-contract.ligo diff --git a/tools/webide/packages/client/examples/pascaligo/arithmetic-contract.ligo b/src/test/examples/pascaligo/arithmetic-contract.ligo similarity index 100% rename from tools/webide/packages/client/examples/pascaligo/arithmetic-contract.ligo rename to src/test/examples/pascaligo/arithmetic-contract.ligo diff --git a/src/test/examples/pascaligo/fa-1.2.ligo b/src/test/examples/pascaligo/fa-1.2.ligo new file mode 100644 index 000000000..62f8d7c98 --- /dev/null +++ b/src/test/examples/pascaligo/fa-1.2.ligo @@ -0,0 +1,164 @@ +(*_* + name: FA1.2 PascaLIGO implementation + language: pascaligo + compile: + entrypoint: main + dryRun: + entrypoint: main + parameters: "" + storage: "" + deploy: + entrypoint: main + storage: "" + evaluateValue: + entrypoint: "" + evaluateFunction: + entrypoint: "" + parameters: "" +*_*) +// This is an implimentation of the FA1.2 specification in PascaLIGO + +type amt is nat; + +type account is record + balance : amt; + allowances: map(address, amt); +end + +type action is +| Transfer of (address * address * amt) +| Approve of (address * amt) +| GetAllowance of (address * address * contract(amt)) +| GetBalance of (address * contract(amt)) +| GetTotalSupply of (unit * contract(amt)) + +type contract_storage is record + totalSupply: amt; + ledger: big_map(address, account); +end + +function isAllowed ( const src : address ; const value : amt ; var s : contract_storage) : bool is + begin + var allowed: bool := False; + if sender =/= source then block { + const src: account = get_force(src, s.ledger); + const allowanceAmount: amt = get_force(sender, src.allowances); + allowed := allowanceAmount >= value; + }; + else allowed := True; + end with allowed + +// Transfer a specific amount of tokens from the accountFrom address to a destination address +// Pre conditions: +// The sender address is the account owner or is allowed to spend x in the name of accountFrom +// The accountFrom account has a balance higher than amount +// Post conditions: +// The balance of accountFrom is decreased by amount +// The balance of destination is increased by amount +function transfer (const accountFrom : address ; const destination : address ; const value : amt ; var s : contract_storage) : contract_storage is + begin + // If accountFrom = destination transfer is not necessary + if accountFrom = destination then skip; + else block { + // Is sender allowed to spend value in the name of source + case isAllowed(accountFrom, value, s) of + | False -> failwith ("Sender not allowed to spend token from source") + | True -> skip + end; + + // Fetch src account + const src: account = get_force(accountFrom, s.ledger); + + // Check that the source can spend that much + if value > src.balance + then failwith ("Source balance is too low"); + else skip; + + // Update the source balance + // Using the abs function to convert int to nat + src.balance := abs(src.balance - value); + + s.ledger[accountFrom] := src; + + // Fetch dst account or add empty dst account to ledger + var dst: account := record + balance = 0n; + allowances = (map end : map(address, amt)); + end; + case s.ledger[destination] of + | None -> skip + | Some(n) -> dst := n + end; + + // Update the destination balance + dst.balance := dst.balance + value; + + // Decrease the allowance amount if necessary + if accountFrom =/= sender then block { + const allowanceAmount: amt = get_force(sender, src.allowances); + if allowanceAmount - value < 0 then failwith ("Allowance amount cannot be negative"); + else src.allowances[sender] := abs(allowanceAmount - value); + } else skip; + + s.ledger[destination] := dst; + } + end with s + +// Approve an amount to be spent by another address in the name of the sender +// Pre conditions: +// The spender account is not the sender account +// Post conditions: +// The allowance of spender in the name of sender is value +function approve (const spender : address ; const value : amt ; var s : contract_storage) : contract_storage is + begin + // If sender is the spender approving is not necessary + if sender = spender then skip; + else block { + const src: account = get_force(sender, s.ledger); + src.allowances[spender] := value; + s.ledger[sender] := src; // Not sure if this last step is necessary + } + end with s + +// View function that forwards the allowance amount of spender in the name of tokenOwner to a contract +// Pre conditions: +// None +// Post conditions: +// The state is unchanged +function getAllowance (const owner : address ; const spender : address ; const contr : contract(amt) ; var s : contract_storage) : list(operation) is + begin + const src: account = get_force(owner, s.ledger); + const destAllowance: amt = get_force(spender, src.allowances); + end with list [transaction(destAllowance, 0tz, contr)] + +// View function that forwards the balance of source to a contract +// Pre conditions: +// None +// Post conditions: +// The state is unchanged +function getBalance (const src : address ; const contr : contract(amt) ; var s : contract_storage) : list(operation) is + begin + const src: account = get_force(src, s.ledger); + end with list [transaction(src.balance, 0tz, contr)] + +// View function that forwards the totalSupply to a contract +// Pre conditions: +// None +// Post conditions: +// The state is unchanged +function getTotalSupply (const contr : contract(amt) ; var s : contract_storage) : list(operation) is + list [transaction(s.totalSupply, 0tz, contr)] + +function main (const p : action ; const s : contract_storage) : + (list(operation) * contract_storage) is + block { + // Reject any transaction that try to transfer token to this contract + if amount =/= 0tz then failwith ("This contract do not accept token"); + else skip; + } with case p of + | Transfer(n) -> ((nil : list(operation)), transfer(n.0, n.1, n.2, s)) + | Approve(n) -> ((nil : list(operation)), approve(n.0, n.1, s)) + | GetAllowance(n) -> (getAllowance(n.0, n.1, n.2, s), s) + | GetBalance(n) -> (getBalance(n.0, n.1, s), s) + | GetTotalSupply(n) -> (getTotalSupply(n.1, s), s) + end diff --git a/tools/webide/packages/client/examples/reasonligo/arithmetic-contract.ligo b/src/test/examples/reasonligo/arithmetic-contract.ligo similarity index 100% rename from tools/webide/packages/client/examples/reasonligo/arithmetic-contract.ligo rename to src/test/examples/reasonligo/arithmetic-contract.ligo diff --git a/tools/webide/Dockerfile b/tools/webide/Dockerfile index 4dfa9963a..cf259c68c 100644 --- a/tools/webide/Dockerfile +++ b/tools/webide/Dockerfile @@ -1,16 +1,20 @@ FROM node:12-alpine as builder +ARG EXAMPLES_DIR_SRC +ARG EXAMPLES_DIR_DEST=packages/client/examples + WORKDIR /app COPY package.json package.json COPY yarn.lock yarn.lock +COPY tsconfig.json tsconfig.json COPY packages/client packages/client COPY packages/server packages/server +COPY $EXAMPLES_DIR_SRC $EXAMPLES_DIR_DEST + +ENV EXAMPLES_DIR=/app/$EXAMPLES_DIR_DEST RUN yarn install - -COPY tsconfig.json tsconfig.json - RUN yarn workspaces run build FROM node:12-buster diff --git a/tools/webide/packages/client/package-examples.js b/tools/webide/packages/client/package-examples.js index 848721660..04146b7f4 100644 --- a/tools/webide/packages/client/package-examples.js +++ b/tools/webide/packages/client/package-examples.js @@ -98,20 +98,27 @@ async function main() { throw error; }); - const EXAMPLES_DEST_DIR = join(process.cwd(), 'build', 'static', 'examples'); - const EXAMPLES_DIR = join(process.cwd(), 'examples'); - const EXAMPLES_GLOB = '**/*.ligo'; - const EXAMPLES_LIST_FILE = 'list'; + const EXAMPLES_DIR = process.env['EXAMPLES_DIR'] || join(process.cwd(), '../../../../src/test/examples'); + // const EXAMPLES_GLOB = '**/*.ligo'; + // const files = await findFiles(EXAMPLES_GLOB, EXAMPLES_DIR); + + const CURATED_EXAMPLES = [ + 'cameligo/arithmetic-contract.ligo', + 'pascaligo/arithmetic-contract.ligo', + 'reasonligo/arithmetic-contract.ligo' + ]; + + const EXAMPLES_DEST_DIR = join(process.cwd(), 'build', 'static', 'examples'); fs.mkdirSync(EXAMPLES_DEST_DIR, { recursive: true }); - const files = await findFiles(EXAMPLES_GLOB, EXAMPLES_DIR); const examples = await processExamples( EXAMPLES_DIR, - files, + CURATED_EXAMPLES, EXAMPLES_DEST_DIR ); + const EXAMPLES_LIST_FILE = 'list'; await writeFile(join(EXAMPLES_DEST_DIR, EXAMPLES_LIST_FILE), examples); } diff --git a/tools/webide/packages/client/src/components/examples.tsx b/tools/webide/packages/client/src/components/examples.tsx index cecd2a4ee..9662b6618 100644 --- a/tools/webide/packages/client/src/components/examples.tsx +++ b/tools/webide/packages/client/src/components/examples.tsx @@ -7,34 +7,18 @@ import { ChangeDirtyAction, EditorState } from '../redux/editor'; import { ChangeSelectedAction, ExamplesState } from '../redux/examples'; import { getExample } from '../services/api'; -const bgColor = 'transparent'; -const borderSize = '5px'; -const verticalPadding = '0.6em'; - const Container = styled.div` flex: 0.5; display: flex; flex-direction: column; + min-width: 0; `; -const MenuItem = styled.div<{ selected?: boolean }>` - padding: ${verticalPadding} 0 ${verticalPadding} 1em; - height: 1em; +const Header = styled.div` + min-height: 2.5em; display: flex; align-items: center; - cursor: pointer; - background-color: ${props => - props.selected ? 'var(--blue_trans1)' : bgColor}; - border-left: ${`${borderSize} solid ${bgColor}`}; - border-left-color: ${props => (props.selected ? 'var(--blue)' : bgColor)}; - - :hover { - background-color: ${props => - props.selected ? 'var(--blue_trans1)' : 'var(--blue_trans2)'}; - border-left: ${`${borderSize} solid ${bgColor}`}; - border-left-color: ${props => - props.selected ? 'var(--blue)' : 'transparent'}; - } + font-weight: 600; `; const MenuContainer = styled.div` @@ -42,15 +26,22 @@ const MenuContainer = styled.div` flex-direction: column; overflow-y: auto; height: var(--content_height); - box-sizing: border-box; + font-size: 0.8em; `; -const Header = styled.div` - min-height: 2.5em; - padding: 0 10px; - display: flex; - align-items: center; - font-weight: 600; +const MenuItem = styled.span` + height: 1em; + padding: 0.6em; + cursor: pointer; + background-color: transparent; + + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + + :hover { + background-color: var(--blue_trans2); + } `; export const Examples = () => {