├── public └── .nojekyll ├── .husky ├── .gitignore ├── pre-commit └── commit-msg ├── .prettierrc ├── commitlint.config.js ├── .eslintrc.js ├── src ├── static │ ├── wc.png │ ├── UnsLogo.png │ ├── social.png │ ├── favicon-dark.png │ ├── README.md │ ├── argent.svg │ ├── RampLogo.svg │ ├── eth.svg │ ├── images │ │ └── logo.svg │ └── ilol48zhg7zypovk.html ├── locales │ ├── .i18n-editor-metadata │ └── en │ │ └── translations.json ├── assets │ ├── style │ │ ├── blocks │ │ │ ├── _onboard.scss │ │ │ ├── _modals.scss │ │ │ ├── _lineTableHeader.scss │ │ │ ├── _connectedWallet.scss │ │ │ ├── _loggingIn.scss │ │ │ ├── _footer.scss │ │ │ └── _infoBlock.scss │ │ ├── components │ │ │ ├── _rampBtn.scss │ │ │ ├── _tooltip.scss │ │ │ ├── _maxHeight.scss │ │ │ ├── _tokenDropdown.scss │ │ │ ├── _transactionToken.scss │ │ │ └── _amountInput.scss │ │ ├── main.scss │ │ ├── _variables.scss │ │ ├── pages │ │ │ ├── _connect.scss │ │ │ ├── _index.scss │ │ │ └── _link.scss │ │ ├── layouts │ │ │ └── _default.scss │ │ └── _default.scss │ └── README.md ├── plugins │ ├── utils.ts │ ├── README.md │ ├── restoreSession.ts │ ├── main.ts │ ├── setCheckoutData.ts │ ├── link.ts │ ├── walletActions │ │ └── transaction.ts │ ├── uns.ts │ ├── useLinkHash.ts │ ├── build.ts │ └── successCheckmark.json ├── layouts │ ├── README.md │ ├── link.vue │ ├── clear.vue │ └── default.vue ├── pages │ ├── README.md │ ├── link │ │ ├── _hash.vue │ │ └── index.vue │ └── connect.vue ├── middleware │ ├── auth.ts │ ├── README.md │ └── wallet.ts ├── store │ ├── README.md │ ├── index.ts │ └── checkout.ts ├── blocks │ ├── lineTableHeader.vue │ ├── footer.vue │ ├── OnboardErrorModal.vue │ ├── WrongNetworkModal.vue │ ├── BottomMenu.vue │ ├── modals.vue │ ├── SystemInfo.vue │ ├── loggingIn.vue │ ├── connectedWallet.vue │ └── infoBlock.vue ├── types │ └── index.d.ts └── components │ ├── BuyWithRampBtn.vue │ ├── PaymentItem.vue │ ├── AddressInput.vue │ ├── AmountInput.vue │ └── TokenDropdown.vue ├── vue-shim.d.ts ├── .eslintignore ├── .nuxtignore ├── .yarnrc.yml ├── .github ├── ISSUE_TEMPLATE │ ├── custom.md │ ├── feature_request.md │ └── bug_report.md ├── semantic.yml └── workflows │ ├── lint-pr-title.yml │ ├── secrets_scanner.yaml │ ├── release.yml │ ├── deploy-staging.yml │ ├── deploy-preview.yml │ └── deploy-prod.yml ├── .editorconfig ├── .babelrc ├── .prettierignore ├── stylelint.config.js ├── environments ├── README.md ├── .env.goerli └── .env.mainnet ├── cypress.json ├── .gitpod.yml ├── .stylelintignore ├── .firebaserc ├── .releaserc ├── cspell.json ├── LICENSE-MIT ├── tsconfig.json ├── cli-dev.sh ├── cspell-zksync.txt ├── .gitignore ├── cli-process-env.sh ├── firebase.json ├── README.md ├── package.json └── nuxt.config.ts /public/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.husky/.gitignore: -------------------------------------------------------------------------------- 1 | _ 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | "@matterlabs/prettier-config" -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"], 3 | }; -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | '@matterlabs/eslint-config-nuxt' 4 | ] 5 | } -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | CI="1" 5 | npx lint-staged 6 | -------------------------------------------------------------------------------- /src/static/wc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matter-labs/zksync-dapp-checkout/HEAD/src/static/wc.png -------------------------------------------------------------------------------- /vue-shim.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue"; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /src/locales/.i18n-editor-metadata: -------------------------------------------------------------------------------- 1 | minify_resources=0 2 | resource_type=JSON 3 | resource_name=translations 4 | -------------------------------------------------------------------------------- /src/static/UnsLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matter-labs/zksync-dapp-checkout/HEAD/src/static/UnsLogo.png -------------------------------------------------------------------------------- /src/static/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matter-labs/zksync-dapp-checkout/HEAD/src/static/social.png -------------------------------------------------------------------------------- /src/static/favicon-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matter-labs/zksync-dapp-checkout/HEAD/src/static/favicon-dark.png -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | .nuxt/* 3 | src/static/* 4 | .github/* 5 | .idea/* 6 | environments/* 7 | public/* 8 | tests/* 9 | -------------------------------------------------------------------------------- /.nuxtignore: -------------------------------------------------------------------------------- 1 | tsconfig.json 2 | cypress.json 3 | public 4 | .nuxt 5 | .github 6 | .yarn 7 | .firebase 8 | .idea 9 | environments 10 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | npmRegistryServer: "https://yarn.npmjs.org" 4 | 5 | yarnPath: .yarn/releases/yarn-3.1.1.cjs 6 | -------------------------------------------------------------------------------- /src/locales/en/translations.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": { 3 | "contacts": { 4 | "add": "Add contact", 5 | "edit": "Edit contact" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/assets/style/blocks/_onboard.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --onboard-wallet-app-icon-border-color: transparent; 3 | 4 | .bn-onboard-modal { 5 | z-index: 4; 6 | } 7 | } -------------------------------------------------------------------------------- /src/plugins/utils.ts: -------------------------------------------------------------------------------- 1 | import { utils } from "ethers"; 2 | 3 | export function checkAddress(address: string): boolean { 4 | return utils.isAddress(address) && address.startsWith("0x"); 5 | } 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/assets/style/blocks/_modals.scss: -------------------------------------------------------------------------------- 1 | .allModalsContainer { 2 | z-index: 1; 3 | position: fixed; 4 | left: 0; 5 | top: 0; 6 | width: 100%; 7 | height: 100%; 8 | pointer-events: none; 9 | 10 | & > * { 11 | pointer-events: all; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "presets": [ 5 | [ 6 | "@babel/preset-env", 7 | { 8 | "targets": { 9 | "node": "current" 10 | } 11 | } 12 | ] 13 | ] 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .nuxt/* 2 | src/static/* 3 | .github/* 4 | .idea/* 5 | environments/* 6 | public/* 7 | _nuxt/* 8 | *.example 9 | node_modules 10 | .eslintrc.js 11 | src/assets/fonts 12 | *.css 13 | *.eot 14 | *.svg 15 | *.ttf 16 | *.woff 17 | *.woff2 18 | successCheckmark.json 19 | sw.js -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | # Always validate the PR title AND all the commits 2 | titleAndCommits: true 3 | # Allows use of Merge commits (e.g. on GitHub: "Merge branch 'master' into feature/ride-unicorns") 4 | # this is only relevant when using commitsOnly: true (or titleAndCommits: true) 5 | allowMergeCommits: true -------------------------------------------------------------------------------- /src/layouts/README.md: -------------------------------------------------------------------------------- 1 | # LAYOUTS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Application Layouts. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts). 8 | -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["stylelint-config-standard", "stylelint-config-prettier"], 3 | // add your custom config here 4 | // https://stylelint.io/user-guide/configuration 5 | customSyntax: "scss", 6 | rules: { 7 | "no-descending-specificity": null, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/pages/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. The framework reads all the `*.vue` files inside this directory and creates the router of your application. 4 | 5 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 6 | -------------------------------------------------------------------------------- /environments/README.md: -------------------------------------------------------------------------------- 1 | # PAGES 2 | 3 | This directory contains your Application Views and Routes. 4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application. 5 | 6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing). 7 | -------------------------------------------------------------------------------- /src/assets/README.md: -------------------------------------------------------------------------------- 1 | # ASSETS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked). 8 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3000", 3 | "fixturesFolder": "cypress/fixtures", 4 | "integrationFolder": "cypress/integration", 5 | "pluginsFile": "cypress/plugins/index.js", 6 | "screenshotsFolder": "cypress/screenshots", 7 | "supportFile": "cypress/support/index.js", 8 | "videosFolder": "cypress/videos" 9 | } 10 | -------------------------------------------------------------------------------- /src/pages/link/_hash.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | -------------------------------------------------------------------------------- /src/plugins/README.md: -------------------------------------------------------------------------------- 1 | # PLUGINS 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins). 8 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # This configuration file was automatically generated by Gitpod. 2 | # Please adjust to your needs (see https://www.gitpod.io/docs/config-gitpod-file) 3 | # and commit this file to your remote git repository to share the goodness with others. 4 | 5 | tasks: 6 | - init: yarn install && yarn run build 7 | command: yarn run start 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/layouts/link.vue: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /src/middleware/auth.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "@nuxt/types"; 2 | 3 | export default ({ redirect, store, route }: Context) => { 4 | if (store.getters["zk-account/loggedIn"]) { 5 | if (route.path === "/") { 6 | return redirect("/account"); 7 | } 8 | } else if (route.path !== "/" && !store.getters["zk-onboard/restoringSession"]) { 9 | return redirect("/"); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | cypress/* 3 | .nuxt/* 4 | src/static/* 5 | .github/* 6 | .idea/* 7 | environments/* 8 | public/* 9 | _nuxt/* 10 | src/static/sw.js 11 | .eslintrc.js 12 | tests/* 13 | *.example 14 | fonts/** 15 | src/assets/imgs/* 16 | .github/* 17 | .idea/* 18 | environments/* 19 | public/* 20 | src/static/sw.js 21 | *.css 22 | *.eot 23 | *.svg 24 | *.ttf 25 | *.woff 26 | *.woff2 27 | successCheckmark.json -------------------------------------------------------------------------------- /src/middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your application middleware. Middleware let you define custom functions that can be run before rendering either a page or a group of pages. 6 | 7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware). 8 | -------------------------------------------------------------------------------- /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "zsync-dapp-checkout" 4 | }, 5 | "targets": { 6 | "zsync-dapp-checkout": { 7 | "hosting": { 8 | "prod-mainnet": [ 9 | "zsync-dapp-checkout" 10 | ], 11 | "prod-goerli": [ 12 | "goerli-checkout-zksync" 13 | ], 14 | "staging": [ 15 | "staging-checkout-v1-zksync" 16 | ] 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/store/README.md: -------------------------------------------------------------------------------- 1 | # STORE 2 | 3 | **This directory is not required, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your Vuex Store files. 6 | Vuex Store option is implemented in the Nuxt.js framework. 7 | 8 | Creating a file in this directory automatically activates the option in the framework. 9 | 10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). -------------------------------------------------------------------------------- /.github/workflows/lint-pr-title.yml: -------------------------------------------------------------------------------- 1 | name: "Lint PR" 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - edited 8 | - synchronize 9 | 10 | jobs: 11 | label: 12 | name: Validate PR title 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: amannn/action-semantic-pull-request@v4 16 | with: 17 | subjectPattern: ^(?![A-Z]).+$ 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /src/plugins/restoreSession.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "@nuxt/types"; 2 | 3 | export default ({ store, route, redirect }: Context) => { 4 | store.commit("zk-onboard/setLoginOptions", { requireSigner: true, requestTransactionHistory: false }); 5 | if (route.matched[0].path === "/link" || route.matched[0].path === "/link/:hash") { 6 | return; 7 | } 8 | if (!route.path.startsWith("/connect")) { 9 | redirect({ path: "/connect", query: route.query }); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /src/static/README.md: -------------------------------------------------------------------------------- 1 | # STATIC 2 | 3 | **This directory is optional, you can delete it if you don't want to use it.** 4 | 5 | This directory contains your static files. Each file inside it will be mapped to `/` after publishing. Thus, you'd want to delete this README.md before deploying to production. 6 | 7 | Example: `/static/robots.txt` is mapped as `/robots.txt`. 8 | 9 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static). 10 | -------------------------------------------------------------------------------- /src/blocks/lineTableHeader.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /.github/workflows/secrets_scanner.yaml: -------------------------------------------------------------------------------- 1 | name: Leaked Secrets Scan 2 | on: [pull_request] 3 | jobs: 4 | TruffleHog: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Checkout code 8 | uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3 9 | with: 10 | fetch-depth: 0 11 | - name: TruffleHog OSS 12 | uses: trufflesecurity/trufflehog@0c66d30c1f4075cee1aada2e1ab46dabb1b0071a 13 | with: 14 | path: ./ 15 | base: ${{ github.event.repository.default_branch }} 16 | head: HEAD 17 | extra_args: --debug --only-verified 18 | -------------------------------------------------------------------------------- /src/assets/style/components/_rampBtn.scss: -------------------------------------------------------------------------------- 1 | .defbtn.rampBtn.filled { 2 | background-color: $rampGreen; 3 | padding: 0 10px; 4 | &:not(:disabled):hover { 5 | background-color: lighten($color: $rampGreen, $amount: 5); 6 | } 7 | &:not(:disabled):active { 8 | background-color: $rampGreen; 9 | } 10 | } 11 | 12 | @media screen and (max-width: $mobile) { 13 | .valuesRightTop .rampBtn.filled { 14 | padding-top: 0.25rem !important; 15 | padding-bottom: 0.25rem !important; 16 | height: auto; 17 | font-size: 10px !important; 18 | text-align: left; 19 | span span { 20 | display: block; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/plugins/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | 3 | // @ts-ignore 4 | import Popover from "vue-js-popover"; 5 | 6 | // eslint-disable-next-line import/no-named-as-default 7 | import VTooltip from "v-tooltip"; 8 | 9 | // @ts-ignore 10 | import VueCustomScrollbar from "vue-custom-scrollbar"; 11 | import "vue-custom-scrollbar/dist/vueScrollbar.css"; 12 | import { Context } from "@nuxt/types"; 13 | import { UNSResolver } from "./uns"; 14 | 15 | export default (_ctx: Context) => { 16 | Vue.use(VTooltip); 17 | 18 | Vue.use(Popover); 19 | Vue.component("VueCustomScrollbar", VueCustomScrollbar); 20 | 21 | Vue.prototype.$domainResolver = new UNSResolver(); 22 | }; 23 | -------------------------------------------------------------------------------- /src/static/argent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/assets/style/blocks/_lineTableHeader.scss: -------------------------------------------------------------------------------- 1 | .lineTableHeader { 2 | display: grid; 3 | grid-gap: 5px; 4 | grid-template-rows: 100%; 5 | grid-template-columns: 65px 90px 1fr max-content; 6 | align-items: center; 7 | width: 100%; 8 | height: 29px; 9 | 10 | .tableItem { 11 | color: $black2; 12 | font-weight: 300; 13 | font-size: 14px; 14 | font-family: $firaCondensed; 15 | line-height: 1.4; 16 | 17 | &:last-child { 18 | justify-self: end; 19 | width: max-content; 20 | } 21 | 22 | letter-spacing: -0.025em; 23 | } 24 | 25 | @media screen and (max-width: $mobile) { 26 | grid-template-columns: 45px 1fr max-content; 27 | margin: 0; 28 | } 29 | } 30 | 31 | .darkMode .lineTableHeader { 32 | .tableItem { 33 | color: $gray; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "branches": "main", 3 | "debug": false, 4 | "tagFormat": "${version}", 5 | "addReleases": "top", 6 | "npmPublish": false, 7 | "plugins": [ 8 | "@semantic-release/commit-analyzer", 9 | "@semantic-release/release-notes-generator", 10 | [ 11 | "@semantic-release/npm", 12 | { 13 | "npmPublish": false 14 | } 15 | ], 16 | "@semantic-release/github", 17 | [ 18 | "@semantic-release/changelog", 19 | { 20 | "changelogFile": "CHANGELOG.md" 21 | } 22 | ], 23 | [ 24 | "@semantic-release/git", 25 | { 26 | "assets": [ "package.json", "yarn.lock", "CHANGELOG.md" ], 27 | "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 28 | } 29 | ] 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /src/assets/style/main.scss: -------------------------------------------------------------------------------- 1 | // Core 2 | @import "./default.scss"; 3 | 4 | //Layouts 5 | @import "./layouts/default.scss"; 6 | 7 | //Blocks 8 | @import "./blocks/infoBlock.scss"; 9 | @import "./blocks/lineTableHeader.scss"; 10 | @import "./blocks/loggingIn.scss"; 11 | @import "./blocks/connectedWallet.scss"; 12 | @import "./blocks/onboard.scss"; 13 | @import "./blocks/modals.scss"; 14 | @import "./blocks/footer.scss"; 15 | 16 | //Components 17 | @import "./components/amountInput.scss"; 18 | @import "./components/maxHeight.scss"; 19 | @import "./components/transactionToken.scss"; 20 | @import "./components/rampBtn.scss"; 21 | @import "./components/tooltip.scss"; 22 | @import "./components/tokenDropdown.scss"; 23 | 24 | //Pages 25 | @import "./pages/index.scss"; 26 | @import "./pages/connect.scss"; 27 | @import "./pages/link.scss"; 28 | -------------------------------------------------------------------------------- /cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "en", 3 | "ignorePaths": [ 4 | "node_modules/**", 5 | ".github/**", 6 | ".firebase/**", 7 | ".yarn/**", 8 | "dist/**" 9 | ], 10 | "dictionaries": [ 11 | "typescript", 12 | "cpp", 13 | "npm", 14 | "filetypes", 15 | "cpp", 16 | "en_GB", 17 | "en_US", 18 | "node", 19 | "bash", 20 | "fonts", 21 | "npm", 22 | "cryptocurrencies", 23 | "companies", 24 | "rust", 25 | "html", 26 | "css", 27 | "entities", 28 | "softwareTerms", 29 | "misc", 30 | "fullstack", 31 | "softwareTerms", 32 | "zksync" 33 | ], 34 | "dictionaryDefinitions": [ 35 | { 36 | "name": "zksync", 37 | "addWords": true, 38 | "path": "./cspell-zksync.txt" 39 | } 40 | ], 41 | "allowCompoundWords": true 42 | } 43 | -------------------------------------------------------------------------------- /src/assets/style/components/_tooltip.scss: -------------------------------------------------------------------------------- 1 | .tooltip { 2 | display: block !important; 3 | z-index: 9999; 4 | opacity: 1; 5 | transition: $transition; 6 | transition-property: visibility, opacity; 7 | will-change: visibility, opacity; 8 | 9 | &:not(.popover) { 10 | pointer-events: none; 11 | 12 | .tooltip-inner { 13 | margin-top: 7px; 14 | background: #00000046; 15 | color: $white; 16 | border-radius: 4px; 17 | font-size: 14px; 18 | padding: 3px 10px; 19 | } 20 | .tooltip-arrow { 21 | display: none; 22 | } 23 | &[aria-hidden="true"] { 24 | visibility: hidden; 25 | opacity: 0; 26 | } 27 | } 28 | &.popover { 29 | .tooltip-inner { 30 | background: #fff; 31 | color: $black; 32 | box-shadow: 0 4px 20px 0 rgba(52, 73, 94, 0.2); 33 | border-radius: 5px; 34 | font-weight: 300; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/assets/style/_variables.scss: -------------------------------------------------------------------------------- 1 | //Colors 2 | $white: #fff; 3 | $white2: #fbfbfb; 4 | $white3: #f4f5f7; 5 | $light: #e1e4e8; 6 | $gray: #8d9aac; 7 | $dark: #243955; 8 | $dark2: #4e566d; 9 | $dark3: #5d768e; 10 | $majorDark: #171a49; 11 | $black2: #3c4257; 12 | $black: #000; 13 | $darkBlue: #272640; 14 | $violet: #5436d6; 15 | $violetOpacity: rgba(84, 54, 214, 0.25); 16 | $lightViolet: lighten( 17 | $color: $violet, 18 | $amount: 10, 19 | ); 20 | $red: #f25f5c; 21 | $green: #057a55; 22 | $rampGreen: #059669; 23 | 24 | //Fonts 25 | $firaSans: "Fira Sans", "sans-serif"; 26 | $firaCode: "Fira Code", "sans-serif"; 27 | $firaCondensed: "Fira Sans", "sans-serif"; 28 | 29 | $mainFontFamily: $firaSans; 30 | 31 | // Transitions 32 | $transition: 0.2s ease; 33 | $transition2: 0.3s ease; 34 | 35 | /* Responsive */ 36 | $mobile: 768px; 37 | $breakpoint1: 1100px; 38 | $maxWidth: 1370px; 39 | 40 | /* max-width */ 41 | $maxWidthDefault: 660px; 42 | $maxWidthMobile: 95rem; 43 | -------------------------------------------------------------------------------- /src/blocks/footer.vue: -------------------------------------------------------------------------------- 1 | 17 | 30 | -------------------------------------------------------------------------------- /src/layouts/clear.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /src/blocks/OnboardErrorModal.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 41 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Matter Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "baseUrl": "./src/", 6 | "checkJs": false, 7 | "declaration": true, 8 | "esModuleInterop": true, 9 | "experimentalDecorators": true, 10 | "lib": [ 11 | "ESNext", 12 | "ESNext.AsyncIterable", 13 | "DOM" 14 | ], 15 | "module": "ESNext", 16 | "moduleResolution": "Node", 17 | "noEmit": true, 18 | "noImplicitAny": true, 19 | "paths": { 20 | "~/*": [ 21 | "./*" 22 | ], 23 | "@/*": [ 24 | "./*" 25 | ] 26 | }, 27 | "suppressImplicitAnyIndexErrors": true, 28 | "resolveJsonModule": true, 29 | "sourceMap": true, 30 | "strict": true, 31 | "target": "ESNext", 32 | "types": [ 33 | "@types/node", 34 | "@nuxt/types", 35 | "@nuxtjs/sentry", 36 | "@matterlabs/zksync-nuxt-core", 37 | ] 38 | }, 39 | "exclude": [ 40 | "node_modules", 41 | ".nuxt", 42 | "dist", 43 | ".nuxt", 44 | ".github", 45 | "environments", 46 | ".yarn", 47 | "tests" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /src/middleware/wallet.ts: -------------------------------------------------------------------------------- 1 | import { Context, Middleware } from "@nuxt/types"; 2 | import useLinkHash from "@/plugins/useLinkHash"; 3 | 4 | const walletMiddleware: Middleware = (context: Context) => { 5 | if (context.route.matched[0].path === "/link" || context.route.matched[0].path === "/link/:hash") { 6 | return; 7 | } 8 | if (context.store.getters["checkout/getErrorState"]) { 9 | if ( 10 | (context.route.matched[0].path === "/connect" || context.route.matched[0].path === "") && 11 | typeof context.query.link === "string" && 12 | !context.store.getters["checkout/isLinkCheckout"] 13 | ) { 14 | return useLinkHash(context, context.query.link); 15 | } 16 | context.redirect("/link"); 17 | return; 18 | } 19 | if (context.store.getters["zk-account/loggedIn"]) { 20 | if (context.route.path.startsWith("/connect")) { 21 | context.redirect({ query: context.route.query, path: "/" }); 22 | } 23 | } else if (!context.route.path.startsWith("/connect") && !context.store.getters["zk-onboard/restoringSession"]) { 24 | context.redirect({ query: context.route.query, path: "/connect" }); 25 | } 26 | }; 27 | 28 | export default walletMiddleware; 29 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: "Semantic release" 2 | "on": 3 | push: 4 | branches: [ main ] 5 | jobs: 6 | release: 7 | if: ${{ github.event.repository.full_name == github.repository }} && {{ !contains(github.event.head_commit.message, "skip ci") }} 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: 12 | fetch-depth: 0 13 | # Whether to configure the token or SSH key with the local git config 14 | # Default: true 15 | persist-credentials: false # <--- checking this in commit context 16 | 17 | - name: "Enable yarn cache" 18 | uses: c-hive/gha-yarn-cache@v2 # using cache 19 | 20 | - name: "Setup node@16" 21 | uses: actions/setup-node@v2 22 | with: 23 | node-version: 16 24 | 25 | - name: "Install dependencies" 26 | run: | 27 | yarn set version 3.1.1 28 | yarn install 29 | 30 | - name: "Lint" 31 | run: | 32 | yarn lint --max-warnings 0 33 | 34 | - name: "Semantic release" 35 | run: yarn semantic-release 36 | env: 37 | HUSKY: 0 38 | CI: true 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { ActionTree, GetterTree, MutationTree } from "vuex"; 2 | 3 | export const state = () => ({ 4 | /** 5 | * Used to handle modals and simplify the code 6 | */ 7 | currentModal: false as String | false, 8 | step: "main" as string, 9 | darkMode: false, 10 | }); 11 | 12 | export type RootState = ReturnType; 13 | 14 | export const getters: GetterTree = { 15 | currentModal(state) { 16 | return state.currentModal; 17 | }, 18 | step(state) { 19 | return state.step; 20 | }, 21 | darkMode(state) { 22 | return state.darkMode; 23 | }, 24 | }; 25 | 26 | export const mutations: MutationTree = { 27 | setStep(state, step: string) { 28 | state.step = step; 29 | }, 30 | setCurrentModal(state, modalName: false | string) { 31 | state.currentModal = modalName; 32 | }, 33 | setDarkMode(state, darkModeState: boolean) { 34 | state.darkMode = darkModeState; 35 | }, 36 | }; 37 | 38 | export const actions: ActionTree = { 39 | openModal({ commit }, modalName) { 40 | commit("setCurrentModal", modalName); 41 | }, 42 | closeActiveModal({ commit }) { 43 | commit("setCurrentModal", false); 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "ethers"; 2 | import { DecimalBalance } from "@matterlabs/zksync-nuxt-core/types"; 3 | import { ZkSyncTransaction } from "zksync-checkout/build/types"; 4 | import { TokenSymbol, Address } from "zksync/build/types"; 5 | 6 | export type PaymentItem = { 7 | address: Address; 8 | token: TokenSymbol; 9 | amount: DecimalBalance; 10 | }; 11 | export type TransactionData = { 12 | transactions: Array; 13 | fromAddress: Address; 14 | feeToken: TokenSymbol; 15 | domains: Map; 16 | }; 17 | export type TransactionFee = { 18 | key: string; 19 | amount: BigNumber; 20 | realAmount: BigNumber; 21 | token: TokenSymbol; 22 | to?: Address; 23 | }; 24 | export type TotalByToken = { 25 | [token: string]: BigNumber; 26 | }; 27 | 28 | export type ZkSingleToken = { 29 | address: string; 30 | id: number; 31 | symbol: string; 32 | decimals: number; 33 | }; 34 | // Tokens are indexed by their symbol (e.g. "ETH") 35 | export type ZkTokens = Iterator; 36 | 37 | export type ZKISingleRampConfig = { 38 | url?: string; 39 | hostApiKey: string; 40 | }; 41 | export type ZKIRampConfig = { 42 | goerli: ZKISingleRampConfig; 43 | mainnet: ZKISingleRampConfig; 44 | }; 45 | -------------------------------------------------------------------------------- /cli-dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CATEGORY="$1" 4 | 5 | ACTION="$2" 6 | 7 | if [[ $CATEGORY == "clean" ]]; then 8 | if [[ $ACTION == "yarn" ]]; then 9 | echo "Yarn garbage collection started" 10 | echo "" 11 | echo " - dropping node_modules folder..." 12 | rm -rf node_modules 13 | echo " Done" 14 | echo " - removing .yarn cached files..." 15 | rm -rf .yarn/cache .yarn/build-state.yml .yarn/install-state.gz 16 | echo " Done" 17 | echo " - cleaning yarn cache..." 18 | yarn cache clean --all 19 | printf " Done\n\n" 20 | echo "All done!" 21 | elif [[ $ACTION == "nuxt" ]]; then 22 | echo "Nuxtjs garbage collection started" 23 | echo "" 24 | echo " - dropping .nuxt folder..." 25 | rm -rf .nuxt 26 | echo " Done" 27 | echo " - cleaning the files inside public folder..." 28 | rm -rf public/* 29 | printf " Done\n\n" 30 | echo "All done!" 31 | fi 32 | fi 33 | 34 | if [[ $CATEGORY == "ci" ]]; then 35 | echo "Yarn pre-deploy garbage collection started" 36 | echo "" 37 | bash cli-dev.sh clean nuxt 38 | echo "" 39 | echo "" 40 | bash cli-dev.sh clean yarn 41 | echo "" 42 | echo "Running yarn install " 43 | yarn install --check-cache 44 | echo "Done" 45 | fi 46 | -------------------------------------------------------------------------------- /src/assets/style/blocks/_connectedWallet.scss: -------------------------------------------------------------------------------- 1 | .connectedWallet { 2 | .address { 3 | position: relative; 4 | cursor: pointer; 5 | transition: border-color $transition; 6 | border-bottom: 1px dashed $black; 7 | 8 | &:hover { 9 | color: $lightViolet; 10 | border-color: $lightViolet; 11 | 12 | .copyIcon { 13 | opacity: 1; 14 | } 15 | } 16 | 17 | &:active { 18 | transition-duration: 0.001s; 19 | color: lighten($color: $lightViolet, $amount: 10); 20 | } 21 | 22 | .dots { 23 | display: none; 24 | } 25 | 26 | @media screen and (max-width: 1155px) { 27 | .addressMiddle { 28 | display: none; 29 | } 30 | 31 | .dots { 32 | display: inline; 33 | } 34 | } 35 | 36 | .copyIcon { 37 | position: absolute; 38 | color: $lightViolet; 39 | left: 103.5%; 40 | top: 42%; 41 | transform: translateY(-50%); 42 | opacity: 0; 43 | transition: opacity $transition; 44 | will-change: opacity; 45 | } 46 | } 47 | 48 | .headline { 49 | &:hover { 50 | color: $violet; 51 | } 52 | } 53 | 54 | .vue-popover { 55 | position: fixed; 56 | color: $lightViolet; 57 | width: fit-content !important; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/assets/style/components/_maxHeight.scss: -------------------------------------------------------------------------------- 1 | .maxHeightBlock { 2 | @media (min-width: $mobile) { 3 | &.mobile { 4 | height: auto !important; 5 | } 6 | } 7 | } 8 | 9 | @media (min-width: $mobile) { 10 | .linkLayout .linkPage .zkFooterContainer #built-by-matterlabs { 11 | position: absolute; 12 | bottom: 40px; 13 | } 14 | } 15 | 16 | @media (max-width: $mobile) { 17 | .linkPage { 18 | .maxHeightBlock { 19 | @apply bg-white; 20 | 21 | height: 110px; 22 | position: absolute !important; 23 | top: -1.5rem; 24 | transform: translateY(-100%); 25 | width: 100vw; 26 | padding: 0 5vw; 27 | left: 0; 28 | } 29 | 30 | .linkFooter { 31 | @apply fixed bottom-0 w-full bg-white left-0 p-0; 32 | } 33 | 34 | .zkFooterContainer { 35 | box-shadow: 0 -4px 6px -1px rgba(0, 0, 0, 0.05), 0 -2px 4px -1px rgba(0, 0, 0, 0.05); 36 | flex-direction: column; 37 | height: auto; 38 | align-items: center; 39 | grid-gap: 5px; 40 | 41 | & > * { 42 | margin-bottom: 5px; 43 | } 44 | 45 | .poweredBy { 46 | margin-left: 0; 47 | } 48 | 49 | .rightSide { 50 | justify-content: center; 51 | margin-bottom: 0; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/deploy-staging.yml: -------------------------------------------------------------------------------- 1 | name: "Deploy staging" 2 | "on": 3 | push: 4 | branches: [ main ] 5 | jobs: 6 | build_and_deploy: 7 | if: ${{ github.event.repository.full_name == github.repository }} 8 | runs-on: ubuntu-latest 9 | environment: dev 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: "Enable yarn cache" 13 | uses: c-hive/gha-yarn-cache@v2 # using cache 14 | 15 | - name: "Setup node@16" 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: 16 19 | 20 | - name: "Install dependencies" 21 | run: | 22 | yarn set version 3.1.1 23 | yarn install 24 | 25 | - name: "Lint" 26 | run: | 27 | yarn lint --max-warnings 0 28 | 29 | - name: "Deploy target: goerli" 30 | run: yarn run ci:build:goerli 31 | 32 | - name: "Setup node@18" 33 | uses: actions/setup-node@v2 34 | with: 35 | node-version: 18 36 | 37 | - uses: matter-labs/action-hosting-deploy@main 38 | with: 39 | repoToken: "${{ secrets.GITHUB_TOKEN }}" 40 | firebaseServiceAccount: "${{ secrets.FIREBASE_SERVICE_ACCOUNT_ZSYNC_DAPP_CHECKOUT_DEV }}" 41 | target: staging 42 | projectId: zsync-dapp-checkout 43 | channelID: live 44 | -------------------------------------------------------------------------------- /cspell-zksync.txt: -------------------------------------------------------------------------------- 1 | // zkSync-related words 2 | matterlabs 3 | zksync 4 | zkscan 5 | zkscrypto 6 | libzkscrypto 7 | pubkey 8 | pubdata 9 | keccak 10 | musig 11 | WASM 12 | jsrpc 13 | backends 14 | ethsig 15 | ethop 16 | decentralization 17 | rollups 18 | zkrollup 19 | unencrypted 20 | permissionless 21 | trustlessness 22 | IERC 23 | Schnorr 24 | MuSig 25 | 26 | mainchain 27 | offchain 28 | blockchains 29 | sidechain 30 | sidechains 31 | tokenomics 32 | validator's 33 | 34 | tlds 35 | 36 | // Names 37 | Stichting 38 | Kingsfordweg 39 | RSIN 40 | ABDK 41 | Alef 42 | Zcon 43 | Paypal 44 | Numio 45 | 46 | // Used libraries 47 | numberish 48 | arrayify 49 | hexlify 50 | markdownlint 51 | ethersproject 52 | 53 | // Used programming language words 54 | printf 55 | charsets 56 | println 57 | fatalf 58 | allowfullscreen 59 | inttypes 60 | 61 | // ETC 62 | gitter 63 | signup 64 | signups 65 | precompiled 66 | checkmark 67 | Vitalik 68 | Buterin 69 | roadmap 70 | majeure 71 | conveniens 72 | reimplementing 73 | subsecond 74 | supermajority 75 | gemeente 76 | unauthorised 77 | Ethereum's 78 | SDKs 79 | 80 | // Famous crypto people 81 | Vitalik's 82 | Buterin's 83 | multisignature 84 | onchain 85 | convertion 86 | Keyhash 87 | Armeabi 88 | scijava 89 | defbtn 90 | closebtn 91 | nuxt 92 | getzk 93 | Vuex 94 | zsync 95 | Syncjs 96 | corejs 97 | Nuxtjs 98 | optimisations 99 | -------------------------------------------------------------------------------- /src/assets/style/blocks/_loggingIn.scss: -------------------------------------------------------------------------------- 1 | .loggingInContainer { 2 | z-index: 2; 3 | background-color: $white2; 4 | position: absolute; 5 | width: 100%; 6 | height: 100%; 7 | left: 0; 8 | right: 0; 9 | top: 0; 10 | bottom: 0; 11 | display: flex; 12 | justify-content: center; 13 | align-items: center; 14 | flex-direction: column; 15 | animation-fill-mode: forwards; 16 | overflow: hidden; 17 | transition-delay: 0.35s; 18 | 19 | &.fade-leave-active { 20 | transition-delay: 0.5s; 21 | } 22 | 23 | .hint { 24 | position: relative; 25 | height: 20px; 26 | width: 100%; 27 | 28 | & > div { 29 | position: absolute; 30 | left: 0; 31 | top: 0; 32 | width: 100%; 33 | height: auto; 34 | } 35 | } 36 | 37 | @media screen and (max-width: $mobile) { 38 | padding: 0 5vw; 39 | 40 | svg { 41 | max-width: 100%; 42 | } 43 | } 44 | } 45 | 46 | .darkMode .loggingInContainer { 47 | background-color: $darkBlue; 48 | } 49 | 50 | @media screen and (max-width: $mobile) { 51 | .loggingInContainer { 52 | .hint { 53 | font-size: 14px !important; 54 | } 55 | } 56 | } 57 | @media screen and (max-width: 400px) { 58 | .loggingInContainer { 59 | & > div, 60 | & > h1 { 61 | text-align: center; 62 | font-size: 18px; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/deploy-preview.yml: -------------------------------------------------------------------------------- 1 | name: "Deploy preview" 2 | "on": pull_request 3 | jobs: 4 | build_and_preview: 5 | if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} 6 | runs-on: ubuntu-latest 7 | environment: dev 8 | steps: 9 | - uses: actions/checkout@v2 10 | 11 | - name: "Enable yarn cache" 12 | uses: c-hive/gha-yarn-cache@v2 # using cache 13 | 14 | - name: "Setup node@16" 15 | uses: actions/setup-node@v2 16 | with: 17 | node-version: 16 18 | 19 | - name: "Install dependencies" 20 | run: | 21 | yarn set version 3.1.1 22 | yarn install 23 | 24 | - name: "Lint" 25 | run: | 26 | yarn lint --max-warnings 0 27 | 28 | - name: "Build: dapp" 29 | run: yarn run ci:build:goerli 30 | 31 | - name: "Setup node@18" 32 | uses: actions/setup-node@v2 33 | with: 34 | node-version: 18 35 | 36 | - name: "Deploy: preview" 37 | uses: matter-labs/action-hosting-deploy@main 38 | with: 39 | repoToken: '${{ secrets.GITHUB_TOKEN }}' 40 | firebaseServiceAccount: "${{ secrets.FIREBASE_SERVICE_ACCOUNT_ZSYNC_DAPP_CHECKOUT_DEV }}" 41 | target: staging 42 | projectId: zsync-dapp-checkout 43 | env: 44 | FIREBASE_CLI_PREVIEWS: hostingchannels 45 | -------------------------------------------------------------------------------- /src/plugins/setCheckoutData.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "@nuxt/types"; 2 | import { ZkSyncCheckoutManager } from "zksync-checkout-internal"; 3 | import theme from "@matterlabs/zksync-nuxt-core/utils/theme"; 4 | 5 | export default async ({ store }: Context): Promise => { 6 | if (theme.getUserTheme() === "dark") { 7 | theme.toggleTheme(); 8 | } 9 | await store.dispatch("zk-provider/requestProvider"); 10 | await store.dispatch("zk-tokens/loadZkTokens"); 11 | if (!window.opener || window.opener === window) { 12 | store.commit("checkout/setError", "Windows isn't a popup. Can't initialize zkCheckoutManager"); 13 | return; 14 | } 15 | try { 16 | const checkoutManager = ZkSyncCheckoutManager.getManager(); 17 | checkoutManager.startCheckout((e) => console.log(`Err ${e} has occurred`)); 18 | const state = await checkoutManager.getCheckoutState(); 19 | console.log("Checkout state", state); 20 | store.commit("checkout/setLinkCheckoutState", false); 21 | store.dispatch("checkout/setTransactionData", { 22 | ...state, 23 | fromAddress: state.userAddress, 24 | }); 25 | // await store.dispatch("zk-transaction/setType", "TransferBatch", { root: true }); 26 | store.dispatch("checkout/requestUsedTokensPrice"); 27 | } catch (error) { 28 | console.log("ZkSyncCheckoutManager error", error); 29 | store.commit("checkout/setError", error); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/assets/style/pages/_connect.scss: -------------------------------------------------------------------------------- 1 | .connectContainer { 2 | height: calc(100vh - 60px - 2rem); 3 | display: flex; 4 | flex-direction: column; 5 | justify-content: center; 6 | align-items: center; 7 | 8 | .tileContainer { 9 | width: max-content; 10 | cursor: pointer; 11 | 12 | &:hover .tile { 13 | box-shadow: 0 4px 8px #00000015; 14 | } 15 | 16 | .tile { 17 | position: relative; 18 | width: 130px; 19 | height: 130px; 20 | background-color: $white; 21 | border-radius: 27px; 22 | padding: 20px; 23 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 24 | transition: box-shadow $transition; 25 | will-change: box-shadow; 26 | 27 | img { 28 | width: 100%; 29 | height: 100%; 30 | object-fit: contain; 31 | } 32 | 33 | .tileIcon { 34 | position: absolute; 35 | top: -24px; 36 | right: -24px; 37 | width: 48px; 38 | height: 48px; 39 | opacity: 0; 40 | border-radius: 50%; 41 | background-color: $white; 42 | line-height: 48px; 43 | font-size: 20px; 44 | color: $violet; 45 | } 46 | } 47 | 48 | .tileName { 49 | font-size: 18px; 50 | font-weight: 600; 51 | padding-top: 10px; 52 | } 53 | } 54 | 55 | @media screen and (max-width: $mobile) { 56 | height: calc(100% - 60px); 57 | @media screen and (max-height: 630px) { 58 | height: calc(100% - 30px); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/blocks/WrongNetworkModal.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 48 | -------------------------------------------------------------------------------- /src/assets/style/components/_tokenDropdown.scss: -------------------------------------------------------------------------------- 1 | .tokenDropdown { 2 | position: relative; 3 | 4 | &.focused, 5 | &.open { 6 | .dropdownMain { 7 | border-color: $violet; 8 | } 9 | } 10 | &.standalone { 11 | &.open { 12 | .dropdownMainContainer .dropdownMain { 13 | width: 100%; 14 | } 15 | } 16 | .dropdownMainContainer { 17 | display: flex; 18 | justify-content: flex-end; 19 | 20 | .dropdownMain { 21 | width: max-content; 22 | & > div { 23 | width: 100%; 24 | padding-right: 0 !important; 25 | justify-content: space-between; 26 | } 27 | } 28 | } 29 | } 30 | 31 | .dropdownMainContainer { 32 | width: 100%; 33 | } 34 | 35 | .trigger { 36 | width: 100%; 37 | 38 | .dropdownMain { 39 | width: 100%; 40 | height: 36px; 41 | background-color: $white; 42 | transition: opacity $transition; 43 | will-change: opacity; 44 | } 45 | } 46 | } 47 | .dropdownBody { 48 | z-index: 99; 49 | box-sizing: content-box; 50 | background-color: $white2; 51 | outline: none; 52 | 53 | .tokensList { 54 | width: 100%; 55 | height: max-content; 56 | max-height: 35vh; 57 | overflow: auto; 58 | 59 | .tokenItem { 60 | transition: background-color $transition; 61 | 62 | &:hover, 63 | &.selected { 64 | background-color: $violetOpacity; 65 | } 66 | } 67 | } 68 | } 69 | 70 | @media screen and (max-width: $mobile) { 71 | .tokenDropdown .trigger .dropdownMain { 72 | height: 28px; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/assets/style/layouts/_default.scss: -------------------------------------------------------------------------------- 1 | .defaultLayout { 2 | width: 100%; 3 | display: grid; 4 | grid-template-columns: 43% 1fr; 5 | grid-template-rows: 100%; 6 | color: $black2; 7 | &.loggedIn { 8 | .routerContainer { 9 | display: grid; 10 | grid-template-columns: 100%; 11 | grid-template-rows: max-content 1fr; 12 | } 13 | } 14 | 15 | @media screen and (min-width: 1300px) { 16 | grid-template-columns: 600px 1fr; 17 | } 18 | 19 | .routerContainer { 20 | position: relative; 21 | width: 100%; 22 | height: max-content; 23 | min-height: 100vh; 24 | 25 | .routeMain { 26 | max-width: 650px; 27 | margin: 0 auto; 28 | } 29 | } 30 | 31 | @media screen and (max-width: $mobile) { 32 | max-width: 650px; 33 | margin: 0 auto; 34 | grid-template-columns: 100%; 35 | grid-template-rows: max-content 1fr; 36 | 37 | .routerContainer { 38 | min-height: initial; 39 | height: 100%; 40 | 41 | .routeMain { 42 | max-width: calc(100vw - 2.5rem); 43 | } 44 | } 45 | } 46 | 47 | .modalContainer { 48 | &.full { 49 | @media screen and (min-width: $mobile+1) { 50 | width: 57% !important; 51 | left: 43% !important; 52 | } 53 | @media screen and (min-width: 1300px) { 54 | left: 600px !important; 55 | width: calc(100% - 600px) !important; 56 | } 57 | } 58 | } 59 | } 60 | 61 | .darkMode.defaultLayout { 62 | color: $white; 63 | background-color: $darkBlue; 64 | 65 | .routerContainer { 66 | background-color: $darkBlue; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/blocks/BottomMenu.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 39 | -------------------------------------------------------------------------------- /src/assets/style/components/_transactionToken.scss: -------------------------------------------------------------------------------- 1 | .amountInputGroup { 2 | .underInput .minAmount { 3 | width: max-content; 4 | height: 0; 5 | padding-left: 6px; 6 | overflow: hidden; 7 | opacity: 0; 8 | transition: $transition; 9 | transition-property: opacity, height, color; 10 | will-change: opacity, height, color; 11 | } 12 | 13 | &.focused, 14 | &.error { 15 | .underInput .minAmount { 16 | height: 12px; 17 | cursor: pointer; 18 | opacity: 1; 19 | } 20 | } 21 | 22 | &.error { 23 | .underInput .minAmount { 24 | color: $red; 25 | } 26 | } 27 | } 28 | 29 | @media screen and (max-width: 450px) { 30 | .transactionTokenContainer:not(.rowLayout) { 31 | .lineContainer { 32 | grid-template-areas: "token L2" "token L1" "rightSide rightSide"; 33 | grid-template-columns: 45px 1fr; 34 | grid-template-rows: repeat(3, max-content); 35 | 36 | .lineItem { 37 | .amount { 38 | margin-left: auto; 39 | text-align: right; 40 | } 41 | .text-red { 42 | margin-right: 0; 43 | } 44 | } 45 | .lineItem:nth-child(4) { 46 | width: 100%; 47 | 48 | .amountInputGroup { 49 | width: 100%; 50 | left: 0; 51 | display: grid; 52 | grid-template-columns: 1fr max-content; 53 | grid-template-rows: 100%; 54 | 55 | .leftSide { 56 | width: 100%; 57 | 58 | .amInputContainer { 59 | max-width: 100%; 60 | } 61 | } 62 | } 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/static/RampLogo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /environments/.env.goerli: -------------------------------------------------------------------------------- 1 | APP_WALLET_CONNECT=547924becf984d16b47dbbdaa0dc344d 2 | APP_ALCHEMY_API_KEY=547924becf984d16b47dbbdaa0dc344d 3 | APP_WS_API_ETHERSCAN_TOKEN=FTKI6X11ZGTQ9J1KZ6WIRQ2ZW8EYZ8Y7FC 4 | RECAPTCHA_SITE_KEY=6LeLrREaAAAAAMu8pgd66XVyy2tzwa8lWw7wRQan 5 | APP_IS_TEST=true 6 | SENTRY_DSN=https://674c7230d4594037bc1a9797339aa831@o496053.ingest.sentry.io/5662609 7 | SITE_TITLE="zkSync Lite Checkout — simple permissionless DeFi payment gateway" 8 | SITE_DESCRIPTION="zkSync Lite Checkout helps anyone permission-less adopt checkout backed by zkSync Lite, receive payments automatically and benefit from all the advantage of zkSync Lite Rollup: speed of the transaction, times lower cost of a single transaction, simplicity of withdrawal fund to the onchain-wallet." 9 | SITE_KEYWORDS="zkSync, Checkout, send payments in crypto, add payment gateway, cryptopayments, defi, rollup, ZK rollup, zero confirmation, ZKP, 10 | zero-knowledge proofs, Ethereum, crypto, blockchain, permissionless, L2, secure payments, scalable crypto payments" 11 | GTAG_ID=GTM-P9S4QSF 12 | SCREENING_API_URL=https://us-central1-screening-service.cloudfunctions.net/validate 13 | 14 | APP_INFURA_API_KEY=f60769ea380544dfa0d2c28bdf869a38 15 | APP_ONBOARDING_APP_ID=c4b83f7e-9bea-40d8-ad61-539f800863ee 16 | APP_FORTMATIC=pk_test_6CCD7EB1D7A9D0DB 17 | APP_PORTIS=68099bdc-3de9-4adc-ace0-e4664c1e77b4 18 | APP_CURRENT_NETWORK=goerli 19 | ZK_NETWORK="goerli" 20 | 21 | UNS_KEY=356a3eb3-d413-490a-94ac-4c9bb312ff49 22 | 23 | RAMP_MAINNET_HOST_API_KEY=j3b3oszn2vsr4qkz4att67zrucm6m6yjpfvxvtyy 24 | RAMP_GOERLI_HOST_API_KEY=pcrtzve8ax7vjamoct77ombequtqhuxbc8wdknwg 25 | RAMP_SEPOLIA_HOST_API_KEY=85quy9gjwceh3rnca2nffo6wq67q776u6rjkyajf 26 | 27 | -------------------------------------------------------------------------------- /environments/.env.mainnet: -------------------------------------------------------------------------------- 1 | APP_WALLET_CONNECT=80adc407f38b44c9afe416d95b8388ad 2 | APP_ALCHEMY_API_KEY=80adc407f38b44c9afe416d95b8388ad 3 | APP_WS_API_ETHERSCAN_TOKEN=FTKI6X11ZGTQ9J1KZ6WIRQ2ZW8EYZ8Y7FC 4 | RECAPTCHA_SITE_KEY=6LeLrREaAAAAAMu8pgd66XVyy2tzwa8lWw7wRQan 5 | APP_IS_TEST=false 6 | SENTRY_DSN=https://674c7230d4594037bc1a9797339aa831@o496053.ingest.sentry.io/5662609 7 | SITE_TITLE="zkSync Lite Checkout — simple permissionless DeFi payment gateway" 8 | SITE_DESCRIPTION="zkSync Lite Checkout helps anyone permission-less adopt checkout backed by zkSync Lite, receive payments automatically and benefit from all the advantage of zkSync Lite Rollup: speed of the transaction, times lower cost of a single transaction, simplicity of withdrawal fund to the onchain-wallet." 9 | SITE_KEYWORDS="zkSync, Checkout, send payments in crypto, add payment gateway, cryptopayments, defi, rollup, ZK rollup, zero confirmation, ZKP, 10 | zero-knowledge proofs, Ethereum, crypto, blockchain, permissionless, L2, secure payments, scalable crypto payments" 11 | GTAG_ID=GTM-P9S4QSF 12 | SCREENING_API_URL=https://us-central1-screening-service.cloudfunctions.net/validate 13 | 14 | APP_INFURA_API_KEY=f60769ea380544dfa0d2c28bdf869a38 15 | APP_ONBOARDING_APP_ID=d5160d1b-92f3-483b-8a54-ec8a5d1c100d 16 | APP_FORTMATIC=pk_live_6B0C5E23B6D8FD3A 17 | APP_PORTIS=2de02c10-9d10-4626-99cb-b71ebedab5c6 18 | APP_CURRENT_NETWORK=mainnet 19 | ZK_NETWORK="mainnet" 20 | 21 | UNS_KEY=356a3eb3-d413-490a-94ac-4c9bb312ff49 22 | 23 | RAMP_MAINNET_HOST_API_KEY=j3b3oszn2vsr4qkz4att67zrucm6m6yjpfvxvtyy 24 | RAMP_GOERLI_HOST_API_KEY=pcrtzve8ax7vjamoct77ombequtqhuxbc8wdknwg 25 | RAMP_SEPOLIA_HOST_API_KEY=85quy9gjwceh3rnca2nffo6wq67q776u6rjkyajf 26 | 27 | -------------------------------------------------------------------------------- /src/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 56 | -------------------------------------------------------------------------------- /src/blocks/modals.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 56 | -------------------------------------------------------------------------------- /src/plugins/link.ts: -------------------------------------------------------------------------------- 1 | import { isAddress } from "@ethersproject/address"; 2 | import { Address, TokenSymbol } from "zksync/build/types"; 3 | import { parseDecimal } from "@matterlabs/zksync-nuxt-core/utils"; 4 | import { RestProvider } from "zksync"; 5 | import { BigNumber } from "ethers"; 6 | import { UNSResolver } from "./uns"; 7 | import { PaymentItem, ZkTokens } from "@/types"; 8 | 9 | export const encrypt = (transactions: PaymentItem[]): string => { 10 | const hashedTransactions: string[] = []; 11 | for (const { address, token, amount } of transactions) { 12 | hashedTransactions.push([address, token, amount].join("|")); 13 | } 14 | return encodeURI(window.btoa(hashedTransactions.join("#")).replace(/=/g, "")); 15 | }; 16 | 17 | type DecryptedPaymentItem = { 18 | address: Address; 19 | token: TokenSymbol; 20 | amount: BigNumber; 21 | domain: string | null; 22 | }; 23 | 24 | export const decrypt = async ( 25 | hash: string, 26 | syncProvider: RestProvider, 27 | tokens: ZkTokens 28 | ): Promise => { 29 | const decoded = window.atob(decodeURI(hash)); 30 | const transactionHashes: string[] = decoded.split("#"); 31 | const transactions: DecryptedPaymentItem[] = []; 32 | const domainResolver = new UNSResolver(); 33 | for (const item of transactionHashes) { 34 | const [address, token, amount] = item.split("|"); 35 | const domainAddress = await domainResolver.lookupDomain(address, token); 36 | if ((!isAddress(address) && !domainAddress) || !Object.prototype.hasOwnProperty.call(tokens, token)) { 37 | continue; 38 | } 39 | try { 40 | transactions.push({ 41 | address: domainAddress || address, 42 | token, 43 | amount: parseDecimal(syncProvider, token, amount), 44 | domain: domainAddress ? address : null, 45 | }); 46 | } catch (error) { 47 | console.warn(`Failed to parse amount of ${amount} for token ${token}`); 48 | continue; 49 | } 50 | } 51 | return transactions; 52 | }; 53 | -------------------------------------------------------------------------------- /src/assets/style/pages/_index.scss: -------------------------------------------------------------------------------- 1 | .indexPage { 2 | width: 100%; 3 | 4 | .mainBtnsContainer { 5 | margin-top: 25px; 6 | 7 | .mainBtns { 8 | z-index: 3; 9 | display: flex; 10 | justify-content: center; 11 | 12 | button.defbtn.filled { 13 | line-height: 1.4; 14 | position: static; 15 | justify-content: center; 16 | font-size: 16px; 17 | text-align: center; 18 | font-weight: 400; 19 | min-width: 300px; 20 | width: max-content; 21 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 22 | border-radius: 4px; 23 | height: 55px; 24 | } 25 | } 26 | } 27 | 28 | .successPage { 29 | display: grid; 30 | grid-template-columns: 100%; 31 | grid-template-rows: repeat(5, max-content) 1fr; 32 | // height: calc(100vh - 1rem - 45px - 80px); 33 | 34 | .customScrollList { 35 | min-height: 80px; 36 | } 37 | } 38 | 39 | @media screen and (max-width: $mobile) { 40 | .successPage { 41 | height: auto; 42 | 43 | .customScrollList { 44 | max-height: 50vh; 45 | } 46 | } 47 | .mainBtnsContainer { 48 | left: 0; 49 | bottom: 0; 50 | margin-top: 0; 51 | width: 100%; 52 | 53 | .mainBtns { 54 | position: fixed; 55 | bottom: -1px; 56 | left: 0; 57 | width: 100%; 58 | background-color: $white; 59 | padding: 10px 1.25rem; 60 | box-shadow: 0 32px 64px rgba(17, 17, 17, 0.08); 61 | 62 | .defbtn { 63 | height: 50px; 64 | width: 100% !important; 65 | font-size: 18px; 66 | 67 | &:last-child { 68 | width: 100%; 69 | text-align: center; 70 | justify-content: center; 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | 78 | .darkMode .indexPage { 79 | @media screen and (max-width: $mobile) { 80 | .mainBtnsContainer .mainBtns { 81 | background-color: $darkBlue; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/blocks/SystemInfo.vue: -------------------------------------------------------------------------------- 1 | 26 | 57 | -------------------------------------------------------------------------------- /src/plugins/walletActions/transaction.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "ethers"; 2 | import { Wallet } from "zksync"; 3 | import { ZkSyncTransaction } from "zksync-checkout/build/types"; 4 | import { submitSignedTransactionsBatch } from "zksync/build/wallet"; 5 | import { TokenSymbol, Address } from "zksync/build/types"; 6 | import { screenAddress } from "@matterlabs/zksync-nuxt-core/utils/screening"; 7 | /** 8 | * Transaction processing action 9 | * 10 | * @param transactions 11 | * @param {TokenSymbol} feeToken 12 | * @param fee 13 | * @param nonce 14 | * @param store 15 | * @param statusFunction 16 | * @returns {Promise} 17 | */ 18 | export const transactionBatch = async ( 19 | transactions: Array, 20 | feeToken: TokenSymbol, 21 | fee: BigNumber, 22 | nonce: number, 23 | store: any, 24 | statusFunction: Function 25 | ) => { 26 | const syncWallet: Wallet = store.getters["zk-wallet/syncWallet"]; 27 | const batchBuilder = syncWallet.batchBuilder(nonce); 28 | await store.dispatch("zk-transaction/addCPKToBatch", batchBuilder); 29 | for (const tx of transactions) { 30 | batchBuilder.addTransfer({ 31 | fee: 0, 32 | amount: tx.amount, 33 | to: tx.to as Address, 34 | token: tx.token as string, 35 | }); 36 | } 37 | batchBuilder.addTransfer({ 38 | fee, 39 | amount: 0, 40 | to: syncWallet.address(), 41 | token: feeToken, 42 | }); 43 | 44 | if (process.env.SCREENING_API_URL) { 45 | await Promise.all([ 46 | ...transactions.map((e) => screenAddress(e.to, process.env.SCREENING_API_URL!)), 47 | store.dispatch("zk-account/screenAccountAddress", null, { root: true }), 48 | ]); 49 | } 50 | statusFunction("waitingUserConfirmation"); 51 | const batchTransactionData = await batchBuilder.build(); 52 | statusFunction("processing"); 53 | return await submitSignedTransactionsBatch( 54 | syncWallet.provider, 55 | batchTransactionData.txs, 56 | batchTransactionData.signature ? [batchTransactionData.signature] : undefined 57 | ); 58 | }; 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | /logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | *.pid.lock 15 | 16 | # Directory for instrumented libs generated by jscoverage/JSCover 17 | lib-cov 18 | 19 | # Coverage directory used by tools like istanbul 20 | coverage 21 | 22 | # nyc test coverage 23 | .nyc_output 24 | 25 | # cypress coverage 26 | cypress 27 | 28 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # Optional npm cache directory 48 | .npm 49 | 50 | # Optional eslint cache 51 | .eslintcache 52 | 53 | # Optional REPL history 54 | .node_repl_history 55 | 56 | # Output of 'npm pack' 57 | *.tgz 58 | 59 | # Yarn Integrity file 60 | .yarn-integrity 61 | 62 | # dotenv environment variables file 63 | .env 64 | 65 | # parcel-bundler cache (https://parceljs.org/) 66 | .cache 67 | 68 | # parcel-bundler cache (https://parceljs.org/) 69 | .brotli_cache/ 70 | .gzip_cache/ 71 | .assets_cache/ 72 | .buildx-cache/ 73 | 74 | # next.js build output 75 | .next 76 | 77 | # nuxt.js build output 78 | .nuxt 79 | _nuxt 80 | public 81 | 82 | 83 | # Nuxt generate 84 | dist 85 | 86 | # Serverless directories 87 | .serverless 88 | 89 | # IDE / Edito 90 | .idea 91 | .vscode 92 | 93 | # Service worker 94 | sw.* 95 | 96 | # macOS 97 | .DS_Store 98 | 99 | # Cache & swap files 100 | *.swp 101 | /.yarn/ 102 | /.firebase/ 103 | /.eslintcache/ 104 | 105 | # Cypress 106 | /cypress/screenshots/examples/misc.spec.js/ 107 | /cypress/videos/examples/ 108 | 109 | # unignore 110 | !/public/.gitignore 111 | !/.yarn/releases/ 112 | !/.yarn/plugins/ 113 | !/.husky/.gitignore 114 | -------------------------------------------------------------------------------- /src/plugins/uns.ts: -------------------------------------------------------------------------------- 1 | const reg = /^[.a-z0-9-]+$/; 2 | 3 | const resolutionService = "https://resolve.unstoppabledomains.com/domains/"; 4 | const tldAPI = "https://resolve.unstoppabledomains.com/supported_tlds"; 5 | export class UNSResolver { 6 | supportedTlds: string[] = []; 7 | domainData = new Map(); 8 | 9 | async lookupDomain(address: string, ticker: string = "ETH"): Promise { 10 | try { 11 | const domain = this.preparedDomain(address); 12 | if (domain !== "" && (await this.isValidTLD(domain))) { 13 | const response = await fetch(resolutionService + domain, { 14 | method: "get", 15 | headers: new Headers({ 16 | Authorization: "Bearer " + process.env.UNS_KEY, 17 | }), 18 | }); 19 | const data = await response.json(); 20 | if (data.records) { 21 | this.domainData.set(domain, data.records); 22 | return data.records[this.getUNSKey(ticker)] 23 | ? data.records[this.getUNSKey(ticker)] 24 | : data.records[this.getUNSKey("ETH")]; 25 | } 26 | return null; 27 | } 28 | return null; 29 | } catch (e) { 30 | return null; 31 | } 32 | } 33 | 34 | getDomain(address: string, ticker: string = "ETH"): string | null { 35 | const domainWithTicker = this.domainData.get(address)?.[this.getUNSKey(ticker)]; 36 | return domainWithTicker || this.domainData.get(address)?.[this.getUNSKey("ETH")]; 37 | } 38 | 39 | isValidAddress(address: string, ticker: string = "ETH"): boolean { 40 | return !!this.getDomain(address, ticker); 41 | } 42 | 43 | async isValidTLD(domain: string): Promise { 44 | if (this.supportedTlds.length === 0) { 45 | const response = await fetch(tldAPI); 46 | const data = await response.json(); 47 | if (data.tlds) { 48 | this.supportedTlds = data.tlds; 49 | } 50 | } 51 | return this.supportedTlds?.some((tld) => domain.endsWith(tld)) ?? false; 52 | } 53 | 54 | preparedDomain(domain: string): string { 55 | const retVal = domain ? domain.trim().toLowerCase() : ""; 56 | if (reg.test(retVal)) { 57 | return retVal; 58 | } 59 | return ""; 60 | } 61 | 62 | getUNSKey(ticker: string): string { 63 | return "crypto." + ticker + ".address"; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/plugins/useLinkHash.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "@nuxt/types"; 2 | import { RestProvider } from "zksync"; 3 | import { Address } from "zksync/build/types"; 4 | import { decrypt } from "@/plugins/link"; 5 | 6 | export default async ({ store, route, redirect }: Context, hash: string) => { 7 | const onError = async () => { 8 | await store.dispatch("openModal", "zkLinkParseFail"); 9 | redirect("/link"); 10 | }; 11 | try { 12 | store.commit("checkout/setLinkCheckoutState", true); 13 | const syncProvider: RestProvider = await store.dispatch("zk-provider/requestProvider"); 14 | await store.dispatch("zk-tokens/loadZkTokens"); 15 | const transactions = await decrypt(hash, syncProvider, store.getters["zk-tokens/zkTokens"]); 16 | store.commit("checkout/setError", false); 17 | if (transactions.length === 0) { 18 | return await onError(); 19 | } 20 | const totalByToken = Object.fromEntries(transactions.map((e) => [e.token, 0])); 21 | const domains = new Map(); 22 | transactions.forEach((e) => { 23 | if (e.domain) { 24 | domains.set(e.address, e.domain); 25 | } 26 | }); 27 | 28 | transactions.forEach((e) => totalByToken[e.token]++); 29 | let highestNumberSymbol = transactions[0].token; 30 | transactions.forEach((e) => { 31 | if (totalByToken[highestNumberSymbol] < totalByToken[e.token]) highestNumberSymbol = e.token; 32 | }); 33 | store.dispatch("checkout/setTransactionData", { 34 | transactions: transactions.map((e, index) => ({ 35 | to: e.address, 36 | token: e.token, 37 | amount: e.amount, 38 | description: `Payment ${index + 1}`, 39 | })), 40 | feeToken: highestNumberSymbol, 41 | fromAddress: undefined, 42 | domains, 43 | }); 44 | store.dispatch("checkout/requestUsedTokensPrice"); 45 | if (store.getters["zk-account/loggedIn"]) { 46 | await Promise.all([ 47 | store.dispatch("checkout/requestInitialData"), 48 | store.dispatch("zk-account/updateAccountState", true), 49 | ]); 50 | } 51 | if (!route.path.startsWith("/connect")) { 52 | redirect({ path: "/connect", query: { link: hash } }); 53 | } 54 | } catch (error) { 55 | console.warn("zkLink error", error); 56 | await onError(); 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /src/assets/style/blocks/_footer.scss: -------------------------------------------------------------------------------- 1 | .zk-footer-space { 2 | width: 100%; 3 | height: 60px; 4 | } 5 | 6 | .zkFooterContainer { 7 | width: 100%; 8 | height: 60px; 9 | bottom: 0; 10 | left: 0; 11 | padding-bottom: 10px; 12 | align-self: end; 13 | 14 | a, 15 | span { 16 | @apply text-sm font-light leading-normal; 17 | } 18 | 19 | @media screen and (max-width: $mobile) { 20 | height: 73px; 21 | text-align: center; 22 | bottom: 1rem; 23 | } 24 | 25 | .colorTheme { 26 | width: 45px; 27 | height: 45px; 28 | border-radius: 50%; 29 | background-color: transparentize($color: $gray, $amount: 0.7); 30 | text-align: center; 31 | line-height: 45px; 32 | cursor: pointer; 33 | transition: background-color $transition; 34 | will-change: background-color; 35 | 36 | &:hover { 37 | background-color: transparentize($color: $gray, $amount: 0.5); 38 | } 39 | 40 | &.dark i { 41 | transform: rotate(-180deg); 42 | } 43 | 44 | i { 45 | transition: $transition; 46 | transition-property: transform; 47 | will-change: transform; 48 | } 49 | } 50 | 51 | .rightSide { 52 | @apply flex items-baseline w-full text-right justify-end; 53 | 54 | .system-info { 55 | @apply flex items-baseline; 56 | } 57 | } 58 | 59 | #system-b-popover { 60 | cursor: help; 61 | } 62 | } 63 | 64 | .darkMode .zkFooterContainer .rightSide { 65 | .linkDefault { 66 | font-weight: 400; 67 | color: $lightViolet; 68 | } 69 | } 70 | 71 | .defaultLayout { 72 | @media screen and (min-width: $mobile) { 73 | &:not(.loggedIn) { 74 | .zk-footer-space { 75 | display: none; 76 | } 77 | } 78 | } 79 | 80 | @media screen and (max-width: $mobile) { 81 | &.footerUpStyle { 82 | .zk-footer-space { 83 | height: 110px; 84 | } 85 | 86 | .zkFooterContainer { 87 | padding-bottom: 80px; 88 | } 89 | } 90 | } 91 | } 92 | 93 | .darkMode .zkFooterContainer { 94 | .colorTheme { 95 | background-color: transparentize($color: $black2, $amount: 0.5); 96 | 97 | &:hover { 98 | background-color: transparentize($color: $black2, $amount: 0.3); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/components/BuyWithRampBtn.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 72 | -------------------------------------------------------------------------------- /src/blocks/loggingIn.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 70 | -------------------------------------------------------------------------------- /cli-process-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | NETWORK="$1" 4 | ENV_VALUE="$2" 5 | FORCE_REWRITE="$3" 6 | LOCAL_RUN="$4" 7 | 8 | # Colors 9 | Red="\033[0;31m" # Red 10 | Default="\033[1;37m" # White 11 | Green="\033[0;32m" # Green 12 | Blue="\033[0;34m" # Blue 13 | BBlue="\033[1;34m" # Bold Blue 14 | 15 | if [ -z $NETWORK ]; then 16 | NETWORK="goerli" 17 | fi 18 | 19 | if [ -z $ENV_VALUE ]; then 20 | ENV_VALUE="dev" 21 | fi 22 | 23 | FILE=".env" 24 | 25 | printf "\n${BBlue}Configuration:\n\n" "" "$NC" 26 | 27 | printf "${Default} Environment:" 28 | if [ $ENV_VALUE = "prod" ]; then 29 | echo "${Red} dev" 30 | else 31 | echo "${Green} $ENV_VALUE" 32 | fi 33 | 34 | echo "${Default} Ethereum network: ${Green}$NETWORK" 35 | 36 | printf "${Default} Localhost:" 37 | 38 | if [ -z $LOCAL_RUN ]; then 39 | echo "${Green} nope" 40 | else 41 | echo "${Red} yes" 42 | fi 43 | 44 | 45 | printf "${Default} Generate .env anyway:" 46 | 47 | if [ -n $FORCE_REWRITE ]; then 48 | echo "${Red} yes" 49 | else 50 | echo "${Green} nope" 51 | fi 52 | 53 | printf "${BBlue}\nSearching for .env...\n\n" 54 | printf "${Default} File status: " 55 | 56 | if [ -e $FILE ]; then 57 | echo "${Green}.env found" 58 | else 59 | echo "${Default} Configuring .env for $NETWORK ethereum network" 60 | FORCE_REWRITE=1 61 | fi 62 | 63 | printf "\n${BBlue}Resolving...\n\n" 64 | 65 | if [ -z $FORCE_REWRITE ]; then 66 | printf "${Default} No changes made\n\n" 67 | echo "${Red} Exiting..." 68 | exit 69 | else 70 | printf "${Red} Overriding found .env\n\n" 71 | fi 72 | 73 | GIT_VERSION="APP_GIT_VERSION=$(git tag -l | tail -n1)" 74 | GIT_REVISION="APP_GIT_REVISION=$(git rev-parse --short HEAD)" 75 | GIT_UPDATED_AT="APP_GIT_UPDATED_AT=\"$(git log -1 --format=%cd)\"" 76 | APP_ENV="APP_ENV=$ENV_VALUE" 77 | 78 | rm -f ./.env && 79 | cp "environments/.env.$NETWORK" ".env" && 80 | echo "$GIT_VERSION" >>".env" && 81 | echo "$GIT_REVISION" >>".env" && 82 | echo "$APP_ENV" >>".env" && 83 | echo "$GIT_UPDATED_AT" >>".env" 84 | 85 | printf "${BBlue}Defining host...\n\n" 86 | 87 | if [ -z $LOCAL_RUN ]; then 88 | echo "${Green} Remote host detected." 89 | else 90 | echo "${Red} Localhost detected:" 91 | echo "" 92 | echo "${Default} Sentry: ${Red}Disabled" 93 | echo "${Default} GTM: ${Red}Disabled" 94 | echo "" 95 | echo "IS_LOCALHOST=1" >> ".env" 96 | fi 97 | echo "" 98 | echo "${Green} ✅ Environment configured successfully!" 99 | -------------------------------------------------------------------------------- /src/pages/connect.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 74 | -------------------------------------------------------------------------------- /src/components/PaymentItem.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 95 | -------------------------------------------------------------------------------- /src/plugins/build.ts: -------------------------------------------------------------------------------- 1 | import { Network } from "zksync/build/types.d"; 2 | import { version as zkSyncVersion } from "zksync/package.json"; 3 | import { NuxtOptionsBuild } from "@nuxt/types/config/build"; 4 | import { version } from "../../package.json"; 5 | import { ZKIRampConfig } from "~/types"; 6 | 7 | export const GIT_REVISION: string = process.env.APP_GIT_REVISION ? process.env.APP_GIT_REVISION.toString() : ""; 8 | export const GIT_REVISION_SHORT: string = GIT_REVISION.length > 8 ? GIT_REVISION.slice(-7) : GIT_REVISION; 9 | export const VERSION: string = version; 10 | export const ETHER_NETWORK_NAME: Network = process.env.APP_CURRENT_NETWORK as Network; 11 | export const ETHER_PRODUCTION: boolean = ETHER_NETWORK_NAME === "mainnet"; 12 | 13 | const env = process.env.APP_ENV ?? "dev"; 14 | export const isProduction: boolean = ETHER_PRODUCTION && env === "prod"; 15 | export const isDebugEnabled: boolean = env === "dev"; 16 | 17 | const nuxtBuildOptionsDefault: NuxtOptionsBuild = { 18 | corejs: 3, 19 | ssr: false, 20 | babel: { 21 | plugins: ["@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-nullish-coalescing-operator"], 22 | }, 23 | }; 24 | const nuxtBuildProdOptions: NuxtOptionsBuild = { 25 | ...nuxtBuildOptionsDefault, 26 | babel: { 27 | compact: true, 28 | ...nuxtBuildOptionsDefault.babel, 29 | }, 30 | extractCSS: { 31 | ignoreOrder: true, 32 | }, 33 | optimization: { 34 | removeAvailableModules: true, 35 | flagIncludedChunks: true, 36 | mergeDuplicateChunks: true, 37 | splitChunks: { 38 | chunks: "all", 39 | name: isProduction ? undefined : "chunk", 40 | maxSize: 900 * 1024, 41 | }, 42 | nodeEnv: isProduction ? "14" : false, 43 | minimize: isProduction, 44 | }, 45 | hardSource: isProduction, 46 | }; 47 | 48 | export const nuxtBuildConfig = isProduction ? nuxtBuildProdOptions : nuxtBuildOptionsDefault; 49 | 50 | export const ZK_LIB_VERSION = zkSyncVersion ?? "latest"; 51 | export const ETHER_NETWORK_CAPITALIZED = `${ETHER_NETWORK_NAME.charAt(0).toUpperCase()}${ETHER_NETWORK_NAME?.slice(1)}`; 52 | export const CURRENT_APP_NAME = "zkSync Lite Checkout"; 53 | 54 | export const CURRENT_APP_TITLE = process.env.SITE_TITLE || CURRENT_APP_NAME; 55 | 56 | export const ZK_NETWORK: string = process.env.ZK_NETWORK ? process.env.ZK_NETWORK : ETHER_NETWORK_NAME; 57 | 58 | /** 59 | * zkLink 60 | */ 61 | export const TWEET_URL = "https://twitter.com/intent/tweet?url="; 62 | export const FACEBOOK_URL = "https://www.facebook.com/sharer/sharer.php?u="; 63 | 64 | export const rampConfig: ZKIRampConfig = { 65 | mainnet: { 66 | url: undefined, // default 67 | hostApiKey: process.env.RAMP_MAINNET_HOST_API_KEY as string, 68 | }, 69 | goerli: { 70 | url: undefined, 71 | hostApiKey: process.env.RAMP_GOERLI_HOST_API_KEY as string, 72 | }, 73 | }; 74 | -------------------------------------------------------------------------------- /src/assets/style/_default.scss: -------------------------------------------------------------------------------- 1 | #__nuxt, 2 | #__layout { 3 | width: 100%; 4 | min-height: 100vh; 5 | } 6 | 7 | #nuxt-loading { 8 | will-change: opacity; 9 | 10 | & > div { 11 | will-change: transform; 12 | } 13 | } 14 | 15 | body { 16 | overscroll-behavior-y: none; 17 | font-family: $mainFontFamily; 18 | color: $black2; 19 | width: 100%; 20 | min-height: 100vh; 21 | @media (max-width: $mobile) { 22 | font-size: 1rem; 23 | } 24 | } 25 | 26 | a { 27 | &:hover { 28 | color: darken($violet, 0.5); 29 | } 30 | } 31 | 32 | head { 33 | display: none; 34 | } 35 | 36 | input::-webkit-outer-spin-button, 37 | input::-webkit-inner-spin-button { 38 | -webkit-appearance: none; 39 | margin: 0; 40 | } 41 | 42 | input[type="number"] { 43 | -moz-appearance: textfield; 44 | } 45 | 46 | .desktopOnly { 47 | @media screen and (max-width: ($mobile+1)) { 48 | display: none !important; 49 | } 50 | } 51 | 52 | .mobileOnly { 53 | @media screen and (min-width: $mobile) { 54 | display: none !important; 55 | } 56 | } 57 | 58 | .customScrollList { 59 | overflow: auto; 60 | width: calc(100% + 10px); 61 | padding-right: 10px; 62 | &::-webkit-scrollbar { 63 | width: 0; 64 | height: 0; 65 | } 66 | 67 | .ps__rail-y { 68 | opacity: 0.7; 69 | width: 6px; 70 | will-change: top, opacity; 71 | transition: opacity $transition; 72 | 73 | &:hover, 74 | &.ps--clicking { 75 | opacity: 1; 76 | background-color: transparent !important; 77 | 78 | .ps__thumb-y { 79 | width: 10px; 80 | } 81 | } 82 | 83 | .ps__thumb-y { 84 | left: 0; 85 | width: 6px !important; 86 | background-color: lighten($color: $gray, $amount: 5) !important; 87 | border-radius: 6px !important; 88 | will-change: width, height, background-color, top; 89 | transition: $transition; 90 | transition-property: width, height; 91 | } 92 | } 93 | } 94 | 95 | .iconBtn { 96 | color: $violet; 97 | cursor: pointer; 98 | transition: $transition; 99 | &:disabled { 100 | opacity: 0.7; 101 | cursor: default; 102 | } 103 | &:not(:disabled):active { 104 | transition-duration: 0.001s; 105 | color: $violet; 106 | } 107 | &:not(:disabled):hover { 108 | color: $lightViolet; 109 | } 110 | } 111 | 112 | .fitRightSide { 113 | z-index: 1; 114 | width: 57% !important; 115 | left: 43% !important; 116 | 117 | @media screen and (min-width: 1300px) { 118 | left: 600px !important; 119 | width: calc(100% - 600px) !important; 120 | } 121 | } 122 | 123 | /** 124 | * New common element 125 | */ 126 | .brandContainer { 127 | width: max-content; 128 | position: relative; 129 | 130 | .networkName { 131 | position: absolute; 132 | left: 100%; 133 | top: -0.4em; 134 | 135 | @media screen and (max-width: $mobile) { 136 | @apply transform -translate-x-1/2 -translate-y-3; 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": [ 3 | { 4 | "target": "prod-mainnet", 5 | "public": "public", 6 | "ignore": [ 7 | "firebase.json", 8 | "**/.*", 9 | "**/node_modules/**" 10 | ], 11 | "rewrites": [ 12 | { 13 | "source": "**", 14 | "destination": "/index.html" 15 | } 16 | ], 17 | "headers": [ 18 | { 19 | "source": "**", 20 | "headers": [ 21 | { 22 | "key": "Referrer-Policy", 23 | "value": "no-referrer, strict-origin-when-cross-origin" 24 | }, 25 | { 26 | "key": "X-Content-Type-Options", 27 | "value": "nosniff" 28 | }, 29 | { 30 | "key": "X-Frame-Options", 31 | "value": "DENY" 32 | }, 33 | { 34 | "key": "X-XSS-Protection", 35 | "value": "1; mode=block" 36 | } 37 | ] 38 | } 39 | ] 40 | }, 41 | { 42 | "target": "prod-goerli", 43 | "public": "public", 44 | "ignore": [ 45 | "firebase.json", 46 | "**/.*", 47 | "**/node_modules/**" 48 | ], 49 | "rewrites": [ 50 | { 51 | "source": "**", 52 | "destination": "/index.html" 53 | } 54 | ], 55 | "headers": [ 56 | { 57 | "source": "**", 58 | "headers": [ 59 | { 60 | "key": "Referrer-Policy", 61 | "value": "no-referrer, strict-origin-when-cross-origin" 62 | }, 63 | { 64 | "key": "X-Content-Type-Options", 65 | "value": "nosniff" 66 | }, 67 | { 68 | "key": "X-Frame-Options", 69 | "value": "DENY" 70 | }, 71 | { 72 | "key": "X-XSS-Protection", 73 | "value": "1; mode=block" 74 | } 75 | ] 76 | } 77 | ] 78 | }, 79 | { 80 | "target": "staging", 81 | "public": "public", 82 | "ignore": [ 83 | "firebase.json", 84 | "**/.*", 85 | "**/node_modules/**" 86 | ], 87 | "rewrites": [ 88 | { 89 | "source": "**", 90 | "destination": "/index.html" 91 | } 92 | ], 93 | "headers": [ 94 | { 95 | "source": "**", 96 | "headers": [ 97 | { 98 | "key": "Referrer-Policy", 99 | "value": "no-referrer, strict-origin-when-cross-origin" 100 | }, 101 | { 102 | "key": "X-Content-Type-Options", 103 | "value": "nosniff" 104 | }, 105 | { 106 | "key": "X-Frame-Options", 107 | "value": "DENY" 108 | }, 109 | { 110 | "key": "X-XSS-Protection", 111 | "value": "1; mode=block" 112 | } 113 | ] 114 | } 115 | ] 116 | } 117 | ] 118 | } 119 | -------------------------------------------------------------------------------- /src/blocks/connectedWallet.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 85 | -------------------------------------------------------------------------------- /src/assets/style/components/_amountInput.scss: -------------------------------------------------------------------------------- 1 | .amountInputGroup { 2 | width: max-content; 3 | height: 36px; 4 | padding-left: 0; 5 | padding-right: 2px; 6 | display: flex; 7 | align-items: center; 8 | border: 1px solid $light; 9 | transition: $transition; 10 | transition-property: border-color; 11 | will-change: border-color; 12 | 13 | &:not(:disabled) { 14 | &.focused { 15 | border-color: $violet; 16 | 17 | .leftSide { 18 | .amInputContainer { 19 | .penIcon { 20 | color: $violet; 21 | } 22 | } 23 | } 24 | } 25 | 26 | .leftSide { 27 | cursor: text; 28 | } 29 | } 30 | 31 | &.error { 32 | border-color: $red !important; 33 | 34 | .leftSide .amInputContainer input { 35 | color: $red; 36 | } 37 | } 38 | 39 | &.hasUnderInput { 40 | .leftSide { 41 | height: 100%; 42 | } 43 | } 44 | 45 | .leftSide { 46 | display: flex; 47 | width: max-content; 48 | height: max-content; 49 | flex-direction: column; 50 | justify-content: center; 51 | align-items: flex-end; 52 | cursor: default; 53 | 54 | .amInputContainer { 55 | height: 18px; 56 | width: max-content; 57 | display: flex; 58 | align-items: center; 59 | 60 | input, 61 | .sizeSpan { 62 | display: block; 63 | font-family: $firaSans; 64 | font-size: 16px; 65 | font-weight: 600; 66 | } 67 | 68 | input { 69 | position: relative; 70 | left: 2px; 71 | height: 100%; 72 | width: 0; 73 | min-width: 50px; 74 | line-height: 18px; 75 | color: $black2; 76 | text-align: right; 77 | background-color: transparent; 78 | outline: none !important; 79 | border: none; 80 | transition: 0.05s ease; 81 | transition-property: width; 82 | will-change: width; 83 | padding: 0 2px; 84 | box-sizing: content-box; 85 | 86 | &::placeholder { 87 | color: $gray; 88 | font-weight: 400; 89 | font-size: 14px; 90 | } 91 | } 92 | 93 | @media screen and (max-width: $breakpoint1) { 94 | max-width: 100px; 95 | } 96 | 97 | .sizeSpan { 98 | position: absolute; 99 | width: max-content; 100 | z-index: -9999; 101 | left: -9999px; 102 | top: -9999px; 103 | opacity: 0; 104 | } 105 | 106 | .penIcon { 107 | transition: $transition; 108 | transition-property: color; 109 | will-change: color; 110 | color: transparentize($color: $gray, $amount: 0.4); 111 | width: 15px; 112 | pointer-events: none; 113 | 114 | i { 115 | display: block; 116 | font-size: 10px; 117 | line-height: 18px; 118 | padding-left: 5px; 119 | } 120 | } 121 | } 122 | 123 | .underInput { 124 | color: $gray; 125 | font-size: 9px; 126 | line-height: 12px; 127 | font-family: $firaCondensed; 128 | } 129 | } 130 | 131 | .rightSide { 132 | height: max-content; 133 | width: max-content; 134 | padding-left: 7px; 135 | } 136 | } 137 | 138 | .darkMode .amountInputGroup { 139 | border-color: $dark2; 140 | &:not(:disabled).focused { 141 | border-color: $lightViolet; 142 | 143 | .leftSide .amInputContainer .penIcon { 144 | color: $lightViolet; 145 | } 146 | } 147 | .leftSide { 148 | .amInputContainer input { 149 | color: $gray; 150 | 151 | &::placeholder { 152 | color: $dark2; 153 | } 154 | } 155 | .underInput { 156 | color: $dark3; 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/components/AddressInput.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 118 | -------------------------------------------------------------------------------- /.github/workflows/deploy-prod.yml: -------------------------------------------------------------------------------- 1 | name: "Deploy production" 2 | "on": 3 | workflow_dispatch: 4 | inputs: 5 | ref: 6 | type: string 7 | description: "The branch, tag or SHA to checkout" 8 | required: true 9 | default: "main" 10 | preview_env: 11 | type: string 12 | description: "Test on goerli or mainnet" 13 | required: true 14 | default: "goerli" 15 | jobs: 16 | build_and_preview: 17 | if: ${{ github.event.repository.full_name == github.repository }} 18 | runs-on: ubuntu-latest 19 | environment: dev 20 | steps: 21 | - uses: actions/checkout@v2 22 | with: 23 | ref: "${{ github.event.inputs.ref }}" 24 | 25 | - name: "Enable yarn cache" 26 | uses: c-hive/gha-yarn-cache@v2 # using cache 27 | 28 | - name: "Setup node@16" 29 | uses: actions/setup-node@v2 30 | with: 31 | node-version: 16 32 | 33 | - name: "Install dependencies" 34 | run: | 35 | yarn set version 3.1.1 36 | yarn install 37 | 38 | - name: "Build: env:${{github.event.inputs.preview_env}} for the preview" 39 | run: "yarn run ci:build:${{github.event.inputs.preview_env}}" 40 | - uses: actions/upload-artifact@v2 41 | with: 42 | name: "checkout-dist" 43 | path: "public/" 44 | if-no-files-found: error # "warn" or "ignore" are also available, defaults to `warn` 45 | retention-days: 5 46 | 47 | - name: "Setup node@18" 48 | uses: actions/setup-node@v2 49 | with: 50 | node-version: 18 51 | 52 | - name: "Deploy: preview prod-${{github.event.inputs.preview_env}}" 53 | uses: matter-labs/action-hosting-deploy@main 54 | with: 55 | repoToken: "${{secrets.GITHUB_TOKEN}}" 56 | firebaseServiceAccount: "${{secrets.FIREBASE_SERVICE_ACCOUNT_ZSYNC_DAPP_CHECKOUT_DEV}}" 57 | target: "staging" 58 | projectId: zsync-dapp-checkout 59 | channelID: "staging_preview" 60 | 61 | build_and_deploy: 62 | if: ${{ github.event.repository.full_name == github.repository }} 63 | needs: build_and_preview 64 | runs-on: ubuntu-latest 65 | environment: prod 66 | steps: 67 | - uses: actions/checkout@v2 68 | with: 69 | ref: "${{github.event.inputs.ref}}" 70 | 71 | - name: "Enable yarn cache" 72 | uses: c-hive/gha-yarn-cache@v2 # using cache 73 | 74 | - name: "Setup node@16" 75 | uses: actions/setup-node@v2 76 | with: 77 | node-version: 16 78 | 79 | - name: "Install dependencies" 80 | run: | 81 | yarn set version 3.1.1 82 | yarn install 83 | 84 | - name: "Deploy target: prod-mainnet" 85 | run: yarn run ci:build:mainnet 86 | 87 | - name: "Setup node@18" 88 | uses: actions/setup-node@v2 89 | with: 90 | node-version: 18 91 | 92 | - uses: matter-labs/action-hosting-deploy@main 93 | with: 94 | repoToken: "${{ secrets.GITHUB_TOKEN }}" 95 | firebaseServiceAccount: "${{ secrets.FIREBASE_SERVICE_ACCOUNT_ZSYNC_DAPP_CHECKOUT_PROD }}" 96 | target: "prod-mainnet" 97 | projectId: zsync-dapp-checkout 98 | channelID: live 99 | 100 | - name: "Setup node@16" 101 | uses: actions/setup-node@v2 102 | with: 103 | node-version: 16 104 | 105 | - name: "Build goerli" 106 | run: yarn run ci:build:goerli 107 | 108 | - name: "Setup node@18" 109 | uses: actions/setup-node@v2 110 | with: 111 | node-version: 18 112 | 113 | - uses: matter-labs/action-hosting-deploy@main 114 | with: 115 | repoToken: "${{ secrets.GITHUB_TOKEN }}" 116 | firebaseServiceAccount: "${{ secrets.FIREBASE_SERVICE_ACCOUNT_ZSYNC_DAPP_CHECKOUT_PROD }}" 117 | target: "prod-goerli" 118 | projectId: zsync-dapp-checkout 119 | channelID: live 120 | -------------------------------------------------------------------------------- /src/static/eth.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 11 | 14 | 17 | 20 | 21 | -------------------------------------------------------------------------------- /src/assets/style/blocks/_infoBlock.scss: -------------------------------------------------------------------------------- 1 | .infoBlockContainer { 2 | z-index: 4; 3 | width: 100%; 4 | height: 100%; 5 | overflow: auto; 6 | box-shadow: 0 32px 64px rgba(17, 17, 17, 0.08); 7 | 8 | .infoBlock { 9 | position: fixed; 10 | width: 43vw; 11 | height: 100vh; 12 | display: grid; 13 | grid-template-columns: 100%; 14 | grid-template-rows: repeat(3, max-content) 1fr; 15 | @media screen and (min-width: 1300px) { 16 | width: 600px; 17 | } 18 | @media screen and (min-width: $mobile) { 19 | .totalTokensList { 20 | max-height: 19vh; 21 | overflow: auto; 22 | } 23 | } 24 | 25 | .valuesBlockContainer { 26 | align-items: center; 27 | } 28 | 29 | .logo-container { 30 | display: inline-flex; 31 | 32 | .zkSyncLogo { 33 | @apply mr-2 relative top-0.5; 34 | 35 | height: 1.8rem; 36 | max-height: 100%; 37 | } 38 | } 39 | 40 | .brandContainer { 41 | .networkName { 42 | position: absolute; 43 | left: 100%; 44 | top: -0.4em; 45 | 46 | @media screen and (max-width: $mobile) { 47 | left: auto !important; 48 | top: auto; 49 | } 50 | } 51 | } 52 | 53 | sup { 54 | top: -1em; 55 | } 56 | 57 | header { 58 | @media screen and (min-width: $mobile+1) { 59 | height: 45px; 60 | } 61 | } 62 | 63 | .transactionsList { 64 | height: max-content; 65 | max-height: 30vh; 66 | } 67 | 68 | .footerContainer { 69 | z-index: 1; 70 | height: max-content; 71 | align-self: end; 72 | 73 | .zkSyncFooter { 74 | z-index: -1; 75 | width: 100%; 76 | height: auto; 77 | opacity: 0.6; 78 | } 79 | } 80 | 81 | footer { 82 | width: 100%; 83 | height: max-content; 84 | } 85 | 86 | @media screen and (max-width: $mobile) { 87 | position: relative; 88 | width: 100%; 89 | height: max-content; 90 | 91 | .valuesBlockContainer { 92 | display: flex; 93 | align-items: center; 94 | justify-content: space-between; 95 | grid-gap: 0.5rem; 96 | white-space: nowrap; 97 | margin: 0; 98 | 99 | .valuesBlockRight .valuesBlockSecondary > div { 100 | height: 100%; 101 | min-height: 36px; 102 | 103 | .secondaryValue { 104 | font-size: 0.65rem !important; 105 | } 106 | } 107 | 108 | .valuesBlockLeft { 109 | max-width: 70%; 110 | } 111 | 112 | &.noAddressPadding .valuesBlockRight .valuesBlockSecondary { 113 | flex-basis: auto; 114 | width: auto; 115 | overflow: hidden; 116 | 117 | .address { 118 | max-width: calc(100% - 0.5rem); 119 | overflow: hidden; 120 | min-height: 0; 121 | } 122 | } 123 | } 124 | .infoBlock { 125 | height: auto; 126 | max-height: 33vh; 127 | grid-template-rows: max-content 1fr repeat(2, max-content); 128 | 129 | header { 130 | display: flex; 131 | align-items: center; 132 | justify-content: space-between; 133 | } 134 | 135 | .transactionsList { 136 | height: 100%; 137 | max-height: initial; 138 | min-height: initial; 139 | } 140 | } 141 | } 142 | } 143 | 144 | .linkDefault { 145 | font-weight: 300; 146 | font-size: 14px; 147 | } 148 | 149 | .darkMode .infoBlockContainer { 150 | background-color: $darkBlue; 151 | 152 | .linkDefault { 153 | font-weight: 400; 154 | color: $lightViolet; 155 | } 156 | } 157 | 158 | .border-b-2, 159 | .border-b-1 { 160 | border-bottom: 1px !important; 161 | 162 | &:last-of-type { 163 | display: none; 164 | } 165 | } 166 | 167 | @media (min-width: $mobile) { 168 | .footerContainer { 169 | margin-bottom: -10px; 170 | } 171 | } 172 | } 173 | 174 | .valuesBlockContainer .address { 175 | position: relative; 176 | width: max-content; 177 | min-width: 30px; 178 | max-width: 100%; 179 | overflow: hidden; 180 | color: $black2; 181 | font-weight: 400; 182 | font-size: 16px; 183 | white-space: nowrap; 184 | text-overflow: ellipsis; 185 | transition: color $transition; 186 | will-change: color; 187 | 188 | &::after { 189 | position: absolute; 190 | z-index: 10; 191 | width: 2px; 192 | height: 100%; 193 | background: rgba(255, 255, 255, 0.3); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/static/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/components/AmountInput.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [zkSync Lite Checkout](https://checkout.zksync.io) · [zkSync.io](https://zksync.io/) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE-MIT) [![GitHub license](https://img.shields.io/badge/license-Apache%202-blue)](./LICENSE-APACHE) 2 | 3 | # zkSync Lite Checkout — trustable permissionless DeFi payment gateway 4 | 5 | zkSync Lite Checkout helps anyone permission-less adopt checkout backed by zkSync, receive payments automatically and benefit from all the advantage of zkSync Lite Rollup: speed of the transaction, times lower cost of a single transaction, simplicity of withdrawal fund to the onchain-wallet. 6 | 7 | * **[SDK documentation](https://zksync.io/api/sdk/checkout/)** | [**Changelog**](CHANGELOG.md) 8 | 9 | ## Deployment 10 | 11 | zkSync Checkout uses firebase hosting. 12 | Resource targets for the zkCheckout are: 13 | 14 | ### Available Hosts 15 | 16 | * [```prod-mainnet```](https://checkout.zksync.io) 17 | * [```prod-goerli```](https://checkout-goerli.zksync.io) 18 | * [```staging```](https://staging-checkout-v1.zksync.dev/link) 19 | 20 | ## Initial Setup / Static version generation 21 | 22 | ``` bash 23 | # install dependencies 24 | $ sh cli-dev.sh ci 25 | 26 | # Populate .env file as of goerli connection && serve with hot reload at localhost:3000 27 | $ yarn dev 28 | 29 | # static version generation 30 | $ yarn ci:build:goerli 31 | # afterward you'll have prepared distributive in /public folder 32 | 33 | # generate static for the mainnet release 34 | $ sh cli-dev.sh ci 35 | $ yarn ci:build:mainnet 36 | # afterward you'll have prepared distributive in /public folder 37 | 38 | ``` 39 | 40 | ## Dev toolset 41 | 42 | ### cli-dev.sh 43 | 44 | This helper-script is used to simplify regular tasks when developing or using the package: 45 | 46 | ```bash 47 | # removes all generated directories & run package installation with the yarn2.* based on stored yarn.lock with the modifier --check-cache 48 | $ sh cli-dev.sh ci 49 | 50 | # drops node_modules, .yarn/cache .yarn/build-state.yml .yarn/install-state.gz & trigger cache flushing (yarn cache clean --all) 51 | $ sh cli-dev.sh clean yarn 52 | 53 | # drops .nuxt and clear public directory 54 | $ sh cli-dev.sh clean nuxt 55 | ``` 56 | 57 | ### Linting & checking 58 | 59 | ```bash 60 | # Run stylelint with --fix modifier 61 | $ yarn run lint-style:fix 62 | 63 | # Run eslint with --fix modifier 64 | $ yarn run lint-ts:fix 65 | 66 | # Formats all of the code w/t stored style rules by running prettier 67 | $ yarn format:prettier 68 | 69 | # Check spelling in src files 70 | $ yarn spell-check 71 | ``` 72 | 73 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org). 74 | 75 | --- 76 | 77 | 78 | ## Release CI 79 | 80 | > This sharable configuration conforms to angular standard 81 | 82 | * Using [@semantic-release/commit-analyzer](https://github.com/semantic-release/commit-analyzer) ensures that commits are conformed to the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0-beta.4/) specification. 83 | * **PATCH** version created if any of **build**, **ci**, **chore**, **docs**, **refactor**, **style**, **test** commit types pushed to master 84 | * **MINOR** version created if fix commit type pushed MAJOR version created if feat commit type pushed 85 | * **MAJOR** version created if feat commit type pushed 86 | * By default, config used publishes the new version to NPM. But in zkSync's case release flow differs from the deployment flow. 87 | * Bumps a version in package.json. 88 | * Generates or updates a [changelog](CHANGELOG.md) file including all **PATCH** keywords (not included in default angular package). 89 | * Releases new release for the GitHub repo 90 | 91 | ## Solutions used 92 | 93 | * [Nuxt.js](https://nuxtjs.org) 94 | * [TS Lang](https://www.typescriptlang.org) 95 | * [Vue.js](https://vuejs.org) 96 | * [Vuex](https://vuex.vuejs.org) 97 | * [nuxt/Tailwind](https://tailwindcss.nuxtjs.org/) 98 | * [nuxt-build-optimisations](https://github.com/harlan-zw/nuxt-build-optimisations) 99 | * [Nuxt TypeScript](https://typescript.nuxtjs.org/) 100 | * inc. [@nuxt/typescript-build](https://typescript.nuxtjs.org/guide/setup) 101 | * inc. [@nuxt/typescript-runtime](https://typescript.nuxtjs.org/guide/runtime) 102 | * w/t built-in linting [typescript-runtime-linting](https://typescript.nuxtjs.org/guide/lint) 103 | * store is build on top of [Vanilla Vuex](https://typescript.nuxtjs.org/cookbook/store#vanilla) 104 | * [Axios Nuxtjs](https://axios.nuxtjs.org/) 105 | * [Nuxt social meta](https://github.com/AlekseyPleshkov/nuxt-social-meta) 106 | * [Sentry](https://sentry.nuxtjs.org/) 107 | * [Nuxt Webfontloader](https://github.com/Developmint/nuxt-webfontloader) 108 | 109 | ...and others. 110 | 111 | ### Libraries used 112 | 113 | * [zkSync Checkout lib](https://www.npmjs.com/package/zksync-checkout): our open sourced NPM-package 114 | * [zkSync.js lib](https://www.npmjs.com/package/zksync-checkout): our open sourced NPM-package 115 | * [zkCheckout link builder](https://checkout.zksync.io/link): UI to create unique permissionless payment link and get paid in tokens 116 | * [SDK description](https://zksync.io/api/sdk/checkout/): details on how to use zkCheckout SDK 117 | * [ethers.js lib](https://docs.ethers.io/v5/): a simple to use Web3 Provider Bridge as a single JavaScript file 118 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@matterlabs/zksync-dapp-checkout", 3 | "title": "zkSync Lite Checkout — simple permissionless DeFi payment gateway", 4 | "homepage": "https://checkout.zksync.io", 5 | "description": "zkSync Lite Checkout helps anyone permission-less adopt checkout backed by zkSync, receive payments automatically and benefit from all the advantage of zkSync Lite Rollup: speed of the transaction, times lower cost of a single transaction, simplicity of withdrawal fund to the onchain-wallet.", 6 | "version": "3.7.6", 7 | "keywords": [ 8 | "zksync", 9 | "ethereum", 10 | "web3", 11 | "payments", 12 | "gitcoin", 13 | "gitcoin grants", 14 | "checkout", 15 | "defi", 16 | "payment gateway", 17 | "cryptopayments", 18 | "gitcoin", 19 | "zksync dapps", 20 | "dapp", 21 | "ethers" 22 | ], 23 | "private": true, 24 | "license": "MIT", 25 | "licenses": [ 26 | { 27 | "type": "MIT", 28 | "url": "https://www.opensource.org/licenses/mit-license.php" 29 | }, 30 | { 31 | "type": "Apache-2.0", 32 | "url": "https://opensource.org/licenses/apache2.0.php" 33 | } 34 | ], 35 | "repository": { 36 | "type": "git", 37 | "url": "https://github.com/matter-labs/zksync-dapp-checkout.git" 38 | }, 39 | "engines": { 40 | "node": ">=14" 41 | }, 42 | "author": "Matter Labs", 43 | "browserslist": [ 44 | "defaults", 45 | "not < 1%", 46 | "IE 11" 47 | ], 48 | "bugs": { 49 | "url": "https://linear.app/matterlabs/project/zkcheckout-eae8f3c3e539/ZKF" 50 | }, 51 | "dependencies": { 52 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", 53 | "@babel/plugin-proposal-optional-chaining": "^7.21.0", 54 | "@nuxt/typescript-runtime": "^2.1.0", 55 | "core-js": "^3.15.1", 56 | "matter-zk-ui": "^1.0.9", 57 | "nuxt": "^2.15.7" 58 | }, 59 | "devDependencies": { 60 | "@babel/eslint-parser": "^7.14.7", 61 | "@commitlint/cli": "^16.0.1", 62 | "@commitlint/config-conventional": "^16.0.0", 63 | "@matterlabs/eslint-config-nuxt": "^2.0.0", 64 | "@matterlabs/prettier-config": "^1.0.2", 65 | "@matterlabs/zksync-nuxt-core": "^1.12.1", 66 | "@matterlabs/zksync-nuxt-ui": "^0.0.0", 67 | "@nuxt/types": "^2.15.7", 68 | "@nuxt/typescript-build": "^2.1.0", 69 | "@nuxtjs/dotenv": "^1.4.1", 70 | "@nuxtjs/google-gtag": "^1.0.4", 71 | "@nuxtjs/sentry": "^5.0.2", 72 | "@nuxtjs/style-resources": "^1.2.1", 73 | "@nuxtjs/stylelint-module": "^4.1.0", 74 | "@nuxtjs/tailwindcss": "^4.2.1", 75 | "@ramp-network/ramp-instant-sdk": "^4.0.4", 76 | "@semantic-release/changelog": "^6.0.1", 77 | "@semantic-release/git": "^10.0.1", 78 | "@types/fibers": "^3.1.1", 79 | "@typescript-eslint/eslint-plugin": "latest", 80 | "@typescript-eslint/parser": "^5.8.1", 81 | "cspell": "latest", 82 | "eslint": "8", 83 | "husky": "^7.0.4", 84 | "lint-staged": "^10.5.4", 85 | "moment": "^2.29.1", 86 | "nuxt-social-meta": "^0.0.5", 87 | "nuxt-webfontloader": "^1.1.0", 88 | "postcss": "latest", 89 | "prettier": "2.x", 90 | "sass": "^1.43.5", 91 | "sass-loader": "^10.2.0", 92 | "semantic-release": "^18.0.1", 93 | "stylelint": "^13.13.1", 94 | "stylelint-config-prettier": "^8.0.2", 95 | "stylelint-config-recommended-scss": "^4.3.0", 96 | "stylelint-config-standard": "^20.0.0", 97 | "stylelint-prettier": "^1.2.0", 98 | "stylelint-scss": "^3.18.0", 99 | "v-tooltip": "^2.1.3", 100 | "vue": "^2.6.12", 101 | "vue-custom-scrollbar": "^1.4.0", 102 | "vue-js-popover": "^1.2.1", 103 | "zksync": "^0.13.1", 104 | "zksync-checkout": "latest", 105 | "zksync-checkout-internal": "latest" 106 | }, 107 | "husky": { 108 | "hooks": { 109 | "commit-msg": "commitlint --format -E HUSKY_GIT_PARAMS", 110 | "pre-commit": "lint-staged", 111 | "post-merge": "yarn clean && yarn install", 112 | "post-checkout": "yarn clean && yarn install", 113 | "post-rebase": "yarn clean && yarn install", 114 | "pre-auto-gc": "yarn clean" 115 | } 116 | }, 117 | "lint-staged": { 118 | "*.{js,ts,vue}": "eslint --fix", 119 | "*.{css,scss,vue}": "stylelint --fix", 120 | "*.{md,vue}": "yarn lint:spell-check" 121 | }, 122 | "scripts": { 123 | "local": "yarn ci:env 'goerli' 'dev' 1 1", 124 | "dev": "yarn local && nuxt", 125 | "generate": "nuxt generate ", 126 | "start": "yarn local && nuxt start", 127 | "build": "yarn local && nuxt build", 128 | "ci:build:mainnet": "yarn ci:prepare:mainnet && yarn generate --fail-on-error", 129 | "ci:build:goerli": "yarn ci:prepare:goerli && yarn generate --fail-on-error", 130 | "ci:env": "sh cli-process-env.sh", 131 | "ci:prepare:goerli": "yarn ci:env 'goerli' 'prod' 1", 132 | "ci:prepare:mainnet": "yarn ci:env 'mainnet' 'prod' 1", 133 | "format:prettier": "prettier --config .prettierrc --ignore-path .prettierignore --write ./src/**/*.{ts,vue,js,scss,css}", 134 | "lint:spell-check": "cspell src/**/*.{ts,vue,js} README.md", 135 | "lint:script": "eslint --ext \".js,.vue,.ts\" --ignore-path .gitignore ./src/", 136 | "lint:style": "stylelint **/*.css **/*.scss **/*.vue", 137 | "lint:fix": "yarn lint:script --fix && yarn lint:style --fix", 138 | "lint": "yarn lint:script && yarn lint:style", 139 | "semantic-release": "semantic-release", 140 | "postinstall": "husky install" 141 | }, 142 | "resolutions": { 143 | "core-js": "^3.15.1", 144 | "eth-sig-utils": "^3.0.1" 145 | }, 146 | "prettier": "@matterlabs/prettier-config", 147 | "packageManager": "yarn@3.1.1" 148 | } 149 | -------------------------------------------------------------------------------- /src/assets/style/pages/_link.scss: -------------------------------------------------------------------------------- 1 | .linkPage { 2 | @apply w-full min-h-screen; 3 | 4 | height: max-content; 5 | display: grid; 6 | grid-template-columns: 90vw; 7 | grid-template-rows: max-content 1fr max-content; 8 | justify-content: center; 9 | 10 | .font-mono { 11 | font-family: $firaCode; 12 | letter-spacing: -0.1em !important; 13 | } 14 | 15 | .inputContainer.disabled .inputBody input { 16 | color: #696969; 17 | } 18 | 19 | .linkHeader { 20 | a { 21 | img.head-logo { 22 | @apply mr-2 relative top-0.5; 23 | 24 | height: 30px; 25 | } 26 | } 27 | 28 | .brandContainer { 29 | align-items: center; 30 | } 31 | 32 | .feature-list { 33 | max-width: 500px; 34 | } 35 | 36 | @media screen and (max-width: $mobile) { 37 | a { 38 | @apply items-center mt-1; 39 | 40 | .brandContainer { 41 | height: 40px; 42 | margin: 0 !important; 43 | padding: 0; 44 | 45 | h1 { 46 | line-height: 39px; 47 | margin-bottom: 0; 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | .lightLink { 55 | @media (max-width: $mobile) { 56 | &::after { 57 | visibility: visible !important; 58 | transform: scaleX(1) !important; 59 | } 60 | } 61 | } 62 | 63 | .headline { 64 | @apply text-center font-medium; 65 | 66 | font-weight: 400; 67 | font-size: 1.2rem; 68 | font-family: $firaCondensed; 69 | line-height: 125%; 70 | 71 | &.big { 72 | font-size: 1.4rem; 73 | } 74 | 75 | @media screen and (max-width: $mobile) { 76 | font-size: 0.8rem; 77 | &.big { 78 | font-size: 1rem; 79 | } 80 | } 81 | } 82 | 83 | .zk-container { 84 | position: relative; 85 | max-width: $maxWidthDefault; 86 | @media screen and (max-width: $mobile) { 87 | max-width: $maxWidthMobile; 88 | } 89 | } 90 | 91 | .linkBody { 92 | .paymentItem { 93 | position: relative; 94 | max-width: $maxWidthDefault; 95 | margin: 0 auto; 96 | 97 | .index, 98 | .delete { 99 | position: absolute; 100 | width: 36px; 101 | height: 36px; 102 | top: 50%; 103 | transform: translateY(-50%); 104 | text-align: center; 105 | line-height: 36px; 106 | } 107 | 108 | .index { 109 | left: -55px; 110 | will-change: opacity; 111 | } 112 | 113 | .delete { 114 | right: -50px; 115 | transition: $transition; 116 | transition-property: background-color, opacity; 117 | will-change: background-color, opacity; 118 | 119 | &:hover { 120 | background-color: $white3; 121 | } 122 | 123 | i { 124 | position: relative; 125 | top: 1px; 126 | } 127 | } 128 | 129 | .addressInput { 130 | input { 131 | font-weight: 400; 132 | } 133 | 134 | .errorTextContainer { 135 | height: 18px; 136 | } 137 | } 138 | 139 | .amountInputGroup { 140 | width: 100%; 141 | padding: 0; 142 | border: none; 143 | height: max-content; 144 | 145 | .penIcon, 146 | .rightSide { 147 | display: none; 148 | } 149 | 150 | .amInputContainer { 151 | max-width: 160px; 152 | 153 | input { 154 | left: 0; 155 | } 156 | } 157 | 158 | .leftSide { 159 | width: 100%; 160 | } 161 | } 162 | 163 | @media screen and (max-width: $mobile) { 164 | max-width: $maxWidthMobile; 165 | 166 | .label { 167 | padding-bottom: 5px; 168 | } 169 | .index, 170 | .delete { 171 | @apply text-sm; 172 | 173 | top: 0.5rem; 174 | right: 0; 175 | width: 1.25rem; 176 | height: 1.25rem; 177 | line-height: 1.25rem; 178 | } 179 | .delete { 180 | background-color: transparentize($color: $gray, $amount: 0.85); 181 | } 182 | } 183 | } 184 | 185 | @media screen and (max-width: $mobile) { 186 | margin-bottom: 12rem; 187 | } 188 | } 189 | 190 | .linkFooter { 191 | @media screen and (max-width: $mobile) { 192 | background-color: transparent !important; 193 | .poweredBy { 194 | @apply mt-5 pt-0; 195 | } 196 | 197 | .zkFooterContainer { 198 | flex-flow: row wrap; 199 | justify-content: space-evenly; 200 | align-items: center; 201 | background-color: $white; 202 | bottom: 0 !important; 203 | grid-gap: 0; 204 | padding: 0.75rem; 205 | 206 | .poweredBy { 207 | @apply p-0 m-0; 208 | 209 | margin-bottom: 1rem; 210 | } 211 | 212 | #built-by-matterlabs { 213 | display: none; 214 | } 215 | } 216 | } 217 | @media (min-width: $mobile) { 218 | //@apply container; 219 | 220 | width: 100%; 221 | margin: 0 auto; 222 | } 223 | } 224 | 225 | .successLinkContainer { 226 | display: grid; 227 | height: max-content; 228 | grid-template-columns: 1fr repeat(3, max-content); 229 | grid-template-rows: 100%; 230 | grid-gap: 0.5rem; 231 | 232 | input { 233 | text-overflow: ellipsis; 234 | } 235 | 236 | .vue-popover { 237 | position: fixed; 238 | color: $lightViolet; 239 | width: fit-content !important; 240 | padding: 5px 10px; 241 | } 242 | } 243 | 244 | .zkFooterContainer { 245 | position: relative; 246 | } 247 | 248 | @media (min-width: $mobile) { 249 | @apply pb-0; 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /src/plugins/successCheckmark.json: -------------------------------------------------------------------------------- 1 | {"v":"5.1.1","fr":30,"ip":0,"op":40,"w":200,"h":200,"nm":"BICOO - Check","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Check","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[-1.312,6,0],"ix":1},"s":{"a":0,"k":[200,200,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-15.75,8],[-8,16],[13.125,-4]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":25,"s":[0],"e":[100]},{"t":33}],"ix":1},"e":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":5,"ix":5},"lc":2,"lj":2,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":40,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Circ Stroke","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[98.045,98.045,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0","0p667_1_0p333_0"],"t":9,"s":[200,200,100],"e":[160,160,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0","0p667_1_0p333_0"],"t":17,"s":[160,160,100],"e":[240,240,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0","0p667_1_0p333_0"],"t":22,"s":[240,240,100],"e":[200,200,100]},{"t":26}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[60,60],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[0],"e":[100]},{"t":9}],"ix":1},"e":{"a":0,"k":0,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.152941176471,0.682352941176,0.376470588235,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":3,"ix":5},"lc":2,"lj":2,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0.978,0.978],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":40,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Circ Verde","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":14,"s":[0],"e":[98]},{"t":25}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0","0p667_1_0p333_0"],"t":14,"s":[0,0,100],"e":[200,200,100]},{"t":25}],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[64,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.152941176471,0.682352941176,0.376470588235,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":40,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Ola 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":14,"s":[0],"e":[90]},{"t":27}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0","0p667_1_0p333_0"],"t":14,"s":[0,0,100],"e":[250,250,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"n":["0p833_1_0p167_0","0p833_1_0p167_0","0p833_1_0p167_0"],"t":27,"s":[250,250,100],"e":[230,230,100]},{"t":34}],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[64,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":40,"op":40,"st":0,"bm":0,"hidden":0},{"ddd":0,"ind":5,"ty":4,"nm":"Ola 2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":14,"s":[0],"e":[60]},{"t":29}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0","0p667_1_0p333_0"],"t":14,"s":[0,0,100],"e":[280,280,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"n":["0p833_1_0p167_0","0p833_1_0p167_0","0p833_1_0p167_0"],"t":29,"s":[280,280,100],"e":[260,260,100]},{"t":36}],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[64,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":40,"op":40,"st":0,"bm":0,"hidden":0},{"ddd":0,"ind":6,"ty":4,"nm":"Ola 3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":14,"s":[0],"e":[30]},{"t":31}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[100,100,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0","0p667_1_0p333_0"],"t":14,"s":[0,0,100],"e":[310,310,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"n":["0p833_1_0p167_0","0p833_1_0p167_0","0p833_1_0p167_0"],"t":31,"s":[310,310,100],"e":[290,290,100]},{"t":38}],"ix":6}},"ao":0,"shapes":[{"d":1,"ty":"el","s":{"a":0,"k":[64,64],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":40,"op":40,"st":0,"bm":0,"hidden":0}],"markers":[]} 2 | -------------------------------------------------------------------------------- /src/components/TokenDropdown.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 206 | -------------------------------------------------------------------------------- /src/store/checkout.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable require-await */ 2 | import { ActionTree, GetterTree, MutationTree } from "vuex"; 3 | import { ZkSyncTransaction } from "zksync-checkout/build/types"; 4 | import { closestPackableTransactionAmount, closestPackableTransactionFee, RestProvider } from "zksync"; 5 | import { TokenSymbol, Address } from "zksync/build/types"; 6 | import { BigNumber } from "ethers"; 7 | import { ZkFee } from "@matterlabs/zksync-nuxt-core/types"; 8 | import { RootState } from "~/store"; 9 | import { TransactionData, TransactionFee, TotalByToken } from "@/types/index"; 10 | 11 | export const state = () => ({ 12 | linkCheckout: false, 13 | isError: false, 14 | noDataError: undefined, 15 | transactions: [] as Array, 16 | fromAddress: "" as Address, 17 | feeToken: undefined as TokenSymbol | undefined, 18 | allowedRampZkTokens: ["ETH", "DAI", "USDT", "USDC"] as TokenSymbol[], 19 | confirmationsAmount: undefined, 20 | domains: new Map(), 21 | }); 22 | 23 | export type CheckoutModuleState = ReturnType; 24 | 25 | export const getters: GetterTree = { 26 | getTransactionData(state, _, __): TransactionData { 27 | return { 28 | transactions: state.transactions, 29 | fromAddress: state.fromAddress, 30 | feeToken: state.feeToken!, 31 | domains: state.domains, 32 | }; 33 | }, 34 | getTransactionBatchFee(_, __, ___, rootGetters): false | TransactionFee { 35 | // eslint-disable-next-line no-unused-expressions 36 | rootGetters["zk-transaction/feeLoading"]; 37 | if (rootGetters["zk-transaction/fee"]) { 38 | const minFee = BigNumber.from(rootGetters["zk-transaction/fee"]); 39 | return { 40 | key: "txBatchFee", 41 | amount: closestPackableTransactionFee(minFee.add(minFee.div("100").mul("5").toString()).toString()), 42 | realAmount: minFee, 43 | token: rootGetters["zk-transaction/feeSymbol"], 44 | }; 45 | } 46 | return false; 47 | }, 48 | getAccountUnlockFee(_, __, ___, rootGetters): false | BigNumber { 49 | // eslint-disable-next-line no-unused-expressions 50 | rootGetters["zk-transaction/activationFeeLoading"]; 51 | return rootGetters["zk-transaction/accountActivationFee"]; 52 | }, 53 | usedTokens(state, _, __, rootGetters): TokenSymbol[] { 54 | const tokens: Set = new Set(); 55 | tokens.add(rootGetters["zk-transaction/feeSymbol"]); 56 | for (const item of state.transactions) { 57 | tokens.add(item.token); 58 | } 59 | return Array.from(tokens); 60 | }, 61 | getTotalByToken(state, _, __, rootGetters): TotalByToken { 62 | const allFees = rootGetters["zk-transaction/fees"].map((e: ZkFee) => ({ 63 | ...e, 64 | token: rootGetters["zk-transaction/feeSymbol"], 65 | })); 66 | const totalByToken = new Map(); 67 | const addToTotalByToken = (amount: BigNumber, token: TokenSymbol) => { 68 | if (totalByToken.has(token)) { 69 | totalByToken.set(token, totalByToken.get(token).add(amount)); 70 | } else { 71 | totalByToken.set(token, amount); 72 | } 73 | }; 74 | for (const item of state.transactions) { 75 | addToTotalByToken(BigNumber.from(item.amount), item.token); 76 | } 77 | for (const item of allFees) { 78 | addToTotalByToken(item.amount, item.token); 79 | } 80 | return Object.fromEntries(totalByToken); 81 | }, 82 | getErrorState(state: CheckoutModuleState): boolean { 83 | return state.isError; 84 | }, 85 | getErrorData(state: CheckoutModuleState): unknown | undefined { 86 | return state.noDataError; 87 | }, 88 | getAllowedRampZkTokens(state: CheckoutModuleState): TokenSymbol[] { 89 | return state.allowedRampZkTokens; 90 | }, 91 | getConfirmationsAmount(state: CheckoutModuleState): number | undefined { 92 | return state.confirmationsAmount; 93 | }, 94 | isLinkCheckout(state: CheckoutModuleState): boolean { 95 | return state.linkCheckout; 96 | }, 97 | }; 98 | 99 | export const mutations: MutationTree = { 100 | setLinkCheckoutState(state, status: boolean) { 101 | state.linkCheckout = status; 102 | }, 103 | setFeeToken(state, feeToken: TokenSymbol) { 104 | state.feeToken = feeToken; 105 | }, 106 | setTransactionData(state, { transactions, fromAddress, domains }: TransactionData) { 107 | state.transactions = transactions.map((tx) => { 108 | return { 109 | ...tx, 110 | amount: closestPackableTransactionAmount(tx.amount).toString(), 111 | }; 112 | }); 113 | state.fromAddress = fromAddress; 114 | state.domains = domains; 115 | }, 116 | setError(state: CheckoutModuleState, errorData) { 117 | state.isError = !!errorData; 118 | state.noDataError = errorData; 119 | }, 120 | setAllowedRampZkTokens(state: CheckoutModuleState, tokens: TokenSymbol[]) { 121 | state.allowedRampZkTokens = tokens; 122 | }, 123 | setConfirmationsAmount(state: CheckoutModuleState, confirmationsAmount: number) { 124 | state.confirmationsAmount = confirmationsAmount; 125 | }, 126 | }; 127 | 128 | export const actions: ActionTree = { 129 | async setTransactionData({ commit, dispatch, rootGetters }, data: TransactionData) { 130 | commit("setTransactionData", data); 131 | commit( 132 | "zk-transaction/setTransferBatch", 133 | [ 134 | ...data.transactions.map((e) => ({ address: e.to, token: e.token })), 135 | { 136 | address: rootGetters["zk-account/address"] ? rootGetters["zk-account/address"] : data.transactions[0].to, 137 | token: data.feeToken, 138 | }, 139 | ], 140 | { root: true } 141 | ); 142 | commit("setFeeToken", data.feeToken); 143 | dispatch("zk-transaction/setType", "TransferBatch", { root: true }); 144 | dispatch("zk-transaction/setSymbol", data.feeToken, { root: true }); 145 | }, 146 | async requestInitialData({ getters, dispatch }) { 147 | const usedTokens = getters.usedTokens; 148 | const allowanceArr = []; 149 | for (const symbol of usedTokens) { 150 | allowanceArr.push(dispatch("zk-balances/requestAllowance", { force: true, symbol }, { root: true })); 151 | } 152 | await Promise.all([ 153 | dispatch("zk-transaction/requestAllFees", true, { root: true }), 154 | dispatch("requestUsedTokensPrice"), 155 | dispatch("requestConfirmationsAmount"), 156 | dispatch("requestUsedTokensEthereumBalance", true), 157 | ...allowanceArr, 158 | ]); 159 | }, 160 | async requestUsedTokensPrice({ getters, dispatch }): Promise { 161 | const usedTokens = getters.usedTokens; 162 | for (const symbol of usedTokens) { 163 | dispatch("zk-tokens/getTokenPrice", symbol, { root: true }); 164 | } 165 | }, 166 | async requestConfirmationsAmount({ getters, commit, dispatch }): Promise { 167 | if (getters.confirmationsAmount) { 168 | return; 169 | } 170 | const syncProvider: RestProvider = await dispatch("zk-provider/requestProvider", null, { root: true }); 171 | const confirmationsAmount = await syncProvider.getConfirmationsForEthOpAmount(); 172 | commit("setConfirmationsAmount", confirmationsAmount); 173 | }, 174 | async requestUsedTokensEthereumBalance({ getters, dispatch }, force = false): Promise { 175 | const usedTokens = getters.usedTokens; 176 | const balancesPromises = []; 177 | for (const symbol of usedTokens) { 178 | balancesPromises.push(dispatch("zk-balances/requestEthereumBalance", { symbol, force }, { root: true })); 179 | } 180 | await Promise.all(balancesPromises); 181 | }, 182 | async setFeeToken({ commit, dispatch }, feeToken: TokenSymbol): Promise { 183 | commit("setFeeToken", feeToken); 184 | dispatch("zk-tokens/getTokenPrice", feeToken, { root: true }); 185 | dispatch("zk-transaction/setSymbol", feeToken, { root: true }); 186 | }, 187 | }; 188 | -------------------------------------------------------------------------------- /src/static/ilol48zhg7zypovk.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | zkSync Checkout test launcher 7 | 8 | 9 | 10 | 11 | 88 | 89 | 90 |
91 |

Checkout Payment Gateway

92 |

Initiate and launch the checkout for testing purposes

93 |
96 | 97 |
98 |
99 | 276 | 277 | -------------------------------------------------------------------------------- /src/blocks/infoBlock.vue: -------------------------------------------------------------------------------- 1 | 148 | 149 | 259 | -------------------------------------------------------------------------------- /src/pages/link/index.vue: -------------------------------------------------------------------------------- 1 | 166 | 167 | 277 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | import { ModuleOptions } from "@matterlabs/zksync-nuxt-core/types"; 2 | import { NuxtOptionsEnv } from "@nuxt/types/config/env"; 3 | import Sass from "sass"; 4 | 5 | import { NuxtConfig } from "@nuxt/types"; 6 | // noinspection ES6PreferShortImport 7 | import { 8 | CURRENT_APP_NAME, 9 | ETHER_NETWORK_CAPITALIZED, 10 | ETHER_PRODUCTION, 11 | isDebugEnabled, 12 | isProduction, 13 | nuxtBuildConfig, 14 | } from "./src/plugins/build"; 15 | 16 | const zkTailwindDefault = require("matter-zk-ui/tailwind.config.js"); 17 | 18 | const srcDir = "./src/"; 19 | 20 | const env = process.env.APP_ENV ?? "dev"; 21 | const pageTitle = `${CURRENT_APP_NAME}`.toString() ?? "zkSync Lite Checkout"; 22 | const pageImg = "/social.png"; 23 | 24 | const pageTitleTemplate = ETHER_PRODUCTION ? CURRENT_APP_NAME : `${ETHER_NETWORK_CAPITALIZED}`; 25 | 26 | const pageDescription: string = process.env.SITE_DESCRIPTION ?? ""; 27 | const pageKeywords = process.env.SITE_KEYWORDS ?? ""; 28 | 29 | const config: NuxtConfig = { 30 | components: ["@/components/", { path: "@/blocks/", prefix: "block" }], 31 | telemetry: false, 32 | 33 | // Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode 34 | ssr: false, 35 | 36 | // Target: https://go.nuxtjs.dev/config-target 37 | target: "static", 38 | 39 | srcDir: `${srcDir}`, 40 | vue: { 41 | config: { 42 | productionTip: isProduction, 43 | devtools: !isProduction, 44 | }, 45 | }, 46 | env: { 47 | ...process.env, 48 | }, 49 | 50 | /** 51 | * Global page headers: https://go.nuxtjs.dev/config-head 52 | */ 53 | head: { 54 | title: pageTitle as string | undefined, 55 | titleTemplate: `%s | ${pageTitleTemplate}`, 56 | htmlAttrs: { 57 | lang: "en", 58 | }, 59 | meta: [ 60 | { 61 | property: "cache-control", 62 | httpEquiv: "cache-control", 63 | content: "no-cache , no-store, must-revalidate", 64 | }, 65 | { 66 | httpEquiv: "pragma", 67 | content: "no-cache", 68 | property: "pragma", 69 | }, 70 | { 71 | httpEquiv: "cache-control", 72 | property: "cache-control", 73 | content: "no-cache , no-store, must-revalidate", 74 | }, 75 | { 76 | httpEquiv: "expires", 77 | content: "0", 78 | property: "expires", 79 | }, 80 | { 81 | hid: "keywords", 82 | name: "keywords", 83 | content: pageKeywords, 84 | }, 85 | { 86 | hid: "description", 87 | name: "description", 88 | content: pageDescription, 89 | }, 90 | { 91 | hid: "author", 92 | name: "author", 93 | content: "https://matter-labs.io", 94 | }, 95 | { 96 | hid: "twitter:title", 97 | name: "twitter:title", 98 | content: pageTitle, 99 | }, 100 | { 101 | hid: "twitter:description", 102 | name: "twitter:description", 103 | content: pageDescription, 104 | }, 105 | { 106 | hid: "twitter:image", 107 | name: "twitter:image", 108 | content: pageImg, 109 | }, 110 | { 111 | hid: "twitter:image:alt", 112 | name: "twitter:image:alt", 113 | content: pageTitle, 114 | }, 115 | { 116 | hid: "twitter:site", 117 | name: "twitter:site", 118 | content: "@zksync", 119 | }, 120 | { 121 | hid: "twitter:creator", 122 | name: "twitter:creator", 123 | content: "@the_matter_labs", 124 | }, 125 | { 126 | hid: "og:title", 127 | property: "og:title", 128 | content: pageTitle, 129 | }, 130 | { 131 | hid: "og:description", 132 | property: "og:description", 133 | content: pageDescription, 134 | }, 135 | { 136 | hid: "og:image", 137 | property: "og:image", 138 | content: pageImg, 139 | }, 140 | { 141 | hid: "og:image:secure_url", 142 | property: "og:image:secure_url", 143 | content: pageImg, 144 | }, 145 | { 146 | hid: "og:image:alt", 147 | property: "og:image:alt", 148 | content: pageTitle, 149 | }, 150 | 151 | { charset: "utf-8" }, 152 | { name: "viewport", content: "width=device-width, initial-scale=1" }, 153 | { 154 | hid: "msapplication-TileImage", 155 | name: "msapplication-TileImage", 156 | content: "/favicon-dark.png", 157 | }, 158 | { hid: "theme-color", name: "theme-color", content: "#4e529a" }, 159 | { 160 | hid: "msapplication-TileColor", 161 | property: "msapplication-TileColor", 162 | content: "#4e529a", 163 | }, 164 | ], 165 | link: [{ rel: "icon", type: "image/x-icon", href: "/favicon-dark.png" }], 166 | }, 167 | 168 | /* 169 | ** Customize the progress-bar color 170 | */ 171 | loading: { 172 | color: "#8c8dfc", 173 | continuous: true, 174 | }, 175 | 176 | /** 177 | * Global CSS: https://go.nuxtjs.dev/config-css 178 | */ 179 | css: ["@/assets/style/main.scss"], 180 | /* 181 | ** Plugins to load before mounting the App 182 | */ 183 | plugins: ["@/plugins/main", "@/plugins/setCheckoutData", "@/plugins/restoreSession"], 184 | 185 | router: { 186 | middleware: ["wallet"], 187 | }, 188 | 189 | /* 190 | ** Nuxt.js dev-modules 191 | */ 192 | buildModules: [ 193 | "@nuxt/typescript-build", 194 | // Doc: https://github.com/nuxt-community/stylelint-module 195 | "@nuxtjs/stylelint-module", 196 | // Doc: https://github.com/nuxt-community/style-resources-module/ 197 | "@nuxtjs/tailwindcss", 198 | "@nuxtjs/style-resources", 199 | ["@nuxtjs/dotenv", { path: __dirname }], 200 | "matter-zk-ui", 201 | [ 202 | "@matterlabs/zksync-nuxt-core", 203 | { 204 | network: process.env.ZK_NETWORK, 205 | apiKeys: { 206 | FORTMATIC_KEY: process.env.APP_FORTMATIC, 207 | PORTIS_KEY: process.env.APP_PORTIS, 208 | INFURA_KEY: process.env.APP_INFURA_API_KEY, 209 | }, 210 | onboardConfig: { 211 | APP_NAME: pageTitle, 212 | APP_ID: process.env.APP_ONBOARDING_APP_ID, 213 | }, 214 | restoreNetwork: false, 215 | logoutRedirect: "/connect", 216 | screeningApiUrl: process.env.SCREENING_API_URL, 217 | }, 218 | ], 219 | ], 220 | 221 | /** 222 | * Modules: https://go.nuxtjs.dev/config-modules 223 | */ 224 | modules: [ 225 | "nuxt-webfontloader", 226 | [ 227 | "nuxt-social-meta", 228 | { 229 | url: "https://checkout.zksync.io", 230 | title: pageTitle, 231 | site_name: pageTitle, 232 | description: pageDescription, 233 | img: "social.png", 234 | img_size: { width: "2560", height: "1280" }, 235 | locale: "en_US", 236 | twitter: "@zksync", 237 | twitter_card: "https://checkout.zksync.io/social.png", 238 | themeColor: "#4e529a", 239 | }, 240 | ], 241 | "@nuxtjs/google-gtag", 242 | "@nuxtjs/sentry", 243 | ], 244 | i18n: { 245 | vueI18n: { 246 | fallbackLocale: "en", 247 | messages: { 248 | en: require(`./${srcDir}/locales/en/translations.json`), 249 | }, 250 | }, 251 | }, 252 | styleResources: { 253 | scss: ["@/assets/style/_variables.scss"], 254 | }, 255 | sentry: { 256 | dsn: process.env.SENTRY_DSN, 257 | disableServerSide: true, 258 | disabled: !isProduction, 259 | config: { 260 | debug: isDebugEnabled, 261 | tracesSampleRate: 1.0, 262 | environment: isProduction ? "production" : env === "dev" ? "development" : env, 263 | }, 264 | }, 265 | "google-gtag": { 266 | id: process.env.GTAG_ID, // required 267 | config: { 268 | allow_google_signals: false, 269 | allow_ad_personalization_signals: false, 270 | // this is the config options for `gtag 271 | // check out official docs: https://developers.google.com/analytics/devguides/collection/gtagjs/ 272 | anonymize_ip: true, // anonymize IP 273 | send_page_view: isProduction, // might be necessary to avoid duplicated page track on page reload 274 | linker: { 275 | domains: ["checkout.zksync.io", "checkout-goerli.zksync.io", "web.app"], 276 | }, 277 | }, 278 | debug: isDebugEnabled, // enable to track in dev mode 279 | disableAutoPageTrack: !isProduction, // disable if you don't want to track each page route with router.afterEach(...) 280 | // optional you can add more configuration like [AdWords](https://developers.google.com/adwords-remarketing-tag/#configuring_the_global_site_tag_for_multiple_accounts) 281 | }, 282 | // Fonts loader https://www.npmjs.com/package/nuxt-webfontloader 283 | webfontloader: { 284 | google: { 285 | families: ["Fira+Sans:300,400,500,600", "Fira+Sans+Condensed:200,400,500,600", "Fira+Code:300"], 286 | }, 287 | }, 288 | tailwindcss: { 289 | mode: "jit", 290 | config: { 291 | ...zkTailwindDefault, 292 | purge: { 293 | enabled: !isProduction, 294 | content: [ 295 | `${srcDir}/components/**/*.vue`, 296 | `${srcDir}/blocks/**/*.vue`, 297 | `${srcDir}/blocks/**/*.vue`, 298 | `${srcDir}/layouts/**/*.vue`, 299 | `${srcDir}/pages/**/*.vue`, 300 | `${srcDir}/plugins/**/*.{js,ts}`, 301 | "./node_modules/matter-zk-ui/components/**/*.vue", 302 | "./node_modules/matter-zk-ui/blocks/**/*.vue", 303 | "./node_modules/matter-zk-ui/blocks/**/*.vue", 304 | "./node_modules/matter-zk-ui/layouts/**/*.vue", 305 | "./node_modules/matter-zk-ui/pages/**/*.vue", 306 | "./node_modules/matter-zk-ui/plugins/**/*.{js,ts}", 307 | "./node_modules/matter-zk-ui/nuxt.config.{js,ts}", 308 | ], 309 | }, 310 | }, 311 | }, 312 | 313 | /* 314 | ** Build configuration 315 | */ 316 | build: { 317 | ...nuxtBuildConfig, 318 | loaders: { 319 | scss: { 320 | implementation: Sass, 321 | }, 322 | }, 323 | /* 324 | ** You can extend webpack config here 325 | */ 326 | extend(config) { 327 | config.module!.rules.push({ 328 | test: /\.m?js$/, 329 | include: [ 330 | /node_modules[\\/]superstruct/, 331 | /node_modules[\\/]@walletconnect/, 332 | /node_modules[\\/]@web3modal[\\/]core/, 333 | /node_modules[\\/]@web3modal[\\/]ui/, 334 | ], 335 | use: { 336 | loader: "babel-loader", 337 | options: { 338 | presets: ["@babel/preset-env"], 339 | plugins: ["@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-nullish-coalescing-operator"], 340 | }, 341 | }, 342 | }); 343 | 344 | config.node = { 345 | fs: "empty", 346 | }; 347 | // Run ESLint on save 348 | // if (ctx.isDev && ctx.isClient) { 349 | // config.module.rules.push({ 350 | // enforce: "pre", 351 | // test: /\.(js|ts|vue)$/, 352 | // loader: "eslint-loader", 353 | // exclude: /(node_modules)/ 354 | // }); 355 | // } 356 | }, 357 | postcss: { 358 | preset: { 359 | autoprefixer: { grid: "autoplace" }, 360 | }, 361 | }, 362 | }, 363 | generate: { 364 | dir: "public", 365 | fallback: "404.html", 366 | devtools: !isProduction, 367 | }, 368 | }; 369 | export default config; 370 | --------------------------------------------------------------------------------