Compare commits
266 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fe2af4574 | ||
|
|
38af86e1bb | ||
|
|
11a8df2a3e | ||
|
|
3726b6bb47 | ||
|
|
bfde34c774 | ||
|
|
bd8113d018 | ||
|
|
14e3ef044e | ||
|
|
4fc4bdcd55 | ||
|
|
3733570a89 | ||
|
|
7a042a5199 | ||
|
|
6d5e17a6e7 | ||
|
|
8301c5892c | ||
|
|
59b757dda0 | ||
|
|
92a6ec67b3 | ||
|
|
1d6a1e90d7 | ||
|
|
01aa3291b3 | ||
|
|
5539ebedf7 | ||
|
|
e6adddbf55 | ||
|
|
0050b1e165 | ||
|
|
5bf33ab004 | ||
|
|
a4cfeecd8c | ||
|
|
76cbfdd0b9 | ||
|
|
0db9e51e41 | ||
|
|
82e7925a17 | ||
|
|
2150347ba2 | ||
|
|
2f80646ddd | ||
|
|
55eea6a724 | ||
|
|
709a70652f | ||
|
|
5a7a041f12 | ||
|
|
b60d98fc17 | ||
|
|
38d9ab67eb | ||
|
|
5e6ef1575b | ||
|
|
4a015e9d0d | ||
|
|
c383a0a0a2 | ||
|
|
d6e92804ad | ||
|
|
f0d8f8b23b | ||
|
|
ff080aa957 | ||
|
|
04d9ff7d71 | ||
|
|
406893d99a | ||
|
|
4630720956 | ||
|
|
e73e1540ad | ||
|
|
3a82642fe6 | ||
|
|
2a50d6a17e | ||
|
|
c8a8149127 | ||
|
|
38cde648cf | ||
|
|
33c099119d | ||
|
|
40247ff7e0 | ||
|
|
30d1de8e84 | ||
|
|
0e5328bee9 | ||
|
|
d1995bc5a6 | ||
|
|
4959836c2a | ||
|
|
ef4a80852d | ||
|
|
c2a972eb75 | ||
|
|
47a2768d89 | ||
|
|
ca60caf6b0 | ||
|
|
252acef199 | ||
|
|
00ecb933ac | ||
|
|
607d0d443e | ||
|
|
ff0209a78f | ||
|
|
924e83139b | ||
|
|
4d5cc8267e | ||
|
|
6bc7cfc996 | ||
|
|
97312bb174 | ||
|
|
d0a10fcf8d | ||
|
|
7a1a476e45 | ||
|
|
b3bfc1003a | ||
|
|
3b1ef8033b | ||
|
|
803485b96a | ||
|
|
6df5d3a701 | ||
|
|
2d4eafc6b3 | ||
|
|
9b52fea58a | ||
|
|
b92b286626 | ||
|
|
a8268728d3 | ||
|
|
ab6debbf46 | ||
|
|
4c664645c6 | ||
|
|
4416a84fd7 | ||
|
|
eaaff81a67 | ||
|
|
f7b8f945f2 | ||
|
|
115c65500b | ||
|
|
3d8d29fd18 | ||
|
|
c178da626f | ||
|
|
e3c589ae41 | ||
|
|
6d60aca437 | ||
|
|
c77885c4c0 | ||
|
|
01684977b3 | ||
|
|
025a84de93 | ||
|
|
0025997175 | ||
|
|
acf97e042c | ||
|
|
771618b1c9 | ||
|
|
87144de9c8 | ||
|
|
63d0290a50 | ||
|
|
963121f19b | ||
|
|
58417412f1 | ||
|
|
cd3cff0202 | ||
|
|
90546fd803 | ||
|
|
bee558fa1a | ||
|
|
87514742f1 | ||
|
|
75b0320bbe | ||
|
|
4966ee56dd | ||
|
|
545fdd2cbe | ||
|
|
201dc2ce85 | ||
|
|
c36d9c3d3f | ||
|
|
bc48b4fb0e | ||
|
|
ffdd311be9 | ||
|
|
3e5c0bddd7 | ||
|
|
b161022fb1 | ||
|
|
a527f00905 | ||
|
|
a05dedd8be | ||
|
|
916ebef480 | ||
|
|
aa77a7a23a | ||
|
|
67b5896886 | ||
|
|
9e31756cc6 | ||
|
|
3900cdd0dc | ||
|
|
fcc6a2d560 | ||
|
|
98841c783d | ||
|
|
820e179560 | ||
|
|
2268860615 | ||
|
|
9c41955e52 | ||
|
|
b6874b8094 | ||
|
|
5e090db8b3 | ||
|
|
cf95949406 | ||
|
|
2b60961812 | ||
|
|
aed969f38d | ||
|
|
4e6121d4ab | ||
|
|
d63bcca539 | ||
|
|
671041360d | ||
|
|
36bdf5dd3e | ||
|
|
8b1a0e99ee | ||
|
|
a68a7e1bb4 | ||
|
|
95b9624bca | ||
|
|
04a0479236 | ||
|
|
55f1e35ffc | ||
|
|
01a3aa1c92 | ||
|
|
7121b4aa1c | ||
|
|
a538bf0b69 | ||
|
|
135cb8fb34 | ||
|
|
bf50582d38 | ||
|
|
110e23d6eb | ||
|
|
7ab6a17b42 | ||
|
|
7ad13c96a8 | ||
|
|
4e99cc4d93 | ||
|
|
6d29815f59 | ||
|
|
4888fe23df | ||
|
|
ef9ecd9ce2 | ||
|
|
f5d0804c46 | ||
|
|
0bac257254 | ||
|
|
a77752ab83 | ||
|
|
bf31ca4f06 | ||
|
|
ed8afbd851 | ||
|
|
47b6a7c4d5 | ||
|
|
086fc65457 | ||
|
|
7df53f30a0 | ||
|
|
66497a0108 | ||
|
|
e0eb701bc0 | ||
|
|
36cb0668a3 | ||
|
|
810f42136e | ||
|
|
07b7d7f268 | ||
|
|
39b5bb37cd | ||
|
|
feed63b1b3 | ||
|
|
ee56382956 | ||
|
|
64e396d9e0 | ||
|
|
2ffc8a0bdf | ||
|
|
5ec9cdc5c4 | ||
|
|
4d85775d90 | ||
|
|
c1c59ca692 | ||
|
|
f29d97413e | ||
|
|
a078d94a38 | ||
|
|
c9c3329bc3 | ||
|
|
13d0b70fa8 | ||
|
|
b852e4e64a | ||
|
|
55bd3555be | ||
|
|
972a65066c | ||
|
|
39a212f762 | ||
|
|
c362f4fe39 | ||
|
|
271ef580e1 | ||
|
|
81ced4cb8b | ||
|
|
ab214a8133 | ||
|
|
1b2d86ae3a | ||
|
|
40cac44e07 | ||
|
|
4e6d28cff4 | ||
|
|
709fad0804 | ||
|
|
573f4c873a | ||
|
|
d300db669f | ||
|
|
fb8217ddea | ||
|
|
7b9a23d920 | ||
|
|
120ad935fa | ||
|
|
4eaf16b624 | ||
|
|
857e2915ab | ||
|
|
7410c81b42 | ||
|
|
fb05439d32 | ||
|
|
fb7eade70b | ||
|
|
bd2b2c487a | ||
|
|
2f004ed1d9 | ||
|
|
db257c73f2 | ||
|
|
7c37b9d00e | ||
|
|
7688c527f0 | ||
|
|
06dd41a9cd | ||
|
|
850fec40a9 | ||
|
|
3c7eabc3d8 | ||
|
|
048607080c | ||
|
|
a0f20c54d8 | ||
|
|
da79abbc0d | ||
|
|
5ac08e1142 | ||
|
|
d330eea375 | ||
|
|
35dace7bfe | ||
|
|
8ce8e17f62 | ||
|
|
eb105b6ec7 | ||
|
|
267e7de2b6 | ||
|
|
ab9f2af054 | ||
|
|
67b405dd42 | ||
|
|
281dbf4305 | ||
|
|
369f2d7dfa | ||
|
|
803c749b13 | ||
|
|
8818dadf24 | ||
|
|
d179fc6b84 | ||
|
|
18ec675c52 | ||
|
|
1a79bac893 | ||
|
|
bbf49b0477 | ||
|
|
7ee41abd99 | ||
|
|
581c0982db | ||
|
|
478a05a34f | ||
|
|
5d6664019e | ||
|
|
0cc187a7cb | ||
|
|
f3e09a90e2 | ||
|
|
17db102484 | ||
|
|
e8ca84ee07 | ||
|
|
dae7314aa9 | ||
|
|
32e6237624 | ||
|
|
cee19aebe7 | ||
|
|
b2fbba13a2 | ||
|
|
a15a6108c1 | ||
|
|
46724cd8f6 | ||
|
|
09e6d38f25 | ||
|
|
236a4dc145 | ||
|
|
991d07ef39 | ||
|
|
7f5707e551 | ||
|
|
4050b1b241 | ||
|
|
a19aa303d9 | ||
|
|
c7f67437fd | ||
|
|
b5192a2b7d | ||
|
|
cd92e4cf6f | ||
|
|
089fba5dee | ||
|
|
503a33314f | ||
|
|
6fee37c4b8 | ||
|
|
a2812fcf79 | ||
|
|
c42aeae96a | ||
|
|
80ab000d34 | ||
|
|
81206f1eef | ||
|
|
568b05fda1 | ||
|
|
3d0ca21036 | ||
|
|
8b743615d1 | ||
|
|
9e4fdabc34 | ||
|
|
decb922d4b | ||
|
|
ac50555647 | ||
|
|
f48356d0fb | ||
|
|
a362f8797a | ||
|
|
783f42abcc | ||
|
|
0923cf4ac9 | ||
|
|
801958d0ae | ||
|
|
8392c29a1e | ||
|
|
8d36edf2b7 | ||
|
|
0f8d3fa506 | ||
|
|
5f64149f39 | ||
|
|
7115729e3e | ||
|
|
6618135e7d | ||
|
|
d3085c1f3c |
12
.env
@@ -1,13 +1,13 @@
|
||||
# These API keys are intentionally public. Please do not report them - thank you for your concern.
|
||||
ESLINT_NO_DEV_ERRORS=true
|
||||
REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
|
||||
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
|
||||
REACT_APP_AWS_API_REGION="us-east-2"
|
||||
REACT_APP_AWS_API_ENDPOINT="https://beta.api.uniswap.org/v1/graphql"
|
||||
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"
|
||||
REACT_APP_SENTRY_DSN="https://a3c62e400b8748b5a8d007150e2f38b7@o1037921.ingest.sentry.io/4504255148851200"
|
||||
REACT_APP_SENTRY_ENABLED=false
|
||||
ESLINT_NO_DEV_ERRORS=true
|
||||
REACT_APP_BNB_RPC_URL="https://rough-sleek-hill.bsc.quiknode.pro/413cc98cbc776cda8fdf1d0f47003583ff73d9bf"
|
||||
REACT_APP_INFURA_KEY="4bf032f2d38a4ed6bb975b80d6340847"
|
||||
REACT_APP_MOONPAY_API="https://api.moonpay.com"
|
||||
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkStaging?platform=web"
|
||||
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkV2?platform=web&env=staging"
|
||||
REACT_APP_MOONPAY_PUBLISHABLE_KEY="pk_test_DycfESRid31UaSxhI5yWKe1r5E5kKSz"
|
||||
REACT_APP_SENTRY_DSN="https://a3c62e400b8748b5a8d007150e2f38b7@o1037921.ingest.sentry.io/4504255148851200"
|
||||
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
|
||||
REACT_APP_TEMP_API_URL="https://temp.api.uniswap.org/v1"
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# These API keys are intentionally public. Please do not report them - thank you for your concern.
|
||||
REACT_APP_AMPLITUDE_PROXY_URL="https://api.uniswap.org/v1/amplitude-proxy"
|
||||
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
|
||||
REACT_APP_AWS_API_ENDPOINT="https://api.uniswap.org/v1/graphql"
|
||||
REACT_APP_BNB_RPC_URL="https://old-wispy-arrow.bsc.quiknode.pro/f5c060177236065c1058531a0615ab4f7a34a2fd"
|
||||
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"
|
||||
REACT_APP_FORTMATIC_KEY="pk_live_F937DF033A1666BF"
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID="G-KDP9B6W4H8"
|
||||
REACT_APP_INFURA_KEY="099fc58e0de9451d80b18d7c74caa7c1"
|
||||
REACT_APP_MOONPAY_API="https://api.moonpay.com"
|
||||
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLink?platform=web"
|
||||
REACT_APP_MOONPAY_LINK="https://us-central1-uniswap-mobile.cloudfunctions.net/signMoonpayLinkV2?platform=web&env=production"
|
||||
REACT_APP_MOONPAY_PUBLISHABLE_KEY="pk_live_uQG4BJC4w3cxnqpcSqAfohdBFDTsY6E"
|
||||
REACT_APP_FIREBASE_KEY="AIzaSyBcZWwTcTJHj_R6ipZcrJkXdq05PuX0Rs0"
|
||||
REACT_APP_SENTRY_ENABLED=true
|
||||
REACT_APP_SENTRY_TRACES_SAMPLE_RATE=0.00003
|
||||
REACT_APP_STATSIG_PROXY_URL="https://api.uniswap.org/v1/statsig-proxy"
|
||||
THE_GRAPH_SCHEMA_ENDPOINT="https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3"
|
||||
REACT_APP_SENTRY_ENABLED=false
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
*.config.ts
|
||||
*.d.ts
|
||||
/src/graphql/data/__generated__/types-and-hooks.ts
|
||||
/src/graphql/thegraph/__generated__/types-and-hooks.ts
|
||||
/src/schema/schema.graphql
|
||||
49
.eslintrc.js
@@ -4,4 +4,53 @@ require('@uniswap/eslint-config/load')
|
||||
|
||||
module.exports = {
|
||||
extends: '@uniswap/eslint-config/react',
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*'],
|
||||
rules: {
|
||||
'multiline-comment-style': ['error', 'separate-lines'],
|
||||
},
|
||||
},
|
||||
{
|
||||
// Configuration/typings typically export objects/definitions that are used outside of the transpiled package
|
||||
// (eg not captured by the tsconfig). Because it's typical and not exceptional, this is turned off entirely.
|
||||
files: ['**/*.config.*', '**/*.d.ts'],
|
||||
rules: {
|
||||
'import/no-unused-modules': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts', '**/*.tsx'],
|
||||
rules: {
|
||||
'import/no-restricted-paths': [
|
||||
'error',
|
||||
{
|
||||
zones: [
|
||||
{
|
||||
target: ['src/**/*[!.test].ts', 'src/**/*[!.test].tsx'],
|
||||
from: 'src/test-utils',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: 'moment',
|
||||
// tree-shaking for moment is not configured because it degrades performance - see craco.config.cjs.
|
||||
message: 'moment is not configured for tree-shaking. If you use it, update the Webpack configuration.',
|
||||
},
|
||||
{
|
||||
name: 'zustand',
|
||||
importNames: ['default'],
|
||||
message: 'Default import from zustand is deprecated. Import `{ create }` instead.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
2
.github/CODEOWNERS
vendored
@@ -1 +1 @@
|
||||
@uniswap/web
|
||||
@uniswap/web-admins
|
||||
|
||||
48
.github/actions/report/action.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Report
|
||||
description: Report test failures via Slack
|
||||
inputs:
|
||||
name:
|
||||
description: The name of the failing test
|
||||
required: true
|
||||
SLACK_WEBHOOK_URL:
|
||||
description: The webhook URL to send the report to
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
|
||||
with:
|
||||
payload: |
|
||||
{
|
||||
"text": "${{ inputs.name }} failing on `${{ github.ref_name }}`",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "*${{ inputs.name }} failing on `${{ github.ref_name }}`:* <https://github.com/${{ github.repository}}/actions/runs/${{ github.run_id }}|view failing action>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "_This is blocking pull requests and branch promotions._\n_Please prioritize fixing the build._"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ inputs.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
|
||||
# The !oncall bot requires its own message:
|
||||
- uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
|
||||
with:
|
||||
payload: |
|
||||
{
|
||||
"text": "!oncall web"
|
||||
}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ inputs.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
|
||||
62
.github/actions/setup/action.yml
vendored
@@ -1,4 +1,6 @@
|
||||
name: Setup
|
||||
description: checkout repo, setup node, and install node_modules
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
@@ -8,14 +10,68 @@ runs:
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org
|
||||
cache: yarn
|
||||
cache: 'yarn'
|
||||
|
||||
- uses: actions/cache@v3
|
||||
id: install-cache
|
||||
with:
|
||||
path: node_modules/
|
||||
# node_modules/.cache is intentionally omitted, as this is used for build tool caches.
|
||||
path: |
|
||||
node_modules
|
||||
!node_modules/.cache
|
||||
key: ${{ runner.os }}-install-${{ hashFiles('**/yarn.lock') }}
|
||||
|
||||
- if: steps.install-cache.outputs.cache-hit != 'true'
|
||||
run: yarn install --frozen-lockfile --ignore-scripts
|
||||
shell: bash
|
||||
|
||||
# Validators compile quickly, so caching can be omitted.
|
||||
- run: yarn ajv
|
||||
shell: bash
|
||||
|
||||
# Contracts are compiled from source. If source hasn't changed, the contracts do not need to be re-compiled.
|
||||
- uses: actions/cache@v3
|
||||
id: contracts-cache
|
||||
with:
|
||||
path: |
|
||||
src/abis/types
|
||||
src/types/v3
|
||||
key: ${{ runner.os }}-contracts-${{ hashFiles('src/abis/**/*.json', 'node_modules/@uniswap/**/artifacts/contracts/**/*.json') }}
|
||||
- if: steps.contracts-cache.outputs.cache-hit != 'true'
|
||||
run: yarn contracts
|
||||
shell: bash
|
||||
|
||||
# GraphQL is generated from schema. The schema is always fetched, but if unchanged, graphql does not need to be re-generated.
|
||||
- run: yarn graphql:fetch
|
||||
shell: bash
|
||||
- uses: actions/cache@v3
|
||||
id: graphql-cache
|
||||
with:
|
||||
path: src/graphql/**/__generated__
|
||||
key: ${{ runner.os }}-graphql-${{ hashFiles('src/graphql/**/schema.graphql') }}
|
||||
- if: steps.graphql-cache.outputs.cache-hit != 'true'
|
||||
run: yarn graphql:generate
|
||||
shell: bash
|
||||
|
||||
# Messages are extracted from source.
|
||||
# A record of source file content hashes is maintained in node_modules/.cache/lingui by a custom extractor.
|
||||
# Messages are always extracted, but extraction may rely on the custom extractor's loaded cache.
|
||||
- uses: actions/cache@v3
|
||||
id: i18n-extract-cache
|
||||
with:
|
||||
path: |
|
||||
src/locales/en-US.po
|
||||
node_modules/.cache
|
||||
key: ${{ runner.os }}-i18n-extract-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-i18n-extract-
|
||||
- run: yarn i18n:extract
|
||||
shell: bash
|
||||
|
||||
# Translations are compiled from messages. If messages haven't changed, the translations do not need to be re-compiled.
|
||||
- uses: actions/cache@v3
|
||||
id: i18n-compile-cache
|
||||
with:
|
||||
path: src/locales/*.js
|
||||
key: ${{ runner.os }}-i18n-compile-${{ hashFiles('src/locales/*.po') }}
|
||||
- if: steps.i18n-compile-cache.outputs.cache-hit !='true'
|
||||
run: yarn i18n:compile
|
||||
shell: bash
|
||||
|
||||
1
.github/dependabot.yml
vendored
@@ -8,6 +8,5 @@ updates:
|
||||
allow:
|
||||
- dependency-name: '@uniswap/default-token-list'
|
||||
- dependency-name: '@uniswap/token-lists'
|
||||
- dependency-name: '@uniswap/widgets'
|
||||
reviewers:
|
||||
- 'Uniswap/dependabot-reviewers'
|
||||
|
||||
59
.github/pull_request_template.md
vendored
@@ -1,24 +1,45 @@
|
||||
Your PR title must follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary), and should start with one of the following [types](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type):
|
||||
<!-- Your PR title must follow conventional commits: https://github.com/Uniswap/interface#pr-title -->
|
||||
|
||||
- build: Changes that affect the build system or external dependencies (example scopes: yarn, eslint, typescript)
|
||||
- ci: Changes to our CI configuration files and scripts (example scopes: vercel, github, cypress)
|
||||
- docs: Documentation only changes
|
||||
- feat: A new feature
|
||||
- fix: A bug fix
|
||||
- perf: A code change that improves performance
|
||||
- refactor: A code change that neither fixes a bug nor adds a feature
|
||||
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
- test: Adding missing tests or correcting existing tests
|
||||
## Description
|
||||
<!-- Summary of change, including motivation and context. -->
|
||||
<!-- Use verb-driven language: "Fixes XYZ" instead of "This change fixes XYZ" -->
|
||||
|
||||
Example commit messages:
|
||||
|
||||
- feat: adds support for gnosis safe wallet
|
||||
- fix: removes a polling memory leak
|
||||
- chore: bumps redux version
|
||||
<!-- Delete inapplicable lines: -->
|
||||
_JIRA ticket:_
|
||||
_Slack thread:_
|
||||
_Relevant docs:_
|
||||
|
||||
Other things to note:
|
||||
|
||||
- Please describe the change using verb statements (ex: Removes X from Y)
|
||||
- PRs with multiple changes should use a list of verb statements
|
||||
- Add any relevant unit / integration tests
|
||||
- Changes will be previewable via vercel. Non-obvious changes should include instructions for how to reproduce them
|
||||
<!-- Delete this section if your change does not affect UI. -->
|
||||
## Screen capture
|
||||
|
||||
| Before | After (Desktop) | After (Mobile) |
|
||||
| ------------ |---------------- | -------------- |
|
||||
| paste_before | past_after | paste_after |
|
||||
|
||||
|
||||
## Test plan
|
||||
|
||||
<!-- Delete this section if your change is not a bug fix. -->
|
||||
### Reproducing the error
|
||||
|
||||
<!-- Include steps to reproduce the bug. -->
|
||||
1.
|
||||
|
||||
### QA (ie manual testing)
|
||||
|
||||
<!-- Include steps to test the change, ensuring no regression. -->
|
||||
- [ ] N/A
|
||||
|
||||
|
||||
#### Devices
|
||||
<!-- If applicable, include different devices and screen sizes that may be affected, and how you've tested them. -->
|
||||
|
||||
|
||||
### Automated testing
|
||||
|
||||
<!-- If N/A, check and note so it is obvious to your reviewers and does not show up as an incomplete task. -->
|
||||
<!-- eg - [x] Unit test N/A -->
|
||||
- [ ] Unit test
|
||||
- [ ] Integration/E2E test
|
||||
|
||||
32
.github/workflows/1-main-to-staging.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: 1 | Push main -> staging
|
||||
|
||||
# This CI job is responsible for pushing the current contents of the `main` branch to the
|
||||
# `releases/staging` branch, which will in turn kick off a deploy to the staging environment.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
# https://stackoverflow.com/questions/57921401/push-to-origin-from-github-action
|
||||
jobs:
|
||||
push-staging:
|
||||
name: 'Push to staging branch'
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: push/staging
|
||||
steps:
|
||||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
token: ${{ secrets.RELEASE_SERVICE_ACCESS_TOKEN }}
|
||||
ref: main
|
||||
- name: Git config
|
||||
run: |
|
||||
git config user.name "UL Service Account"
|
||||
git config user.email "hello-happy-puppy@users.noreply.github.com"
|
||||
- name: Add CODEOWNERS file
|
||||
run: |
|
||||
echo "@uniswap/web-admins" > CODEOWNERS
|
||||
git add CODEOWNERS
|
||||
git commit -m "ci: add global CODEOWNERS"
|
||||
- name: Git push
|
||||
run: |
|
||||
git push origin main:releases/staging --force
|
||||
41
.github/workflows/2-deploy-to-staging.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: 2 | Deploy staging
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'releases/staging'
|
||||
|
||||
jobs:
|
||||
deploy-to-staging:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: deploy/staging
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn prepare
|
||||
- run: yarn build
|
||||
env:
|
||||
REACT_APP_STAGING: 1
|
||||
- name: Setup node@16 (required by Cloudflare Pages)
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Update Cloudflare Pages deployment
|
||||
uses: cloudflare/pages-action@364c7ca09a4b57837c5967871d64a2c31adb8c0d
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
projectName: interface-staging
|
||||
directory: build
|
||||
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload source maps to Sentry
|
||||
uses: getsentry/action-release@bd5f874fcda966ba48139b0140fb3ec0cb3aabdd
|
||||
continue-on-error: true
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||
with:
|
||||
environment: staging
|
||||
sourcemaps: './build/static/js'
|
||||
url_prefix: '~/static/js'
|
||||
27
.github/workflows/3-staging-to-prod.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: 3 | Push staging -> prod
|
||||
|
||||
# This CI job is responsible for force pushing the content of releases/staging to releases/prod. It
|
||||
# is restricted to web-reviewers through virtue of the GitHub environment protection rules for the
|
||||
# prod environment.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
push-prod:
|
||||
name: 'Push to prod branch'
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: push/prod
|
||||
steps:
|
||||
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab
|
||||
with:
|
||||
token: ${{ secrets.RELEASE_SERVICE_ACCESS_TOKEN }}
|
||||
ref: releases/staging
|
||||
- name: Git config
|
||||
run: |
|
||||
git config user.name "UL Service Account"
|
||||
git config user.email "hello-happy-puppy@users.noreply.github.com"
|
||||
- name: Git push
|
||||
run: |
|
||||
git push origin releases/staging:releases/prod --force
|
||||
91
.github/workflows/4-deploy-to-prod.yml
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
name: 4 | Deploy prod
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'releases/prod'
|
||||
|
||||
jobs:
|
||||
deploy-to-prod:
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: deploy/prod
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn prepare
|
||||
- run: yarn build
|
||||
- name: Bump and tag
|
||||
id: github-tag-action
|
||||
uses: mathieudutour/github-tag-action@v6.0
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
release_branches: releases/prod
|
||||
default_bump: patch
|
||||
|
||||
- name: Pin to IPFS
|
||||
id: pinata
|
||||
uses: anantaramdas/ipfs-pinata-deploy-action@39bbda1ce1fe24c69c6f57861b8038278d53688d
|
||||
with:
|
||||
pin-name: Uniswap ${{ steps.github-tag-action.outputs.new_tag }}
|
||||
path: './build'
|
||||
pinata-api-key: ${{ secrets.PINATA_API_KEY }}
|
||||
pinata-secret-api-key: ${{ secrets.PINATA_API_SECRET_KEY }}
|
||||
|
||||
- name: Convert CIDv0 to CIDv1
|
||||
id: convert-cidv0
|
||||
uses: uniswap/convert-cidv0-cidv1@v1.0.0
|
||||
with:
|
||||
cidv0: ${{ steps.pinata.outputs.hash }}
|
||||
|
||||
- name: Release
|
||||
uses: actions/create-release@v1.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ steps.github-tag-action.outputs.new_tag }}
|
||||
release_name: Release ${{ steps.github-tag-action.outputs.new_tag }}
|
||||
body: |
|
||||
IPFS hash of the deployment:
|
||||
- CIDv0: `${{ steps.pinata.outputs.hash }}`
|
||||
- CIDv1: `${{ steps.convert-cidv0.outputs.cidv1 }}`
|
||||
|
||||
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
|
||||
|
||||
You can also access the Uniswap Interface from an IPFS gateway.
|
||||
**BEWARE**: The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported.
|
||||
**You should always use an IPFS gateway that enforces origin separation**, or our hosted deployment of the latest release at [app.uniswap.org](https://app.uniswap.org).
|
||||
Your Uniswap settings are never remembered across different URLs.
|
||||
|
||||
IPFS gateways:
|
||||
- https://${{ steps.convert-cidv0.outputs.cidv1 }}.ipfs.dweb.link/
|
||||
- https://${{ steps.convert-cidv0.outputs.cidv1 }}.ipfs.cf-ipfs.com/
|
||||
- [ipfs://${{ steps.pinata.outputs.hash }}/](ipfs://${{ steps.pinata.outputs.hash }}/)
|
||||
|
||||
${{ steps.github-tag-action.outputs.changelog }}
|
||||
|
||||
- name: Setup node@16 (required by Cloudflare Pages)
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Update Cloudflare Pages deployment
|
||||
uses: cloudflare/pages-action@364c7ca09a4b57837c5967871d64a2c31adb8c0d
|
||||
with:
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
|
||||
directory: build
|
||||
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: main
|
||||
|
||||
- name: Upload source maps to Sentry
|
||||
uses: getsentry/action-release@4744f6a65149f441c5f396d5b0877307c0db52c7
|
||||
continue-on-error: true
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
||||
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
||||
with:
|
||||
environment: production
|
||||
sourcemaps: './build/static/js'
|
||||
url_prefix: '~/static/js'
|
||||
89
.github/workflows/notify-slack-on-release-merge.yml
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
name: Slack notifications for releases/* merges
|
||||
|
||||
# This CI job will push notifications to Slack whenever code is merged into any releases/* branch
|
||||
#
|
||||
# The steps of the command line kung-fu shown below are as follows:
|
||||
# First we take the JSON-formatted Github context
|
||||
# echo $GITHUB_CONTEXT \
|
||||
# Then we parse out the specific fields we want for our messages using jq and format it into tab-separated values
|
||||
# | jq '.event.commits[] | [.url, .id[0:7], .author.username, .timestamp, .message] | @tsv' \
|
||||
# We need to do some cleaning on this output - specifically removing quotes and replacing newlines with something easier to split
|
||||
# | sed 's/"//g' | sed 's/\\t/;/g' | sed 's/\\n/;/g' | sed 's/\\//g' \
|
||||
# We then use awk to format the TSV into a Slack message
|
||||
# | awk -F';' '{print "• <"$1"|"$2"> (<https://github.com/"$3"|"$3">, "$4") - "$5}' \
|
||||
# Finally, we need to deal with some escaping issues with newlines so that we don't break the Slack message format
|
||||
# | sed 's/$/\\n/g' | tr -d '\n' \
|
||||
# Then shove the bytes into a file to store them in their exact format
|
||||
# > /tmp/parsed_github_context
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'releases/*'
|
||||
|
||||
jobs:
|
||||
notify-slack:
|
||||
name: 'Emit Slack notification(s)'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Parse event to slug
|
||||
id: parse-slug
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
# Formats the contents of the GitHub event into slugs: one line per commit, formatted for Slack.
|
||||
# Explanation for each line is in the comments above.
|
||||
run: |
|
||||
echo $GITHUB_CONTEXT \
|
||||
| jq '.event.commits[] | [.url, .id[0:7], .author.username, .timestamp, .message] | @tsv' \
|
||||
| sed 's/"//g' | sed 's/\\t/;/g' | sed 's/\\n/;/g' | sed 's/\\//g' \
|
||||
| awk -F';' '{print "• <"$1"|"$2"> (<https://github.com/"$3"|"$3">, "$4") - "$5}' \
|
||||
| sed 's/$/\\n/g' | tr -d '\n' \
|
||||
> /tmp/parsed_github_context
|
||||
echo "SLACK_COMMITS=$(cat /tmp/parsed_github_context)" >> "$GITHUB_OUTPUT"
|
||||
- name: Send custom JSON data to Slack workflow
|
||||
id: slack
|
||||
uses: slackapi/slack-github-action@007b2c3c751a190b6f0f040e47ed024deaa72844
|
||||
with:
|
||||
payload: |
|
||||
{
|
||||
"text": "GitHub Action build result: ${{ job.status }}\n${{ github.event.pull_request.html_url || github.event.head_commit.url }}",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "divider"
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "*Code merged to <https://github.com/Uniswap/interface/tree/${{ github.ref }}|${{ github.ref_name }}> branch:*\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "divider"
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "*Actor*: <https://github.com/${{ github.triggering_actor }}/|${{ github.triggering_actor }}>\n*Force pushed*: ${{ github.event.forced || false }}\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "${{ steps.parse-slug.outputs.SLACK_COMMITS || 'New branch created' }}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "<${{ github.event.compare}}|View Diff>"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
|
||||
5
.github/workflows/release.yaml
vendored
@@ -42,10 +42,11 @@ jobs:
|
||||
needs: tag
|
||||
if: ${{ needs.tag.outputs.new_tag != null }}
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: release
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn prepare
|
||||
- run: yarn build
|
||||
|
||||
- name: Pin to IPFS
|
||||
@@ -112,7 +113,7 @@ jobs:
|
||||
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload source maps to Sentry
|
||||
uses: getsentry/action-release@bd5f874fcda966ba48139b0140fb3ec0cb3aabdd
|
||||
uses: getsentry/action-release@4744f6a65149f441c5f396d5b0877307c0db52c7
|
||||
continue-on-error: true
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
|
||||
145
.github/workflows/test.yml
vendored
@@ -1,5 +1,10 @@
|
||||
name: Test
|
||||
|
||||
# Many build steps have their own caches, so each job has its own cache to improve subsequent build times.
|
||||
# Build tools are configured to cache cache to node_modules/.cache, so this is cached independently of node_modules.
|
||||
# Caches are saved every run (by keying on github.run_id), and the most recent available cache is loaded.
|
||||
# See https://jongleberry.medium.com/speed-up-your-ci-and-dx-with-node-modules-cache-ac8df82b7bb0.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -14,7 +19,36 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: actions/cache@v3
|
||||
id: eslint-cache
|
||||
with:
|
||||
path: node_modules/.cache
|
||||
key: ${{ runner.os }}-eslint-${{ hashFiles('**/yarn.lock') }}-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-eslint-${{ hashFiles('**/yarn.lock') }}-
|
||||
- run: yarn lint
|
||||
- if: failure() && github.ref_name == 'main'
|
||||
uses: ./.github/actions/report
|
||||
with:
|
||||
name: Lint
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: actions/cache@v3
|
||||
id: tsc-cache
|
||||
with:
|
||||
path: node_modules/.cache
|
||||
key: ${{ runner.os }}-tsc-${{ hashFiles('**/yarn.lock') }}-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-tsc-${{ hashFiles('**/yarn.lock') }}-
|
||||
- run: yarn typecheck
|
||||
- if: failure() && github.ref_name == 'main'
|
||||
uses: ./.github/actions/report
|
||||
with:
|
||||
name: Typecheck
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
deps-tests:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -22,62 +56,66 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn yarn-deduplicate --strategy=highest --list --fail
|
||||
- if: failure() && github.ref_name == 'main'
|
||||
uses: ./.github/actions/report
|
||||
with:
|
||||
name: Dependency checks
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn prepare
|
||||
- run: yarn test
|
||||
- uses: actions/cache@v3
|
||||
id: jest-cache
|
||||
with:
|
||||
path: node_modules/.cache
|
||||
key: ${{ runner.os }}-jest-${{ hashFiles('**/yarn.lock') }}-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-jest-${{ hashFiles('**/yarn.lock') }}-
|
||||
- run: yarn test --coverage --maxWorkers=100%
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
fail_ci_if_error: false
|
||||
verbose: true
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- run: yarn prepare
|
||||
- run: yarn build
|
||||
- uses: actions/upload-artifact@v2
|
||||
flags: unit-tests
|
||||
- if: failure() && github.ref_name == 'main'
|
||||
uses: ./.github/actions/report
|
||||
with:
|
||||
name: build
|
||||
path: build
|
||||
if-no-files-found: error
|
||||
name: Unit tests
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
size-tests:
|
||||
needs: [build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: build
|
||||
path: build
|
||||
- run: yarn test:size
|
||||
|
||||
|
||||
cypress-build:
|
||||
build-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
- uses: actions/cache@v3
|
||||
id: cypress-cache
|
||||
id: build-e2e-cache
|
||||
with:
|
||||
path: /home/runner/.cache/Cypress
|
||||
key: ${{ runner.os }}-cypress-${{ hashFiles('node_modules/cypress') }}
|
||||
- if: steps.cypress-cache.outputs.cache-hit != 'true'
|
||||
run: yarn cypress install
|
||||
path: node_modules/.cache
|
||||
key: ${{ runner.os }}-build-e2e-${{ hashFiles('**/yarn.lock') }}-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-build-e2e-${{ hashFiles('**/yarn.lock') }}-
|
||||
- run: yarn build:e2e
|
||||
env:
|
||||
NODE_OPTIONS: "--max_old_space_size=4096"
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: build-e2e
|
||||
path: build
|
||||
if-no-files-found: error
|
||||
|
||||
# Allows for parallel re-runs of cypress tests without re-building.
|
||||
cypress-rerun:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: exit 0
|
||||
|
||||
cypress-test-matrix:
|
||||
needs: [build, cypress-build]
|
||||
needs: [build-e2e, cypress-rerun]
|
||||
runs-on: ubuntu-latest
|
||||
container: cypress/browsers:node-18.14.1-chrome-111.0.5563.64-1-ff-111.0-edge-111.0.1661.43-1
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -85,19 +123,19 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/setup
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: build
|
||||
path: build
|
||||
|
||||
- uses: actions/cache@v3
|
||||
id: cypress-cache
|
||||
with:
|
||||
path: /home/runner/.cache/Cypress
|
||||
key: ${{ runner.os }}-cypress-${{ hashFiles('node_modules/cypress') }}
|
||||
- if: steps.cypress-cache.outputs.cache-hit != 'true'
|
||||
run: yarn cypress install
|
||||
path: /root/.cache/Cypress
|
||||
key: ${{ runner.os }}-cypress
|
||||
- run: |
|
||||
yarn cypress install
|
||||
yarn cypress info
|
||||
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-e2e
|
||||
path: build
|
||||
|
||||
- uses: cypress-io/github-action@v4
|
||||
with:
|
||||
@@ -111,9 +149,24 @@ jobs:
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Included as a single job to check against for cypress test success, as cypress runs in a matrix.
|
||||
- uses: codecov/codecov-action@v3
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
fail_ci_if_error: false
|
||||
verbose: true
|
||||
flags: e2e-tests
|
||||
|
||||
# Included as a single job to check for cypress-test-matrix success, as a matrix cannot be checked.
|
||||
cypress-tests:
|
||||
if: always()
|
||||
needs: [cypress-test-matrix]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo 'Finished cypress tests https\://dashboard.cypress.io/projects/yp82ef'
|
||||
- uses: actions/checkout@v3
|
||||
- if: needs.cypress-test-matrix.result != 'success'
|
||||
run: exit 1
|
||||
- if: failure() && github.ref_name == 'main'
|
||||
uses: ./.github/actions/report
|
||||
with:
|
||||
name: Cypress tests
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEST_REPORTER_WEBHOOK }}
|
||||
|
||||
11
.gitignore
vendored
@@ -8,8 +8,10 @@
|
||||
/src/locales/**/en-US.po
|
||||
/src/locales/**/pseudo.po
|
||||
|
||||
# generated graphql types
|
||||
/src/graphql/**/__generated__
|
||||
# generated files
|
||||
/src/**/__generated__
|
||||
|
||||
# schema
|
||||
schema.graphql
|
||||
|
||||
# dependencies
|
||||
@@ -17,6 +19,7 @@ schema.graphql
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
/cache
|
||||
|
||||
# builds
|
||||
/build
|
||||
@@ -29,6 +32,10 @@ schema.graphql
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
instrumented
|
||||
.nyc_output
|
||||
.nyc_output/**/*
|
||||
|
||||
/.netlify
|
||||
|
||||
npm-debug.log*
|
||||
|
||||
1
CODEOWNERS
Normal file
@@ -0,0 +1 @@
|
||||
@uniswap/web-admins
|
||||
29
README.md
@@ -38,12 +38,39 @@ You can block an entire list of tokens by passing in a tokenlist like [here](./s
|
||||
|
||||
For steps on local deployment, development, and code contribution, please see [CONTRIBUTING](./CONTRIBUTING.md).
|
||||
|
||||
#### PR Title
|
||||
Your PR title must follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary), and should start with one of the following [types](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type):
|
||||
|
||||
- build: Changes that affect the build system or external dependencies (example scopes: yarn, eslint, typescript)
|
||||
- ci: Changes to our CI configuration files and scripts (example scopes: vercel, github, cypress)
|
||||
- docs: Documentation only changes
|
||||
- feat: A new feature
|
||||
- fix: A bug fix
|
||||
- perf: A code change that improves performance
|
||||
- refactor: A code change that neither fixes a bug nor adds a feature
|
||||
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
- test: Adding missing tests or correcting existing tests
|
||||
|
||||
Example commit messages:
|
||||
|
||||
- feat: adds support for gnosis safe wallet
|
||||
- fix: removes a polling memory leak
|
||||
- chore: bumps redux version
|
||||
|
||||
Other things to note:
|
||||
|
||||
- Please describe the change using verb statements (ex: Removes X from Y)
|
||||
- PRs with multiple changes should use a list of verb statements
|
||||
- Add any relevant unit / integration tests
|
||||
- Changes will be previewable via vercel. Non-obvious changes should include instructions for how to reproduce them
|
||||
|
||||
|
||||
## Accessing Uniswap V2
|
||||
|
||||
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for Uniswap protocol V2.
|
||||
|
||||
- Swap on Uniswap V2: <https://app.uniswap.org/#/swap?use=v2>
|
||||
- View V2 liquidity: <https://app.uniswap.org/#/pool/v2>
|
||||
- View V2 liquidity: <https://app.uniswap.org/#/pools/v2>
|
||||
- Add V2 liquidity: <https://app.uniswap.org/#/add/v2>
|
||||
- Migrate V2 liquidity to V3: <https://app.uniswap.org/#/migrate/v2>
|
||||
|
||||
|
||||
25
codecov.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
ignore:
|
||||
- "**/generated/**/*"
|
||||
- "**/generated/*"
|
||||
- "**/cypress/**/*"
|
||||
- "cypress/**/*"
|
||||
- "**/instrumented/**/*"
|
||||
- "**/styles/**/*"
|
||||
- "styles/**/*"
|
||||
- "**/constants/**/*"
|
||||
- "constants/**/*"
|
||||
|
||||
flag_management:
|
||||
default_rules:
|
||||
statuses:
|
||||
- type: project
|
||||
target: auto
|
||||
threshold: 1%
|
||||
if_ci_failed: error
|
||||
- type: patch
|
||||
target: 80%
|
||||
individual_flags:
|
||||
- name: e2e-tests
|
||||
statuses:
|
||||
- type: patch
|
||||
target: 0%
|
||||
114
craco.config.cjs
@@ -1,17 +1,65 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
/* eslint-env node */
|
||||
const { VanillaExtractPlugin } = require('@vanilla-extract/webpack-plugin')
|
||||
const { execSync } = require('child_process')
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const { DefinePlugin } = require('webpack')
|
||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
|
||||
const { DefinePlugin, IgnorePlugin } = require('webpack')
|
||||
|
||||
const commitHash = require('child_process').execSync('git rev-parse HEAD')
|
||||
const commitHash = execSync('git rev-parse HEAD').toString().trim()
|
||||
const isProduction = process.env.NODE_ENV === 'production'
|
||||
|
||||
// Linting and type checking are only necessary as part of development and testing.
|
||||
// Omit them from production builds, as they slow down the feedback loop.
|
||||
const shouldLintOrTypeCheck = !isProduction
|
||||
|
||||
module.exports = {
|
||||
babel: {
|
||||
plugins: ['@vanilla-extract/babel-plugin'],
|
||||
plugins: [
|
||||
'@vanilla-extract/babel-plugin',
|
||||
...(process.env.REACT_APP_ADD_COVERAGE_INSTRUMENTATION
|
||||
? [
|
||||
[
|
||||
'istanbul',
|
||||
{
|
||||
all: true,
|
||||
include: ['src/**/*.tsx', 'src/**/*.ts'],
|
||||
exclude: [
|
||||
'src/**/*.css',
|
||||
'src/**/*.css.ts',
|
||||
'src/**/*.test.ts',
|
||||
'src/**/*.test.tsx',
|
||||
'src/**/*.spec.ts',
|
||||
'src/**/*.spec.tsx',
|
||||
'src/**/graphql/**/*',
|
||||
'src/**/*.d.ts',
|
||||
],
|
||||
},
|
||||
],
|
||||
]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
eslint: {
|
||||
enable: shouldLintOrTypeCheck,
|
||||
pluginOptions(eslintConfig) {
|
||||
return Object.assign(eslintConfig, {
|
||||
cache: true,
|
||||
cacheLocation: 'node_modules/.cache/eslint/',
|
||||
ignorePath: '.gitignore',
|
||||
})
|
||||
},
|
||||
},
|
||||
typescript: {
|
||||
enableTypeChecking: shouldLintOrTypeCheck,
|
||||
},
|
||||
jest: {
|
||||
configure(jestConfig) {
|
||||
return Object.assign({}, jestConfig, {
|
||||
return Object.assign(jestConfig, {
|
||||
transform: {
|
||||
'\\.css\\.ts$': './vanilla.transform.cjs',
|
||||
...jestConfig.transform,
|
||||
},
|
||||
cacheDirectory: 'node_modules/.cache/jest',
|
||||
transformIgnorePatterns: ['@uniswap/conedison/format', '@uniswap/conedison/provider'],
|
||||
moduleNameMapper: {
|
||||
'@uniswap/conedison/format': '@uniswap/conedison/dist/format',
|
||||
@@ -21,27 +69,51 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
webpack: {
|
||||
plugins: [
|
||||
new VanillaExtractPlugin({ identifiers: 'short' }),
|
||||
new DefinePlugin({
|
||||
'process.env.REACT_APP_GIT_COMMIT_HASH': JSON.stringify(commitHash.toString()),
|
||||
}),
|
||||
],
|
||||
plugins: [new VanillaExtractPlugin({ identifiers: 'short' })],
|
||||
configure: (webpackConfig) => {
|
||||
const instanceOfMiniCssExtractPlugin = webpackConfig.plugins.find(
|
||||
(plugin) => plugin instanceof MiniCssExtractPlugin
|
||||
)
|
||||
if (instanceOfMiniCssExtractPlugin !== undefined) instanceOfMiniCssExtractPlugin.options.ignoreOrder = true
|
||||
webpackConfig.plugins = webpackConfig.plugins
|
||||
.map((plugin) => {
|
||||
// Extend process.env with dynamic values (eg commit hash).
|
||||
// This will make dynamic values available to JavaScript only, not to interpolated HTML (ie index.html).
|
||||
if (plugin instanceof DefinePlugin) {
|
||||
Object.assign(plugin.definitions['process.env'], {
|
||||
REACT_APP_GIT_COMMIT_HASH: JSON.stringify(commitHash),
|
||||
})
|
||||
}
|
||||
|
||||
// We're currently on Webpack 4.x that doesn't support the `exports` field in package.json.
|
||||
// CSS ordering is mitigated through scoping / naming conventions, so we can ignore order warnings.
|
||||
// See https://webpack.js.org/plugins/mini-css-extract-plugin/#remove-order-warnings.
|
||||
if (plugin instanceof MiniCssExtractPlugin) {
|
||||
plugin.options.ignoreOrder = true
|
||||
}
|
||||
|
||||
return plugin
|
||||
})
|
||||
.filter((plugin) => {
|
||||
// Case sensitive paths are enforced by TypeScript.
|
||||
// See https://www.typescriptlang.org/tsconfig#forceConsistentCasingInFileNames.
|
||||
if (plugin instanceof CaseSensitivePathsPlugin) return false
|
||||
|
||||
// IgnorePlugin is used to tree-shake moment locales, but we do not use moment in this project.
|
||||
if (plugin instanceof IgnorePlugin) return false
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
// We're currently on Webpack 4.x which doesn't support the `exports` field in package.json.
|
||||
// Instead, we need to manually map the import path to the correct exports path (eg dist or build folder).
|
||||
// See https://github.com/webpack/webpack/issues/9509.
|
||||
//
|
||||
// In case you need to add more modules, make sure to remap them to the correct path.
|
||||
//
|
||||
// Map @uniswap/conedison to its dist folder.
|
||||
// This is required because conedison uses * to redirect all imports to its dist.
|
||||
webpackConfig.resolve.alias['@uniswap/conedison'] = '@uniswap/conedison/dist'
|
||||
|
||||
// Configure webpack optimization:
|
||||
webpackConfig.optimization.splitChunks = Object.assign(webpackConfig.optimization.splitChunks, {
|
||||
// Cap the chunk size to 5MB.
|
||||
// react-scripts suggests a chunk size under 1MB after gzip, but we can only measure maxSize before gzip.
|
||||
// react-scripts also caps cacheable chunks at 5MB, which gzips to below 1MB, so we cap chunk size there.
|
||||
// See https://github.com/facebook/create-react-app/blob/d960b9e/packages/react-scripts/config/webpack.config.js#L713-L716.
|
||||
maxSize: 5 * 1024 * 1024,
|
||||
})
|
||||
|
||||
return webpackConfig
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import codeCoverageTask from '@cypress/code-coverage/task'
|
||||
import { defineConfig } from 'cypress'
|
||||
import { setupHardhatEvents } from 'cypress-hardhat'
|
||||
|
||||
export default defineConfig({
|
||||
projectId: 'yp82ef',
|
||||
videoUploadOnPasses: false,
|
||||
defaultCommandTimeout: 24000, // 2x average block time
|
||||
chromeWebSecurity: false,
|
||||
retries: { runMode: 2 },
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
async setupNodeEvents(on, config) {
|
||||
await setupHardhatEvents(on, config)
|
||||
codeCoverageTask(on, config)
|
||||
return {
|
||||
...config,
|
||||
// Only enable Chrome.
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
projectId: 'yp82ef',
|
||||
e2e: {
|
||||
specPattern: 'cypress/release.ts',
|
||||
},
|
||||
})
|
||||
28
cypress/e2e/buy-crypto-modal.test.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
describe('Buy Crypto Modal', () => {
|
||||
it('should open and close', () => {
|
||||
cy.visit('/')
|
||||
|
||||
// Open the fiat onramp modal
|
||||
cy.get(getTestSelector('buy-fiat-button')).click()
|
||||
cy.get(getTestSelector('fiat-onramp-modal')).should('be.visible')
|
||||
|
||||
// Click on a location that should be outside the modal, which should close it
|
||||
cy.get('body').click(0, 100)
|
||||
cy.get(getTestSelector('fiat-onramp-modal')).should('not.exist')
|
||||
})
|
||||
|
||||
it('should open and close, mobile viewport', () => {
|
||||
cy.viewport('iphone-6')
|
||||
cy.visit('/')
|
||||
|
||||
// Open the fiat onramp modal
|
||||
cy.get(getTestSelector('buy-fiat-button')).click()
|
||||
cy.get(getTestSelector('fiat-onramp-modal')).should('be.visible')
|
||||
|
||||
// Click on a location that should be outside the modal, which should close it
|
||||
cy.get('body').click(10, 10)
|
||||
cy.get(getTestSelector('fiat-onramp-modal')).should('not.exist')
|
||||
})
|
||||
})
|
||||
@@ -1,21 +1,22 @@
|
||||
import { getTestSelector } from '../utils'
|
||||
import { CONNECTED_WALLET_USER_STATE } from '../utils/user-state'
|
||||
|
||||
describe('Landing Page', () => {
|
||||
it('shows landing page when no selectedWallet', () => {
|
||||
cy.visit('/', { noWallet: true })
|
||||
it('shows landing page when no user state exists', () => {
|
||||
cy.visit('/', { userState: {} })
|
||||
cy.get(getTestSelector('landing-page'))
|
||||
cy.screenshot()
|
||||
})
|
||||
|
||||
it('redirects to swap page when selectedWallet is INJECTED', () => {
|
||||
cy.visit('/', { selectedWallet: 'INJECTED' })
|
||||
it('redirects to swap page when a user has already connected a wallet', () => {
|
||||
cy.visit('/', { userState: CONNECTED_WALLET_USER_STATE })
|
||||
cy.get('#swap-page')
|
||||
cy.url().should('include', '/swap')
|
||||
cy.screenshot()
|
||||
})
|
||||
|
||||
it('shows landing page when selectedWallet is INJECTED and ?intro=true is in query', () => {
|
||||
cy.visit('/?intro=true', { selectedWallet: 'INJECTED' })
|
||||
it('shows landing page when a user has already connected a wallet but ?intro=true is in query', () => {
|
||||
cy.visit('/?intro=true', { userState: CONNECTED_WALLET_USER_STATE })
|
||||
cy.get(getTestSelector('landing-page'))
|
||||
})
|
||||
|
||||
@@ -25,7 +26,16 @@ describe('Landing Page', () => {
|
||||
})
|
||||
|
||||
it('allows navigation to pool', () => {
|
||||
cy.viewport(2000, 1600)
|
||||
cy.visit('/swap')
|
||||
cy.get(getTestSelector('pool-nav-link')).first().click()
|
||||
cy.url().should('include', '/pool')
|
||||
cy.url().should('include', '/pools')
|
||||
})
|
||||
|
||||
it('allows navigation to pool on mobile', () => {
|
||||
cy.viewport('iphone-6')
|
||||
cy.visit('/swap')
|
||||
cy.get(getTestSelector('pool-nav-link')).last().click()
|
||||
cy.url().should('include', '/pools')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// see https://github.com/Uniswap/interface/pull/4115
|
||||
describe('Link', () => {
|
||||
it('should update route', () => {
|
||||
cy.viewport(2000, 1600)
|
||||
cy.visit('/')
|
||||
cy.contains('Pool').click()
|
||||
cy.get('[data-cy="join-pool-button"]').should('exist')
|
||||
|
||||
133
cypress/e2e/mini-portfolio/activity-history.test.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import { getTestSelector } from '../../utils'
|
||||
|
||||
describe('mini-portfolio activity history', () => {
|
||||
afterEach(() => {
|
||||
cy.intercept(
|
||||
{
|
||||
method: 'POST',
|
||||
url: 'https://beta.api.uniswap.org/v1/graphql',
|
||||
},
|
||||
// Pass an empty object to allow the original behavior
|
||||
{}
|
||||
).as('restoreOriginalBehavior')
|
||||
})
|
||||
|
||||
it('should deduplicate activity history by nonce', () => {
|
||||
cy.visit('/swap', { ethereum: 'hardhat' })
|
||||
.hardhat({ automine: false })
|
||||
.then((hardhat) => hardhat.wallet.getTransactionCount())
|
||||
.then((currentNonce) => {
|
||||
const nextNonce = currentNonce + 1
|
||||
// Mock graphql response to include a specific nonce.
|
||||
cy.intercept(
|
||||
{
|
||||
method: 'POST',
|
||||
url: 'https://beta.api.uniswap.org/v1/graphql',
|
||||
},
|
||||
{
|
||||
body: {
|
||||
data: {
|
||||
portfolios: [
|
||||
{
|
||||
id: 'UG9ydGZvbGlvOjB4NUNlYUI3NGU0NDZkQmQzYkY2OUUyNzcyMDBGMTI5ZDJiQzdBMzdhMQ==',
|
||||
assetActivities: [
|
||||
{
|
||||
id: 'QXNzZXRBY3Rpdml0eTpWSEpoYm5OaFkzUnBiMjQ2TUhnME5tUm1PVGs0T0RrNVl6UmtNR1kzWTJNNE9HRTVNVFEzTURBME9EWmtOVGhrTURnNFpqbG1NelkxTnpRM1l6WXdZek15WVRFNE4yWXlaRFEwWVdVNFh6QjRZV1EyWXpCa05XTmlOVEZsWWpjMU5qUTFaRGszT1RneE4yRTJZVEkxTmpreU1UbG1ZbVE1Wmw4d2VEQXpOR0UwTURjMk5EUTROV1kzWlRBNFkyRXhOak0yTm1VMU1ETTBPVEZoTm1GbU56ZzFNR1E9',
|
||||
timestamp: 1681150079,
|
||||
type: 'UNKNOWN',
|
||||
chain: 'ETHEREUM',
|
||||
transaction: {
|
||||
id: 'VHJhbnNhY3Rpb246MHg0NmRmOTk4ODk5YzRkMGY3Y2M4OGE5MTQ3MDA0ODZkNThkMDg4ZjlmMzY1NzQ3YzYwYzMyYTE4N2YyZDQ0YWU4XzB4YWQ2YzBkNWNiNTFlYjc1NjQ1ZDk3OTgxN2E2YTI1NjkyMTlmYmQ5Zl8weDAzNGE0MDc2NDQ4NWY3ZTA4Y2ExNjM2NmU1MDM0OTFhNmFmNzg1MGQ=',
|
||||
blockNumber: 17019453,
|
||||
hash: '0x46df998899c4d0f7cc88a914700486d58d088f9f365747c60c32a187f2d44ae8',
|
||||
status: 'CONFIRMED',
|
||||
to: '0x034a40764485f7e08ca16366e503491a6af7850d',
|
||||
from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
|
||||
nonce: currentNonce,
|
||||
__typename: 'Transaction',
|
||||
},
|
||||
assetChanges: [],
|
||||
__typename: 'AssetActivity',
|
||||
},
|
||||
{
|
||||
id: 'QXNzZXRBY3Rpdml0eTpWSEpoYm5OaFkzUnBiMjQ2TUhneE16UXpaR1ppTlROaE9XRmpNR00yWW1aaVpqUTNNRFEyWWpObFkyRXhORGN3TUdZd00yWXhOMkV3WWpnM1pqWXpPRFpsWVRnNU16QTRNVFZtWmpoaFh6QjRZMkUzTXpOalkySm1OelZoTXpnME1ERXhPR1ZpT1RjNU9EVTJOemRpTkdRMk56TTBZemMwWmw4d2VERmlOVEUxTkdGaE5HSTRaakF5TjJJNVptUXhPVE0wTVRFek1tWmpPV1JoWlRFd1pqY3pOVGs9',
|
||||
timestamp: 1681149995,
|
||||
type: 'SEND',
|
||||
chain: 'ETHEREUM',
|
||||
transaction: {
|
||||
id: 'VHJhbnNhY3Rpb246MHgxMzQzZGZiNTNhOWFjMGM2YmZiZjQ3MDQ2YjNlY2ExNDcwMGYwM2YxN2EwYjg3ZjYzODZlYTg5MzA4MTVmZjhhXzB4Y2E3MzNjY2JmNzVhMzg0MDExOGViOTc5ODU2NzdiNGQ2NzM0Yzc0Zl8weDFiNTE1NGFhNGI4ZjAyN2I5ZmQxOTM0MTEzMmZjOWRhZTEwZjczNTk=',
|
||||
blockNumber: 17019446,
|
||||
hash: '0x1343dfb53a9ac0c6bfbf47046b3eca14700f03f17a0b87f6386ea8930815ff8a',
|
||||
status: 'CONFIRMED',
|
||||
to: '0x1b5154aa4b8f027b9fd19341132fc9dae10f7359',
|
||||
from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
|
||||
nonce: nextNonce,
|
||||
__typename: 'Transaction',
|
||||
},
|
||||
assetChanges: [
|
||||
{
|
||||
__typename: 'TokenTransfer',
|
||||
id: 'VG9rZW5UcmFuc2ZlcjoweDVjZWFiNzRlNDQ2ZGJkM2JmNjllMjc3MjAwZjEyOWQyYmM3YTM3YTFfMHhiMWRjNDlmMDY1N2FkNTA1YjUzNzUyN2RkOWE1MDk0YTM2NTkzMWMxXzB4MTM0M2RmYjUzYTlhYzBjNmJmYmY0NzA0NmIzZWNhMTQ3MDBmMDNmMTdhMGI4N2Y2Mzg2ZWE4OTMwODE1ZmY4YQ==',
|
||||
asset: {
|
||||
id: 'VG9rZW46RVRIRVJFVU1fMHgxY2MyYjA3MGNhZjAxNmE3ZGRjMzA0N2Y5MzI3MmU4Yzc3YzlkZGU5',
|
||||
name: 'USD Coin (USDC)',
|
||||
symbol: 'USDC',
|
||||
address: '0x1cc2b070caf016a7ddc3047f93272e8c77c9dde9',
|
||||
decimals: 6,
|
||||
chain: 'ETHEREUM',
|
||||
standard: null,
|
||||
project: {
|
||||
id: 'VG9rZW5Qcm9qZWN0OkVUSEVSRVVNXzB4MWNjMmIwNzBjYWYwMTZhN2RkYzMwNDdmOTMyNzJlOGM3N2M5ZGRlOQ==',
|
||||
isSpam: true,
|
||||
logo: null,
|
||||
__typename: 'TokenProject',
|
||||
},
|
||||
__typename: 'Token',
|
||||
},
|
||||
tokenStandard: 'ERC20',
|
||||
quantity: '18011.212084',
|
||||
sender: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
|
||||
recipient: '0xb1dc49f0657ad505b537527dd9a5094a365931c1',
|
||||
direction: 'OUT',
|
||||
transactedValue: null,
|
||||
},
|
||||
],
|
||||
__typename: 'AssetActivity',
|
||||
},
|
||||
],
|
||||
__typename: 'Portfolio',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
).as('graphqlMock')
|
||||
|
||||
// Input swap info.
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('1')
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.contains('USDC').click()
|
||||
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
|
||||
|
||||
// Set slippage to a high value.
|
||||
cy.get(getTestSelector('open-settings-dialog-button')).click()
|
||||
cy.get(getTestSelector('max-slippage-settings')).click()
|
||||
cy.get(getTestSelector('slippage-input')).clear().type('5')
|
||||
cy.get('body').click('topRight')
|
||||
cy.get(getTestSelector('slippage-input')).should('not.exist')
|
||||
|
||||
// Click swap button.
|
||||
cy.contains('1 USDC = ').should('exist')
|
||||
cy.get('#swap-button').should('not.be', 'disabled').click()
|
||||
cy.get('#confirm-swap-or-send').click()
|
||||
cy.get(getTestSelector('dismiss-tx-confirmation')).click()
|
||||
|
||||
// Check activity history tab.
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('mini-portfolio-nav-activity')).click()
|
||||
|
||||
// Assert that the local pending transaction is replaced by a remote transaction with the same nonce.
|
||||
cy.contains('Swapping').should('not.exist')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -48,4 +48,16 @@ describe('Testing nfts', () => {
|
||||
cy.get(getTestSelector('nft-details-toggle-bag')).eq(1).click()
|
||||
cy.get(getTestSelector('nft-bag')).should('exist')
|
||||
})
|
||||
|
||||
it('should navigate to the owned nfts page', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('nft-view-self-nfts')).click()
|
||||
})
|
||||
|
||||
it('should close the sidebar when navigating to NFT details', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('mini-portfolio-nav-nfts')).click()
|
||||
cy.get(getTestSelector('mini-portfolio-nft')).first().click()
|
||||
cy.contains('Buy crypto').should('not.be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
223
cypress/e2e/permit2.test.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
import { MaxUint160, MaxUint256 } from '@uniswap/permit2-sdk'
|
||||
|
||||
import { DAI, USDC_MAINNET } from '../../src/constants/tokens'
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
const APPROVE_BUTTON = '[data-testid="swap-approve-button"]'
|
||||
|
||||
/** Initiates a swap and confirms its success. */
|
||||
function swaps() {
|
||||
// The swap-button can be temporarily disabled following approval, & Cypress will retry clicking the disabled version.
|
||||
// This ensures that we don't click until the button is enabled.
|
||||
cy.get('#swap-button').should('not.have.attr', 'disabled')
|
||||
|
||||
// Completes the swap.
|
||||
cy.get('#swap-button').click()
|
||||
cy.get('#confirm-swap-or-send').click()
|
||||
cy.get(getTestSelector('dismiss-tx-confirmation')).click()
|
||||
|
||||
// Verifies that there is a successful swap notification.
|
||||
cy.contains('Swapped').should('exist')
|
||||
}
|
||||
|
||||
// TODO(WEB-3299): Update tests to differentiate between permit2 vs token approval button text once UI is updated to indicate approval step.
|
||||
describe('Permit2', () => {
|
||||
// The same tokens & swap-amount combination is used for all permit2 tests.
|
||||
const INPUT_TOKEN = DAI
|
||||
const OUTPUT_TOKEN = USDC_MAINNET
|
||||
const TEST_BALANCE_INCREMENT = 0.01
|
||||
|
||||
beforeEach(() => {
|
||||
// Sets up a swap between INPUT_TOKEN and OUTPUT_TOKEN.
|
||||
cy.visit(`/swap/?inputCurrency=${INPUT_TOKEN.address}&outputCurrency=${OUTPUT_TOKEN.address}`, {
|
||||
ethereum: 'hardhat',
|
||||
})
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type(TEST_BALANCE_INCREMENT.toString())
|
||||
})
|
||||
|
||||
/** Asserts permit2 has a max approval for spend of the input token on-chain. */
|
||||
function expectTokenAllowanceForPermit2ToBeMax() {
|
||||
// check token approval
|
||||
return cy
|
||||
.hardhat()
|
||||
.then(({ approval, wallet }) => approval.getTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN }))
|
||||
.should('deep.equal', MaxUint256)
|
||||
}
|
||||
|
||||
/** Asserts the universal router has a max permit2 approval for spend of the input token on-chain. */
|
||||
function expectPermit2AllowanceForUniversalRouterToBeMax(approvalTime: number) {
|
||||
return cy
|
||||
.hardhat()
|
||||
.then((hardhat) => hardhat.approval.getPermit2Allowance({ owner: hardhat.wallet, token: INPUT_TOKEN }))
|
||||
.then((allowance) => {
|
||||
cy.wrap(MaxUint160.eq(allowance.amount)).should('eq', true)
|
||||
// Asserts that the on-chain expiration is in 30 days, within a tolerance of 20 seconds.
|
||||
const expected = Math.floor((approvalTime + 2_592_000_000) / 1000)
|
||||
cy.wrap(allowance.expiration).should('be.closeTo', expected, 20)
|
||||
})
|
||||
}
|
||||
|
||||
it('swaps when user has already approved token and permit2', () => {
|
||||
cy.hardhat()
|
||||
.then(({ approval, wallet }) => {
|
||||
approval.setTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN })
|
||||
approval.setPermit2Allowance({ owner: wallet, token: INPUT_TOKEN })
|
||||
})
|
||||
.then(swaps)
|
||||
})
|
||||
|
||||
it('swaps after completing full permit2 approval process', () => {
|
||||
cy.get(APPROVE_BUTTON)
|
||||
.click()
|
||||
.then(() => {
|
||||
const approvalTime = Date.now()
|
||||
cy.get(APPROVE_BUTTON).should('have.text', 'Approval pending')
|
||||
|
||||
// There should be a successful Approved notification.
|
||||
cy.contains('Approved').should('exist')
|
||||
|
||||
swaps()
|
||||
|
||||
expectTokenAllowanceForPermit2ToBeMax()
|
||||
expectPermit2AllowanceForUniversalRouterToBeMax(approvalTime)
|
||||
})
|
||||
})
|
||||
|
||||
it('swaps after handling user rejection of approvals and signatures', () => {
|
||||
const USER_REJECTION = { code: 4001 }
|
||||
cy.hardhat().then((hardhat) => {
|
||||
const tokenApprovalStub = cy.stub(hardhat.wallet, 'sendTransaction')
|
||||
tokenApprovalStub.rejects(USER_REJECTION) // reject token approval
|
||||
|
||||
const permitApprovalStub = cy.stub(hardhat.provider, 'send')
|
||||
permitApprovalStub.withArgs('eth_signTypedData_v4').rejects(USER_REJECTION) // reject permit approval
|
||||
permitApprovalStub.callThrough() // allows non-eth_signTypedData_v4 send calls to return non-stubbed values
|
||||
|
||||
// Clicking the approve button should trigger a token approval that will be rejected by the user (tokenApprovalStub).
|
||||
cy.get(APPROVE_BUTTON).click()
|
||||
|
||||
// The swap component should prompt approval again.
|
||||
cy.get(APPROVE_BUTTON)
|
||||
.should('have.text', `Approve use of ${INPUT_TOKEN.symbol}`)
|
||||
.then(() => {
|
||||
tokenApprovalStub.restore() // allow token approval
|
||||
|
||||
// The user is now allowing approval, but the permit2 signature will be rejected by the user (permitApprovalStub).
|
||||
cy.get(APPROVE_BUTTON).click()
|
||||
cy.get(APPROVE_BUTTON)
|
||||
.should('have.text', `Approve use of ${INPUT_TOKEN.symbol}`)
|
||||
.then(() => {
|
||||
permitApprovalStub.restore() // allow permit approval
|
||||
|
||||
// The swap should now be able to proceed, as the permit2 signature will be accepted by the user.
|
||||
cy.get(APPROVE_BUTTON)
|
||||
.click()
|
||||
.then(() => {
|
||||
const approvalTime = Date.now()
|
||||
cy.get(APPROVE_BUTTON).should('have.text', 'Approval pending')
|
||||
|
||||
// There should be a successful Approved notification.
|
||||
cy.contains('Approved').should('exist')
|
||||
|
||||
swaps()
|
||||
|
||||
expectTokenAllowanceForPermit2ToBeMax()
|
||||
expectPermit2AllowanceForUniversalRouterToBeMax(approvalTime)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('swaps with existing token approval and missing permit approval', () => {
|
||||
cy.hardhat()
|
||||
.then(({ approval, wallet }) => approval.setTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN }))
|
||||
.then(() => {
|
||||
cy.get(APPROVE_BUTTON)
|
||||
.click()
|
||||
.then(() => {
|
||||
const approvalTime = Date.now()
|
||||
|
||||
swaps()
|
||||
|
||||
expectPermit2AllowanceForUniversalRouterToBeMax(approvalTime)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('swaps with existing permit approval and missing token approval', () => {
|
||||
cy.hardhat()
|
||||
.then(({ approval, wallet }) => approval.setPermit2Allowance({ owner: wallet, token: INPUT_TOKEN }))
|
||||
.then(() => {
|
||||
cy.get(APPROVE_BUTTON).click()
|
||||
cy.get(APPROVE_BUTTON).should('have.text', 'Approval pending')
|
||||
|
||||
// There should be a successful Approved notification.
|
||||
cy.contains('Approved').should('exist')
|
||||
|
||||
swaps()
|
||||
|
||||
expectTokenAllowanceForPermit2ToBeMax()
|
||||
})
|
||||
})
|
||||
|
||||
it('prompts signature when existing permit approval is expired', () => {
|
||||
const expiredAllowance = { expiration: Math.floor((Date.now() - 1) / 1000) }
|
||||
|
||||
cy.hardhat()
|
||||
.then(({ approval, wallet }) => {
|
||||
approval.setTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN })
|
||||
approval.setPermit2Allowance({ owner: wallet, token: INPUT_TOKEN }, expiredAllowance)
|
||||
})
|
||||
.then(() => {
|
||||
cy.get(APPROVE_BUTTON)
|
||||
.click()
|
||||
.then(() => {
|
||||
const approvalTime = Date.now()
|
||||
|
||||
swaps()
|
||||
|
||||
expectPermit2AllowanceForUniversalRouterToBeMax(approvalTime)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('prompts signature when existing permit approval amount is too low', () => {
|
||||
const smallAllowance = { amount: 1 }
|
||||
|
||||
cy.hardhat()
|
||||
.then(({ approval, wallet }) => {
|
||||
approval.setTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN })
|
||||
approval.setPermit2Allowance({ owner: wallet, token: INPUT_TOKEN }, smallAllowance)
|
||||
})
|
||||
.then(() => {
|
||||
cy.get(APPROVE_BUTTON)
|
||||
.click()
|
||||
.then(() => {
|
||||
const approvalTime = Date.now()
|
||||
|
||||
swaps()
|
||||
|
||||
expectPermit2AllowanceForUniversalRouterToBeMax(approvalTime)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('prompts token approval when existing approval amount is too low', () => {
|
||||
cy.hardhat()
|
||||
.then(({ approval, wallet }) => {
|
||||
approval.setPermit2Allowance({ owner: wallet, token: INPUT_TOKEN })
|
||||
approval.setTokenAllowanceForPermit2({ owner: wallet, token: INPUT_TOKEN }, 1)
|
||||
})
|
||||
.then(() => {
|
||||
cy.get(APPROVE_BUTTON).click()
|
||||
|
||||
// There should be a successful Approved notification.
|
||||
cy.contains('Approved').should('exist')
|
||||
|
||||
swaps()
|
||||
|
||||
expectTokenAllowanceForPermit2ToBeMax()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
describe('Pool', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/pool').then(() => {
|
||||
cy.visit('/pools').then(() => {
|
||||
cy.wait('@eth_blockNumber')
|
||||
})
|
||||
})
|
||||
|
||||
11
cypress/e2e/position.test.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
describe('Position', () => {
|
||||
it('shows an valid state on a supported network', () => {
|
||||
cy.visit('/pools/1')
|
||||
cy.contains('UNI / ETH')
|
||||
})
|
||||
|
||||
it('shows an invalid state on a supported network', () => {
|
||||
cy.visit('/pools/788893')
|
||||
cy.contains('To view a position, you must be connected to the network it belongs to.')
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
import assert = require('assert')
|
||||
import assert from 'assert'
|
||||
|
||||
describe('Service Worker', () => {
|
||||
before(() => {
|
||||
@@ -53,8 +53,9 @@ describe('Service Worker', () => {
|
||||
it('installs a ServiceWorker', () => {
|
||||
cy.visit('/', { serviceWorker: true })
|
||||
.get('#swap-page')
|
||||
.wait('@NotInstalled', { timeout: 20000 })
|
||||
.window({ timeout: 20000 })
|
||||
// This is emitted after caching the entry file, which takes some time to load.
|
||||
.wait('@NotInstalled', { timeout: 60000 })
|
||||
.window()
|
||||
.and((win) => {
|
||||
expect(win.navigator.serviceWorker.controller?.state).to.equal('activated')
|
||||
})
|
||||
|
||||
@@ -1,53 +1,358 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { parseEther } from '@ethersproject/units'
|
||||
import { SupportedChainId, WETH9 } from '@uniswap/sdk-core'
|
||||
|
||||
import { UNI, USDC_MAINNET } from '../../src/constants/tokens'
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
const UNI_MAINNET = UNI[SupportedChainId.MAINNET]
|
||||
|
||||
describe('Swap', () => {
|
||||
before(() => {
|
||||
cy.visit('/swap')
|
||||
describe('Swap on main page', () => {
|
||||
before(() => {
|
||||
cy.visit('/swap', { ethereum: 'hardhat' })
|
||||
})
|
||||
|
||||
it('starts with ETH selected by default', () => {
|
||||
cy.get(`#swap-currency-input .token-amount-input`).should('have.value', '')
|
||||
cy.get(`#swap-currency-input .token-symbol-container`).should('contain.text', 'ETH')
|
||||
cy.get(`#swap-currency-output .token-amount-input`).should('not.have.value')
|
||||
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'Select token')
|
||||
})
|
||||
|
||||
it('can enter an amount into input', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.001').should('have.value', '0.001')
|
||||
})
|
||||
|
||||
it('zero swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.0').should('have.value', '0.0')
|
||||
})
|
||||
|
||||
it('invalid swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('\\').should('have.value', '')
|
||||
})
|
||||
|
||||
it('can enter an amount into output', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('0.001').should('have.value', '0.001')
|
||||
})
|
||||
|
||||
it('zero output amount', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('0.0').should('have.value', '0.0')
|
||||
})
|
||||
|
||||
it('should render an error when a transaction fails due to a passed deadline', () => {
|
||||
const DEADLINE_MINUTES = 1
|
||||
const TEN_MINUTES_MS = 1000 * 60 * DEADLINE_MINUTES * 10
|
||||
cy.visit('/swap', { ethereum: 'hardhat' })
|
||||
.hardhat({ automine: false })
|
||||
.then((hardhat) => {
|
||||
cy.then(() => hardhat.getBalance(hardhat.wallet.address, USDC_MAINNET))
|
||||
.then((balance) => Number(balance.toFixed(1)))
|
||||
.then((initialBalance) => {
|
||||
// Input swap info.
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.contains('USDC').click()
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('1')
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.equal', '')
|
||||
|
||||
// Set deadline to minimum. (1 minute)
|
||||
cy.get(getTestSelector('open-settings-dialog-button')).click()
|
||||
cy.get(getTestSelector('transaction-deadline-settings')).click()
|
||||
cy.get(getTestSelector('deadline-input')).clear().type(DEADLINE_MINUTES.toString())
|
||||
cy.get('body').click('topRight')
|
||||
cy.get(getTestSelector('deadline-input')).should('not.exist')
|
||||
|
||||
cy.get('#swap-button').click()
|
||||
cy.get('#confirm-swap-or-send').click()
|
||||
|
||||
// Dismiss the modal that appears when a transaction is broadcast to the network.
|
||||
cy.get(getTestSelector('dismiss-tx-confirmation')).click()
|
||||
|
||||
// The UI should show the transaction as pending.
|
||||
cy.contains('1 Pending').should('exist')
|
||||
|
||||
// Mine a block past the deadline.
|
||||
cy.then(() => hardhat.mine(1, TEN_MINUTES_MS)).then(() => {
|
||||
// The UI should no longer show the transaction as pending.
|
||||
cy.contains('1 Pending').should('not.exist')
|
||||
|
||||
// Check that the user is informed of the failure
|
||||
cy.contains('Swap failed').should('exist')
|
||||
|
||||
// Check that the balance is unchanged in the UI
|
||||
cy.get('#swap-currency-output [data-testid="balance-text"]').should(
|
||||
'have.text',
|
||||
`Balance: ${initialBalance}`
|
||||
)
|
||||
|
||||
// Check that the balance is unchanged on chain
|
||||
cy.then(() => hardhat.getBalance(hardhat.wallet.address, USDC_MAINNET))
|
||||
.then((balance) => Number(balance.toFixed(1)))
|
||||
.should('eq', initialBalance)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should default inputs from URL params ', () => {
|
||||
cy.visit(`/swap?inputCurrency=${UNI_MAINNET.address}`, { ethereum: 'hardhat' })
|
||||
cy.get(`#swap-currency-input .token-symbol-container`).should('contain.text', 'UNI')
|
||||
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'Select token')
|
||||
|
||||
cy.visit(`/swap?outputCurrency=${UNI_MAINNET.address}`, { ethereum: 'hardhat' })
|
||||
cy.get(`#swap-currency-input .token-symbol-container`).should('contain.text', 'Select token')
|
||||
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'UNI')
|
||||
|
||||
cy.visit(`/swap?inputCurrency=ETH&outputCurrency=${UNI_MAINNET.address}`, { ethereum: 'hardhat' })
|
||||
cy.get(`#swap-currency-input .token-symbol-container`).should('contain.text', 'ETH')
|
||||
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'UNI')
|
||||
})
|
||||
|
||||
it('ETH to wETH is same value (wrapped swaps have no price impact)', () => {
|
||||
cy.visit('/swap')
|
||||
cy.get(`#swap-currency-output .open-currency-select-button`).click()
|
||||
cy.contains('WETH').click()
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.01')
|
||||
cy.get('#swap-currency-output .token-amount-input').should('have.value', '0.01')
|
||||
})
|
||||
|
||||
it('Opens and closes the settings menu', () => {
|
||||
cy.visit('/swap')
|
||||
cy.contains('Settings').should('not.exist')
|
||||
cy.get(getTestSelector('swap-settings-button')).click()
|
||||
cy.contains('Max slippage').should('exist')
|
||||
cy.contains('Transaction deadline').should('exist')
|
||||
cy.contains('Auto Router API').should('exist')
|
||||
cy.get(getTestSelector('swap-settings-button')).click()
|
||||
cy.contains('Settings').should('not.exist')
|
||||
})
|
||||
|
||||
it('inputs reset when navigating between pages', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.01')
|
||||
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
|
||||
cy.visit('/pool')
|
||||
cy.visit('/swap')
|
||||
cy.get('#swap-currency-input .token-amount-input').should('have.value', '')
|
||||
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
|
||||
})
|
||||
|
||||
it('can swap ETH for USDC', () => {
|
||||
cy.visit('/swap', { ethereum: 'hardhat' })
|
||||
const TOKEN_ADDRESS = USDC_MAINNET.address
|
||||
const BALANCE_INCREMENT = 1
|
||||
cy.hardhat().then((hardhat) => {
|
||||
cy.then(() => hardhat.getBalance(hardhat.wallet.address, USDC_MAINNET))
|
||||
.then((balance) => Number(balance.toFixed(1)))
|
||||
.then((initialBalance) => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get(getTestSelector('token-search-input')).clear().type(TOKEN_ADDRESS)
|
||||
cy.contains('USDC').click()
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type(BALANCE_INCREMENT.toString())
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.equal', '')
|
||||
cy.get('#swap-button').click()
|
||||
cy.get('#confirm-swap-or-send').click()
|
||||
cy.get(getTestSelector('dismiss-tx-confirmation')).click()
|
||||
|
||||
// ui check
|
||||
cy.get('#swap-currency-output [data-testid="balance-text"]').should(
|
||||
'have.text',
|
||||
`Balance: ${initialBalance + BALANCE_INCREMENT}`
|
||||
)
|
||||
|
||||
// chain state check
|
||||
cy.then(() => hardhat.getBalance(hardhat.wallet.address, USDC_MAINNET))
|
||||
.then((balance) => Number(balance.toFixed(1)))
|
||||
.should('eq', initialBalance + BALANCE_INCREMENT)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('starts with ETH selected by default', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').should('have.value', '')
|
||||
cy.get('#swap-currency-input .token-symbol-container').should('contain.text', 'ETH')
|
||||
cy.get('#swap-currency-output .token-amount-input').should('not.have.value')
|
||||
cy.get('#swap-currency-output .token-symbol-container').should('contain.text', 'Select token')
|
||||
it('should be able to wrap ETH', () => {
|
||||
const BALANCE_INCREMENT = 1
|
||||
cy.visit('/swap', { ethereum: 'hardhat' })
|
||||
.hardhat()
|
||||
.then((hardhat) => {
|
||||
cy.then(() => hardhat.getBalance(hardhat.wallet.address, WETH9[SupportedChainId.MAINNET]))
|
||||
.then((balance) => Number(balance.toFixed(1)))
|
||||
.then((initialWethBalance) => {
|
||||
// Select WETH for the token output.
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.contains('WETH').click()
|
||||
|
||||
// Enter the amount to wrap.
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type(BALANCE_INCREMENT.toString())
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.equal', '')
|
||||
|
||||
// Click the wrap button.
|
||||
cy.get(getTestSelector('wrap-button')).should('not.be.disabled')
|
||||
cy.get(getTestSelector('wrap-button')).click()
|
||||
|
||||
// The pending transaction indicator should be visible.
|
||||
cy.get(getTestSelector('web3-status-connected')).should('have.descendants', ':contains("1 Pending")')
|
||||
|
||||
// <automine transaction>
|
||||
|
||||
// The pending transaction indicator should be gone.
|
||||
cy.get(getTestSelector('web3-status-connected')).should('not.have.descendants', ':contains("1 Pending")')
|
||||
|
||||
// The UI balance should have increased.
|
||||
cy.get('#swap-currency-output [data-testid="balance-text"]').should(
|
||||
'have.text',
|
||||
`Balance: ${initialWethBalance + BALANCE_INCREMENT}`
|
||||
)
|
||||
|
||||
// There should be a successful wrap notification.
|
||||
cy.contains('Wrapped').should('exist')
|
||||
|
||||
// The user's WETH account balance should have increased
|
||||
cy.then(() => hardhat.getBalance(hardhat.wallet.address, WETH9[SupportedChainId.MAINNET]))
|
||||
.then((balance) => Number(balance.toFixed(1)))
|
||||
.should('eq', initialWethBalance + BALANCE_INCREMENT)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('can enter an amount into input', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.001').should('have.value', '0.001')
|
||||
it('should be able to unwrap WETH', () => {
|
||||
const BALANCE_INCREMENT = 1
|
||||
cy.visit('/swap', { ethereum: 'hardhat' })
|
||||
.hardhat()
|
||||
.then((hardhat) => {
|
||||
cy.then(() => hardhat.getBalance(hardhat.wallet.address, WETH9[SupportedChainId.MAINNET])).then(
|
||||
(initialBalance) => {
|
||||
// Select WETH for the token output.
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.contains('WETH').click()
|
||||
|
||||
// Enter the amount to wrap.
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type(BALANCE_INCREMENT.toString())
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.equal', '')
|
||||
|
||||
// Click the wrap button.
|
||||
cy.get(getTestSelector('wrap-button')).should('not.be.disabled')
|
||||
cy.get(getTestSelector('wrap-button')).click()
|
||||
|
||||
// <automine transaction>
|
||||
|
||||
// The pending transaction indicator should be visible.
|
||||
cy.contains('1 Pending').should('exist')
|
||||
// The user should see a notification telling them they successfully wrapped their ETH.
|
||||
cy.contains('Wrapped').should('exist')
|
||||
|
||||
// Switch to unwrapping the ETH we just wrapped.
|
||||
cy.get(getTestSelector('swap-currency-button')).click()
|
||||
cy.get(getTestSelector('wrap-button')).should('not.be.disabled')
|
||||
|
||||
// Click the Unwrap button.
|
||||
cy.get(getTestSelector('wrap-button')).click()
|
||||
|
||||
// The pending transaction indicator should be visible.
|
||||
cy.contains('1 Pending').should('exist')
|
||||
|
||||
// <automine transaction>
|
||||
|
||||
// The pending transaction indicator should be gone.
|
||||
cy.contains('1 Pending').should('not.exist')
|
||||
// The user should see a notification telling them they successfully unwrapped their ETH.
|
||||
cy.contains('Unwrapped').should('exist')
|
||||
|
||||
// The UI balance should have decreased.
|
||||
cy.get('#swap-currency-input [data-testid="balance-text"]').should(
|
||||
'have.text',
|
||||
`Balance: ${initialBalance.toFixed(0)}`
|
||||
)
|
||||
|
||||
// There should be a successful unwrap notification.
|
||||
cy.contains('Unwrapped').should('exist')
|
||||
|
||||
// The user's WETH account balance should not have changed from the initial balance
|
||||
cy.then(() => hardhat.getBalance(hardhat.wallet.address, WETH9[SupportedChainId.MAINNET]))
|
||||
.then((balance) => balance.toFixed(0))
|
||||
.should('eq', initialBalance.toFixed(0))
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('zero swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.0').should('have.value', '0.0')
|
||||
it('should render and dismiss the wallet rejection modal', () => {
|
||||
cy.visit('/swap', { ethereum: 'hardhat' })
|
||||
.hardhat()
|
||||
.then((hardhat) => {
|
||||
cy.stub(hardhat.wallet, 'sendTransaction').log(false).rejects(new Error('user cancelled'))
|
||||
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get(getTestSelector('token-search-input')).clear().type(USDC_MAINNET.address)
|
||||
cy.contains('USDC').click()
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('1')
|
||||
cy.get('#swap-currency-input .token-amount-input').should('not.equal', '')
|
||||
cy.get('#swap-button').click()
|
||||
cy.get('#confirm-swap-or-send').click()
|
||||
cy.contains('Transaction rejected').should('exist')
|
||||
cy.contains('Dismiss').click()
|
||||
cy.contains('Transaction rejected').should('not.exist')
|
||||
})
|
||||
})
|
||||
|
||||
it('invalid swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('\\').should('have.value', '')
|
||||
})
|
||||
it.skip('should render an error for slippage failure', () => {
|
||||
cy.visit('/swap', { ethereum: 'hardhat' })
|
||||
.hardhat({ automine: false })
|
||||
.then((hardhat) => {
|
||||
cy.then(() => hardhat.provider.getBalance(hardhat.wallet.address)).then((initialBalance) => {
|
||||
// Gas estimation fails for this transaction (that would normally fail), so we stub it.
|
||||
const send = cy.stub(hardhat.provider, 'send').log(false)
|
||||
send.withArgs('eth_estimateGas').resolves(BigNumber.from(2_000_000))
|
||||
send.callThrough()
|
||||
|
||||
it('can enter an amount into output', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('0.001').should('have.value', '0.001')
|
||||
})
|
||||
// Set slippage to a very low value.
|
||||
cy.get(getTestSelector('open-settings-dialog-button')).click()
|
||||
cy.get(getTestSelector('max-slippage-settings')).click()
|
||||
cy.get(getTestSelector('slippage-input')).clear().type('0.01')
|
||||
cy.get('body').click('topRight')
|
||||
cy.get(getTestSelector('slippage-input')).should('not.exist')
|
||||
|
||||
it('zero output amount', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('0.0').should('have.value', '0.0')
|
||||
})
|
||||
// Open the currency select modal.
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
|
||||
it.skip('can swap ETH for DAI', () => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get('.token-item-0xc7AD46e0b8a400Bb3C915120d284AafbA8fc4735').click()
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.0000001')
|
||||
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
|
||||
cy.get('#swap-button').click()
|
||||
cy.get('#confirm-swap-or-send').should('contain', 'Confirm Swap')
|
||||
cy.get('[data-cy="confirmation-close-icon"]').click()
|
||||
})
|
||||
// Select UNI as output token
|
||||
cy.get(getTestSelector('token-search-input')).clear().type('Uniswap')
|
||||
cy.get(getTestSelector('currency-list-wrapper'))
|
||||
.contains(/^Uniswap$/)
|
||||
.first()
|
||||
// Our scrolling library (react-window) seems to freeze when acted on by cypress, with this element set to
|
||||
// `pointer-events: none`. This can be ignored using `{force: true}`.
|
||||
.click({ force: true })
|
||||
|
||||
it('add a recipient does not exist unless in expert mode', () => {
|
||||
cy.get('#add-recipient-button').should('not.exist')
|
||||
})
|
||||
// Swap 2 times.
|
||||
const AMOUNT_TO_SWAP = 400
|
||||
const NUMBER_OF_SWAPS = 2
|
||||
const INDIVIDUAL_SWAP_INPUT = AMOUNT_TO_SWAP / NUMBER_OF_SWAPS
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type(INDIVIDUAL_SWAP_INPUT.toString())
|
||||
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
|
||||
cy.get('#swap-button').click()
|
||||
cy.get('#confirm-swap-or-send').click()
|
||||
cy.get(getTestSelector('dismiss-tx-confirmation')).click()
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type(INDIVIDUAL_SWAP_INPUT.toString())
|
||||
cy.get('#swap-currency-output .token-amount-input').should('not.equal', '')
|
||||
cy.get('#swap-button').click()
|
||||
cy.get('#confirm-swap-or-send').click()
|
||||
cy.get(getTestSelector('dismiss-tx-confirmation')).click()
|
||||
|
||||
it.skip('ETH to wETH is same value (wrapped swaps have no price impact)', () => {
|
||||
cy.get('#swap-currency-output .open-currency-select-button').click()
|
||||
cy.get('.token-item-0xc778417E063141139Fce010982780140Aa0cD5Ab').click()
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.01')
|
||||
cy.get('#swap-currency-output .token-amount-input').should('have.value', '0.01')
|
||||
// The pending transaction indicator should be visible.
|
||||
cy.contains('Pending').should('exist')
|
||||
|
||||
cy.then(() => hardhat.mine()).then(() => {
|
||||
// The pending transaction indicator should not be visible.
|
||||
cy.contains('Pending').should('not.exist')
|
||||
|
||||
// Check for a failed transaction notification.
|
||||
cy.contains('Swap failed').should('exist')
|
||||
|
||||
// Assert that at least one of the swaps failed due to slippage.
|
||||
cy.then(() => hardhat.provider.getBalance(hardhat.wallet.address)).then((finalBalance) => {
|
||||
expect(finalBalance.gt(initialBalance.sub(parseEther(AMOUNT_TO_SWAP.toString())))).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import { SupportedChainId, WETH9 } from '@uniswap/sdk-core'
|
||||
|
||||
import { UNI } from '../../src/constants/tokens'
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
const UNI_MAINNET = UNI[SupportedChainId.MAINNET]
|
||||
|
||||
const UNI_ADDRESS = '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984'
|
||||
|
||||
describe('Token details', () => {
|
||||
before(() => {
|
||||
cy.visit('/')
|
||||
beforeEach(() => {
|
||||
cy.viewport(1440, 900)
|
||||
})
|
||||
|
||||
it('Uniswap token should have all information populated', () => {
|
||||
// Uniswap token
|
||||
cy.visit('/tokens/ethereum/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
|
||||
cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`)
|
||||
|
||||
// Price chart should be filled in
|
||||
cy.get('[data-cy="chart-header"]').should('include.text', '$')
|
||||
@@ -28,21 +35,16 @@ describe('Token details', () => {
|
||||
|
||||
// Links section should link out to Etherscan, More analytics, Website, Twitter
|
||||
cy.get('[data-cy="resources-container"]').within(() => {
|
||||
cy.contains('Etherscan')
|
||||
.should('have.attr', 'href')
|
||||
.and('include', 'etherscan.io/address/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
|
||||
cy.contains('Etherscan').should('have.attr', 'href').and('include', `etherscan.io/address/${UNI_ADDRESS}`)
|
||||
cy.contains('More analytics')
|
||||
.should('have.attr', 'href')
|
||||
.and('include', 'info.uniswap.org/#/tokens/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984')
|
||||
.and('include', `info.uniswap.org/#/tokens/${UNI_ADDRESS}`)
|
||||
cy.contains('Website').should('have.attr', 'href').and('include', 'uniswap.org')
|
||||
cy.contains('Twitter').should('have.attr', 'href').and('include', 'twitter.com/Uniswap')
|
||||
})
|
||||
|
||||
// Contract address should be displayed
|
||||
cy.contains('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984').should('exist')
|
||||
|
||||
// Swap widget should have this token pre-selected as the “destination” token
|
||||
cy.get(getTestSelector('token-select')).should('include.text', 'UNI')
|
||||
cy.contains(UNI_ADDRESS).should('exist')
|
||||
})
|
||||
|
||||
it('token with warning and low trading volume should have all information populated', () => {
|
||||
@@ -81,12 +83,75 @@ describe('Token details', () => {
|
||||
// Contract address should be displayed
|
||||
cy.contains('0xa71d0588EAf47f12B13cF8eC750430d21DF04974').should('exist')
|
||||
|
||||
// Swap widget should have this token pre-selected as the “destination” token
|
||||
cy.get(getTestSelector('token-select')).should('include.text', 'QOM')
|
||||
|
||||
// Warning label should show if relevant ([spec](https://www.notion.so/3f7fce6f93694be08a94a6984d50298e))
|
||||
cy.get('[data-cy="token-safety-message"]')
|
||||
.should('include.text', 'Warning')
|
||||
.and('include.text', "This token isn't traded on leading U.S. centralized exchanges")
|
||||
})
|
||||
|
||||
describe('swapping', () => {
|
||||
beforeEach(() => {
|
||||
// On mobile widths, we just link back to /swap instead of rendering the swap component.
|
||||
cy.viewport(1200, 800)
|
||||
cy.visit(`/tokens/ethereum/${UNI_MAINNET.address}`, {
|
||||
ethereum: 'hardhat',
|
||||
}).then(() => {
|
||||
cy.wait('@eth_blockNumber')
|
||||
cy.scrollTo('top')
|
||||
})
|
||||
})
|
||||
|
||||
it('should have the expected output for a tokens detail page', () => {
|
||||
cy.get(`#swap-currency-input .token-amount-input`).should('have.value', '')
|
||||
cy.get(`#swap-currency-input .token-symbol-container`).should('contain.text', 'Select token')
|
||||
cy.get(`#swap-currency-output .token-amount-input`).should('not.have.value')
|
||||
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'UNI')
|
||||
})
|
||||
|
||||
it('should automatically navigate to the new TDP', () => {
|
||||
cy.get(`#swap-currency-output .open-currency-select-button`).click()
|
||||
cy.contains('WETH').click()
|
||||
cy.url().should('include', `${WETH9[1].address}`)
|
||||
cy.url().should('not.include', `${UNI_MAINNET.address}`)
|
||||
})
|
||||
|
||||
it.only('should not share swap state with the main swap page', () => {
|
||||
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'UNI')
|
||||
cy.get(`#swap-currency-input .open-currency-select-button`).click()
|
||||
cy.contains('WETH').click()
|
||||
cy.visit('/swap')
|
||||
cy.contains('UNI').should('not.exist')
|
||||
cy.contains('WETH').should('not.exist')
|
||||
})
|
||||
|
||||
it('can enter an amount into input', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.001').should('have.value', '0.001')
|
||||
})
|
||||
|
||||
it('zero swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('0.0').should('have.value', '0.0')
|
||||
})
|
||||
|
||||
it('invalid swap amount', () => {
|
||||
cy.get('#swap-currency-input .token-amount-input').clear().type('\\').should('have.value', '')
|
||||
})
|
||||
|
||||
it('can enter an amount into output', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('0.001').should('have.value', '0.001')
|
||||
})
|
||||
|
||||
it('zero output amount', () => {
|
||||
cy.get('#swap-currency-output .token-amount-input').clear().type('0.0').should('have.value', '0.0')
|
||||
})
|
||||
|
||||
it('should show a L2 token even if the user is connected to a different network', () => {
|
||||
cy.visit('/tokens', { ethereum: 'hardhat' })
|
||||
cy.get(getTestSelector('tokens-network-filter-selected')).click()
|
||||
cy.get(getTestSelector('tokens-network-filter-option-arbitrum')).click()
|
||||
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Arbitrum')
|
||||
cy.get(getTestSelector('token-table-row-ARB')).click()
|
||||
cy.get(`#swap-currency-output .token-symbol-container`).should('contain.text', 'ARB')
|
||||
cy.contains('Connect to Arbitrum').should('exist')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ describe('Token explore', () => {
|
||||
|
||||
it('should load token leaderboard', () => {
|
||||
cy.visit('/tokens/ethereum')
|
||||
cy.get(getTestSelectorStartsWith('token-table')).its('length').should('be.eq', 100)
|
||||
cy.get(getTestSelectorStartsWith('token-table')).its('length').should('be.greaterThan', 0)
|
||||
// check sorted svg icon is present in volume cell, since tokens are sorted by volume by default
|
||||
cy.get(getTestSelector('header-row')).find(getTestSelector('volume-cell')).find('svg').should('exist')
|
||||
cy.get(getTestSelector('token-table-row-ETH')).find(getTestSelector('name-cell')).should('include.text', 'Ether')
|
||||
@@ -69,6 +69,6 @@ describe('Token explore', () => {
|
||||
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
|
||||
cy.reload()
|
||||
cy.get(getTestSelector('tokens-network-filter-selected')).should('contain', 'Optimism')
|
||||
cy.get(getTestSelector('chain-selector')).last().should('contain', 'Ethereum')
|
||||
cy.get(getTestSelector('chain-selector-logo')).invoke('attr', 'alt').should('eq', 'Ethereum')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('Universal search bar', () => {
|
||||
.and('contain.text', 'UNI')
|
||||
.and('contain.text', '$')
|
||||
.and('contain.text', '%')
|
||||
cy.get('[data-cy="searchbar-token-row-UNI"]').click()
|
||||
cy.get('[data-cy="searchbar-token-row-UNI"]').first().click()
|
||||
|
||||
cy.get('div').contains('Uniswap').should('exist')
|
||||
// Stats should have: TVL, 24H Volume, 52W low, 52W high.
|
||||
|
||||
@@ -1,18 +1,46 @@
|
||||
import { getTestSelector } from '../utils'
|
||||
|
||||
function visit(darkMode: boolean) {
|
||||
cy.visit('/swap', {
|
||||
onBeforeLoad(win) {
|
||||
cy.stub(win, 'matchMedia')
|
||||
.withArgs('(prefers-color-scheme: dark)')
|
||||
.returns({
|
||||
matches: darkMode,
|
||||
addEventListener() {
|
||||
// do nothing
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
describe('Wallet Dropdown', () => {
|
||||
before(() => {
|
||||
cy.visit('/pool')
|
||||
cy.visit('/pools')
|
||||
})
|
||||
|
||||
it('should change the theme', () => {
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).contains('Light theme').should('exist')
|
||||
cy.get(getTestSelector('wallet-settings')).click()
|
||||
cy.get(getTestSelector('theme-lightmode')).click()
|
||||
|
||||
cy.get(getTestSelector('theme-lightmode')).should('not.have.css', 'background-color', 'rgba(0, 0, 0, 0)')
|
||||
cy.get(getTestSelector('theme-darkmode')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
|
||||
cy.get(getTestSelector('theme-auto')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
|
||||
|
||||
cy.get(getTestSelector('theme-darkmode')).click()
|
||||
cy.get(getTestSelector('theme-lightmode')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
|
||||
cy.get(getTestSelector('theme-darkmode')).should('not.have.css', 'background-color', 'rgba(0, 0, 0, 0)')
|
||||
cy.get(getTestSelector('theme-auto')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
|
||||
|
||||
cy.get(getTestSelector('theme-auto')).click()
|
||||
cy.get(getTestSelector('theme-lightmode')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
|
||||
cy.get(getTestSelector('theme-darkmode')).should('have.css', 'background-color', 'rgba(0, 0, 0, 0)')
|
||||
cy.get(getTestSelector('theme-auto')).should('not.have.css', 'background-color', 'rgba(0, 0, 0, 0)')
|
||||
})
|
||||
|
||||
it('should select a language', () => {
|
||||
cy.get(getTestSelector('wallet-select-language')).click()
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('Deutsch').click({ force: true })
|
||||
cy.get(getTestSelector('wallet-header')).should('contain', 'Sprache')
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
|
||||
@@ -20,22 +48,13 @@ describe('Wallet Dropdown', () => {
|
||||
cy.get(getTestSelector('wallet-back')).click()
|
||||
})
|
||||
|
||||
it('should be able to view transactions', () => {
|
||||
cy.get(getTestSelector('wallet-transactions')).click()
|
||||
cy.get(getTestSelector('wallet-empty-transaction-text')).should('exist')
|
||||
cy.get(getTestSelector('wallet-back')).click()
|
||||
})
|
||||
|
||||
it('should change the theme when not connected', () => {
|
||||
cy.get(getTestSelector('wallet-disconnect')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).contains('Dark theme').should('exist')
|
||||
cy.get(getTestSelector('wallet-select-theme')).click()
|
||||
cy.get(getTestSelector('wallet-select-theme')).contains('Light theme').should('exist')
|
||||
cy.get(getTestSelector('wallet-settings')).click()
|
||||
cy.get(getTestSelector('theme-lightmode')).should('exist')
|
||||
})
|
||||
|
||||
it('should select a language when not connected', () => {
|
||||
cy.get(getTestSelector('wallet-select-language')).click()
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('Deutsch').click({ force: true })
|
||||
cy.get(getTestSelector('wallet-header')).should('contain', 'Sprache')
|
||||
cy.get(getTestSelector('wallet-language-item')).contains('English').click({ force: true })
|
||||
@@ -43,9 +62,35 @@ describe('Wallet Dropdown', () => {
|
||||
cy.get(getTestSelector('wallet-back')).click()
|
||||
})
|
||||
|
||||
it('should open the wallet connect modal from the drop down when not connected', () => {
|
||||
cy.get(getTestSelector('wallet-connect-wallet')).click()
|
||||
cy.get(getTestSelector('wallet-modal')).should('exist')
|
||||
cy.get(getTestSelector('wallet-modal-close')).click()
|
||||
it('should properly use dark system theme when auto theme setting is selected', () => {
|
||||
visit(true)
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-settings')).click()
|
||||
cy.get(getTestSelector('theme-auto')).click()
|
||||
cy.get(getTestSelector('wallet-header')).should('have.css', 'color', 'rgb(152, 161, 192)')
|
||||
})
|
||||
|
||||
it('should properly use light system theme when auto theme setting is selected', () => {
|
||||
visit(false)
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-settings')).click()
|
||||
cy.get(getTestSelector('theme-auto')).click()
|
||||
cy.get(getTestSelector('wallet-header')).should('have.css', 'color', 'rgb(119, 128, 160)')
|
||||
})
|
||||
|
||||
it('should dismiss the wallet bottom sheet when clicking buy crypto', () => {
|
||||
visit(false)
|
||||
cy.viewport('iphone-6')
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.get(getTestSelector('wallet-buy-crypto')).click()
|
||||
cy.contains('Buy crypto').should('not.be.visible')
|
||||
})
|
||||
|
||||
it('should use a bottom sheet and dismiss when on a mobile screen size', () => {
|
||||
visit(true)
|
||||
cy.viewport('iphone-6')
|
||||
cy.get(getTestSelector('web3-status-connected')).click()
|
||||
cy.root().click(15, 40)
|
||||
cy.get(getTestSelector('wallet-settings')).should('not.be.visible')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
const ONE_MINUTE = 60_000
|
||||
|
||||
describe(
|
||||
'Release',
|
||||
{
|
||||
pageLoadTimeout: ONE_MINUTE,
|
||||
retries: 30,
|
||||
},
|
||||
() => {
|
||||
it('loads swap page', () => {
|
||||
// TODO: We *must* wait in order to space out the retry attempts. Find a better way to do this.
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(ONE_MINUTE)
|
||||
.visit('/', {
|
||||
retryOnStatusCodeFailure: true,
|
||||
retryOnNetworkFailure: true,
|
||||
})
|
||||
.get('#swap-page')
|
||||
})
|
||||
}
|
||||
)
|
||||
80
cypress/support/commands.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'cypress-hardhat/lib/browser'
|
||||
|
||||
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
|
||||
|
||||
import { FeatureFlag } from '../../src/featureFlags/flags/featureFlags'
|
||||
import { UserState } from '../../src/state/user/reducer'
|
||||
import { CONNECTED_WALLET_USER_STATE } from '../utils/user-state'
|
||||
import { injected } from './ethereum'
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface ApplicationWindow {
|
||||
ethereum: Eip1193Bridge
|
||||
}
|
||||
interface VisitOptions {
|
||||
serviceWorker?: true
|
||||
featureFlags?: Array<FeatureFlag>
|
||||
/**
|
||||
* The mock ethereum provider to inject into the page.
|
||||
* @default 'goerli'
|
||||
*/
|
||||
// TODO(INFRA-175): Migrate all usage of 'goerli' to 'hardhat'.
|
||||
ethereum?: 'goerli' | 'hardhat'
|
||||
/**
|
||||
* Initial user state.
|
||||
* @default {@type import('../utils/user-state').CONNECTED_WALLET_USER_STATE}
|
||||
*/
|
||||
userState?: Partial<UserState>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sets up the injected provider to be a mock ethereum provider with the given mnemonic/index
|
||||
// eslint-disable-next-line no-undef
|
||||
Cypress.Commands.overwrite(
|
||||
'visit',
|
||||
(original, url: string | Partial<Cypress.VisitOptions>, options?: Partial<Cypress.VisitOptions>) => {
|
||||
if (typeof url !== 'string') throw new Error('Invalid arguments. The first argument to cy.visit must be the path.')
|
||||
|
||||
// Add a hash in the URL if it is not present (to use hash-based routing correctly with queryParams).
|
||||
let hashUrl = url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url
|
||||
if (options?.ethereum === 'goerli') hashUrl += `${url.includes('?') ? '&' : '?'}chain=goerli`
|
||||
|
||||
return cy
|
||||
.intercept('/service-worker.js', options?.serviceWorker ? undefined : { statusCode: 404 })
|
||||
.provider()
|
||||
.then((provider) =>
|
||||
original({
|
||||
...options,
|
||||
url: hashUrl,
|
||||
onBeforeLoad(win) {
|
||||
options?.onBeforeLoad?.(win)
|
||||
|
||||
// We want to test from a clean state, so we clear the local storage (which clears redux).
|
||||
win.localStorage.clear()
|
||||
|
||||
// Set initial user state.
|
||||
win.localStorage.setItem(
|
||||
'redux_localstorage_simple_user', // storage key for the user reducer using 'redux-localstorage-simple'
|
||||
JSON.stringify(options?.userState ?? CONNECTED_WALLET_USER_STATE)
|
||||
)
|
||||
|
||||
// Set feature flags, if configured.
|
||||
if (options?.featureFlags) {
|
||||
const featureFlags = options.featureFlags.reduce((flags, flag) => ({ ...flags, [flag]: 'enabled' }), {})
|
||||
win.localStorage.setItem('featureFlags', JSON.stringify(featureFlags))
|
||||
}
|
||||
|
||||
// Inject the mock ethereum provider.
|
||||
if (options?.ethereum === 'hardhat') {
|
||||
win.ethereum = provider
|
||||
} else {
|
||||
win.ethereum = injected
|
||||
}
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -5,88 +5,18 @@
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.ts using ES2015 syntax:
|
||||
import { injected } from './ethereum'
|
||||
import assert = require('assert')
|
||||
import '@cypress/code-coverage/support'
|
||||
import './commands'
|
||||
import './setupTests'
|
||||
|
||||
import { FeatureFlag } from '../../src/featureFlags/flags/featureFlags'
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface ApplicationWindow {
|
||||
ethereum: typeof injected
|
||||
}
|
||||
interface VisitOptions {
|
||||
serviceWorker?: true
|
||||
featureFlags?: Array<FeatureFlag>
|
||||
selectedWallet?: string
|
||||
noWallet?: boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sets up the injected provider to be a mock ethereum provider with the given mnemonic/index
|
||||
// eslint-disable-next-line no-undef
|
||||
Cypress.Commands.overwrite(
|
||||
'visit',
|
||||
(original, url: string | Partial<Cypress.VisitOptions>, options?: Partial<Cypress.VisitOptions>) => {
|
||||
assert(typeof url === 'string')
|
||||
|
||||
cy.intercept('/service-worker.js', options?.serviceWorker ? undefined : { statusCode: 404 }).then(() => {
|
||||
original({
|
||||
...options,
|
||||
url:
|
||||
(url.startsWith('/') && url.length > 2 && !url.startsWith('/#') ? `/#${url}` : url) +
|
||||
`${url.includes('?') ? '&' : '?'}chain=goerli`,
|
||||
onBeforeLoad(win) {
|
||||
options?.onBeforeLoad?.(win)
|
||||
win.localStorage.clear()
|
||||
|
||||
const userState = {
|
||||
selectedWallet: options?.noWallet !== true ? options?.selectedWallet || 'INJECTED' : undefined,
|
||||
fiatOnrampDismissed: true,
|
||||
}
|
||||
win.localStorage.setItem('redux_localstorage_simple_user', JSON.stringify(userState))
|
||||
|
||||
if (options?.featureFlags) {
|
||||
const featureFlags = options.featureFlags.reduce(
|
||||
(flags, flag) => ({
|
||||
...flags,
|
||||
[flag]: 'enabled',
|
||||
}),
|
||||
{}
|
||||
)
|
||||
win.localStorage.setItem('featureFlags', JSON.stringify(featureFlags))
|
||||
}
|
||||
|
||||
win.ethereum = injected
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
// Infura security policies are based on Origin headers.
|
||||
// These are stripped by cypress because chromeWebSecurity === false; this adds them back in.
|
||||
cy.intercept(/infura.io/, (res) => {
|
||||
res.headers['origin'] = 'http://localhost:3000'
|
||||
res.alias = res.body.method
|
||||
res.continue()
|
||||
})
|
||||
|
||||
// Graphql security policies are based on Origin headers.
|
||||
// These are stripped by cypress because chromeWebSecurity === false; this adds them back in.
|
||||
cy.intercept('https://api.uniswap.org/v1/graphql', (res) => {
|
||||
res.headers['origin'] = 'https://app.uniswap.org'
|
||||
res.continue()
|
||||
})
|
||||
cy.intercept('https://beta.api.uniswap.org/v1/graphql', (res) => {
|
||||
res.headers['origin'] = 'https://app.uniswap.org'
|
||||
res.continue()
|
||||
})
|
||||
})
|
||||
// Squelch logs from fetches, as they clutter the logs so much as to make them unusable.
|
||||
// See https://docs.cypress.io/api/commands/intercept#Disabling-logs-for-a-request.
|
||||
// TODO(https://github.com/cypress-io/cypress/issues/26069): Squelch only wildcard logs once Cypress allows it.
|
||||
const log = Cypress.log
|
||||
Cypress.log = function (options, ...args) {
|
||||
if (options.displayName === 'script' || options.name === 'request') return
|
||||
return log(options, ...args)
|
||||
} as typeof log
|
||||
|
||||
Cypress.on('uncaught:exception', () => {
|
||||
// returning false here prevents Cypress from failing the test
|
||||
|
||||
41
cypress/support/setupTests.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
// @ts-ignore
|
||||
import TokenListJSON from '@uniswap/default-token-list'
|
||||
|
||||
beforeEach(() => {
|
||||
// Many API calls enforce that requests come from our app, so we must mock Origin and Referer.
|
||||
cy.intercept('*', (req) => {
|
||||
req.headers['referer'] = 'https://app.uniswap.org'
|
||||
req.headers['origin'] = 'https://app.uniswap.org'
|
||||
})
|
||||
|
||||
// Infura uses a test endpoint, which allow-lists http://localhost:3000 instead.
|
||||
cy.intercept(/infura.io/, (req) => {
|
||||
req.headers['referer'] = 'http://localhost:3000'
|
||||
req.headers['origin'] = 'http://localhost:3000'
|
||||
req.alias = req.body.method
|
||||
req.continue()
|
||||
})
|
||||
|
||||
// Mock analytics responses to avoid analytics in tests.
|
||||
cy.intercept('https://api.uniswap.org/v1/amplitude-proxy', (req) => {
|
||||
const requestBody = JSON.stringify(req.body)
|
||||
const byteSize = new Blob([requestBody]).size
|
||||
req.reply(
|
||||
JSON.stringify({
|
||||
code: 200,
|
||||
server_upload_time: Date.now(),
|
||||
payload_size_bytes: byteSize,
|
||||
events_ingested: req.body.events.length,
|
||||
})
|
||||
)
|
||||
}).intercept('https://*.sentry.io', { statusCode: 200 })
|
||||
|
||||
// Mock our own token list responses to avoid the latency of IPFS.
|
||||
cy.intercept('https://gateway.ipfs.io/ipns/tokens.uniswap.org', TokenListJSON)
|
||||
.intercept('https://gateway.ipfs.io/ipns/extendedtokens.uniswap.org', { statusCode: 404 })
|
||||
.intercept('https://gateway.ipfs.io/ipns/unsupportedtokens.uniswap.org', { statusCode: 404 })
|
||||
|
||||
// Reset hardhat between tests to ensure isolation.
|
||||
// This resets the fork, as well as options like automine.
|
||||
cy.hardhat().then((hardhat) => hardhat.reset())
|
||||
})
|
||||
@@ -1,9 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true,
|
||||
"incremental": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"target": "es5",
|
||||
"lib": ["es5", "dom"],
|
||||
"types": ["cypress"]
|
||||
"target": "ES5",
|
||||
"tsBuildInfoFile": "../node_modules/.cache/tsbuildinfo/cypress", // avoid clobbering the build tsbuildinfo
|
||||
"types": ["cypress", "node"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
"exclude": ["node_modules"],
|
||||
"include": ["**/*.ts"],
|
||||
"watchOptions": {
|
||||
"excludeDirectories": ["node_modules"]
|
||||
}
|
||||
}
|
||||
|
||||
3
cypress/utils/user-state.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { UserState } from '../../src/state/user/reducer'
|
||||
|
||||
export const CONNECTED_WALLET_USER_STATE: Partial<UserState> = { selectedWallet: 'INJECTED' }
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-env node */
|
||||
|
||||
const defaultConfig = require('./graphql.config')
|
||||
const defaultConfig = require('./graphql.data.config')
|
||||
|
||||
module.exports = {
|
||||
src: defaultConfig.src,
|
||||
30
hardhat.config.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/* eslint-env node */
|
||||
require('dotenv').config()
|
||||
|
||||
// Block selection is arbitrary, as e2e tests will build up their own state.
|
||||
// The only requirement is that all infrastructure under test (eg Permit2 contracts) are already deployed.
|
||||
const BLOCK_NUMBER = 17023328
|
||||
|
||||
const mainnetFork = {
|
||||
url: `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_KEY}`,
|
||||
blockNumber: BLOCK_NUMBER,
|
||||
httpHeaders: {
|
||||
Origin: 'localhost:3000', // infura allowlists requests by origin
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
networks: {
|
||||
hardhat: {
|
||||
chainId: 1,
|
||||
forking: mainnetFork,
|
||||
accounts: {
|
||||
count: 1,
|
||||
},
|
||||
mining: {
|
||||
auto: true, // automine to make tests easier to write.
|
||||
interval: 0, // do not interval mine so that tests remain deterministic
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1,8 +1,63 @@
|
||||
import { default as babelExtractor } from '@lingui/cli/api/extractors/babel'
|
||||
import { createHash } from 'crypto'
|
||||
import { mkdirSync, readFileSync, writeFileSync } from 'fs'
|
||||
import * as path from 'path'
|
||||
import * as pkgUp from 'pkg-up' // pkg-up is used by lingui, and is used here to match lingui's own extractors
|
||||
|
||||
/**
|
||||
* A custom caching extractor for CI.
|
||||
* Falls back to the babelExtractor in a non-CI (ie local) environment.
|
||||
* Caches a file's latest extracted content's hash, and skips re-extracting if it is already present in the cache.
|
||||
* In CI, re-extracting files takes over one minute, so this is a significant savings.
|
||||
*/
|
||||
const cachingExtractor: typeof babelExtractor = {
|
||||
match(filename: string) {
|
||||
return babelExtractor.match(filename)
|
||||
},
|
||||
extract(filename: string, code: string, ...options: unknown[]) {
|
||||
if (!process.env.CI) return babelExtractor.extract(filename, code, ...options)
|
||||
|
||||
// This runs from node_modules/@lingui/conf, so we need to back out to the root.
|
||||
const pkg = pkgUp.sync()
|
||||
if (!pkg) throw new Error('No root found')
|
||||
const root = path.dirname(pkg)
|
||||
|
||||
const filePath = path.join(root, filename)
|
||||
const file = readFileSync(filePath)
|
||||
const hash = createHash('sha256').update(file).digest('hex')
|
||||
|
||||
const cacheRoot = path.join(root, 'node_modules/.cache/lingui')
|
||||
mkdirSync(cacheRoot, { recursive: true })
|
||||
const cachePath = path.join(cacheRoot, filename.replace(/\//g, '-'))
|
||||
|
||||
// Only read from the cache if we're not performing a "clean" run, as a clean run must re-extract from all
|
||||
// files to ensure that obsolete messages are removed.
|
||||
if (!process.argv.includes('--clean')) {
|
||||
try {
|
||||
const cache = readFileSync(cachePath, 'utf8')
|
||||
if (cache === hash) return
|
||||
} catch (e) {
|
||||
// It should not be considered an error if there is no cache file.
|
||||
}
|
||||
}
|
||||
writeFileSync(cachePath, hash)
|
||||
|
||||
return babelExtractor.extract(filename, code, ...options)
|
||||
},
|
||||
}
|
||||
|
||||
const linguiConfig = {
|
||||
catalogs: [
|
||||
{
|
||||
path: '<rootDir>/src/locales/{locale}',
|
||||
include: ['<rootDir>/src'],
|
||||
include: ['<rootDir>/src/**/*.ts', '<rootDir>/src/**/*.tsx'],
|
||||
exclude: [
|
||||
'<rootDir>/src/**/*.d.ts',
|
||||
'<rootDir>/src/**/*.test.*',
|
||||
'<rootDir>/src/types/v3/**',
|
||||
'<rootDir>/src/abis/types/**',
|
||||
'<rootDir>/src/graphql/**/__generated__/**',
|
||||
],
|
||||
},
|
||||
],
|
||||
compileNamespace: 'cjs',
|
||||
@@ -53,6 +108,7 @@ const linguiConfig = {
|
||||
runtimeConfigModule: ['@lingui/core', 'i18n'],
|
||||
sourceLocale: 'en-US',
|
||||
pseudoLocale: 'pseudo',
|
||||
extractors: [cachingExtractor],
|
||||
}
|
||||
|
||||
export default linguiConfig
|
||||
|
||||
109
package.json
@@ -1,43 +1,49 @@
|
||||
{
|
||||
"name": "@uniswap/interface",
|
||||
"version": "1.0.7",
|
||||
"version": "1.1.0",
|
||||
"description": "Uniswap Interface",
|
||||
"homepage": ".",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"ajv": "node scripts/compile-ajv-validators.js",
|
||||
"contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types \"./src/abis/**/*.json\"",
|
||||
"contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 \"./node_modules/@uniswap/**/artifacts/contracts/**/*[!dbg].json\"",
|
||||
"contracts:compile": "yarn contracts:compile:abi && yarn contracts:compile:v3",
|
||||
"contracts": "yarn contracts:compile:abi && yarn contracts:compile:v3",
|
||||
"graphql:fetch": "node scripts/fetch-schema.js",
|
||||
"graphql:generate:data": "graphql-codegen --config apollo-codegen.ts",
|
||||
"graphql:generate:thegraph": "graphql-codegen --config apollo-codegen_thegraph.ts",
|
||||
"graphql:generate:data": "graphql-codegen --config graphql.data.codegen.config.ts",
|
||||
"graphql:generate:thegraph": "graphql-codegen --config graphql.thegraph.codegen.config.ts",
|
||||
"graphql:generate": "yarn graphql:generate:data && yarn graphql:generate:thegraph",
|
||||
"prei18n:extract": "node scripts/prei18n-extract.js",
|
||||
"graphql": "yarn graphql:fetch && yarn graphql:generate",
|
||||
"i18n:extract": "lingui extract --locale en-US",
|
||||
"i18n:compile": "yarn i18n:extract && lingui compile",
|
||||
"i18n:pseudo": "lingui extract --locale pseudo && lingui compile",
|
||||
"prepare": "yarn contracts:compile && yarn graphql:fetch && yarn graphql:generate && yarn i18n:compile",
|
||||
"i18n:pseudo": "lingui extract --locale pseudo",
|
||||
"i18n:compile": "lingui compile",
|
||||
"i18n": "yarn i18n:extract --clean && yarn i18n:compile",
|
||||
"prepare": "yarn ajv && yarn contracts && yarn graphql && yarn i18n",
|
||||
"start": "craco start",
|
||||
"build": "craco build",
|
||||
"build:e2e": "REACT_APP_CSP_ALLOW_UNSAFE_EVAL=true REACT_APP_ADD_COVERAGE_INSTRUMENTATION=true craco build",
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js' --only-mapped",
|
||||
"serve": "serve build -l 3000",
|
||||
"deduplicate": "yarn-deduplicate --strategy=highest",
|
||||
"lint": "yarn eslint .",
|
||||
"test": "craco test --coverage",
|
||||
"test:size": "node scripts/test-size.js",
|
||||
"lint": "yarn eslint --ignore-path .gitignore --cache --cache-location node_modules/.cache/eslint/ .",
|
||||
"typecheck": "tsc",
|
||||
"test": "craco test",
|
||||
"cypress:open": "cypress open --browser chrome --e2e",
|
||||
"cypress:run": "cypress run --browser chrome --e2e",
|
||||
"postinstall": "patch-package"
|
||||
"deduplicate": "yarn-deduplicate --strategy=highest"
|
||||
},
|
||||
"jest": {
|
||||
"collectCoverageFrom": [
|
||||
"src/components/**/*.ts*",
|
||||
"src/hooks/**/*.ts*",
|
||||
"src/lib/hooks/**/*.ts*",
|
||||
"src/lib/state/**/*.ts*",
|
||||
"src/lib/utils/**/*.ts*",
|
||||
"src/pages/**/*.ts*",
|
||||
"src/state/**/*.ts*",
|
||||
"src/utils/**/*.ts*"
|
||||
"src/**/*.ts*",
|
||||
"!src/**/*.d.ts",
|
||||
"!src/abis/types/**",
|
||||
"!src/constants/**/*.ts",
|
||||
"!src/graphql/**/__generated__/**",
|
||||
"!src/locales/**",
|
||||
"!src/test-utils/**",
|
||||
"!src/types/v3/**"
|
||||
],
|
||||
"coveragePathIgnorePatterns": [
|
||||
".snap"
|
||||
],
|
||||
"coverageThreshold": {
|
||||
"global": {
|
||||
@@ -66,6 +72,7 @@
|
||||
"@lingui/cli": "^3.9.0",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.1",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@typechain/ethers-v5": "^7.0.0",
|
||||
"@types/array.prototype.flat": "^1.2.1",
|
||||
"@types/array.prototype.flatmap": "^1.2.2",
|
||||
@@ -91,20 +98,27 @@
|
||||
"@types/ua-parser-js": "^0.7.35",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/wcag-contrast": "^3.0.0",
|
||||
"@uniswap/eslint-config": "^1.1.1",
|
||||
"@uniswap/default-token-list": "^9.4.0",
|
||||
"@uniswap/eslint-config": "^1.2.0",
|
||||
"@vanilla-extract/babel-plugin": "^1.1.7",
|
||||
"@vanilla-extract/jest-transform": "^1.1.1",
|
||||
"@vanilla-extract/webpack-plugin": "^2.1.11",
|
||||
"cypress": "^10.3.1",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"cypress": "10.3.1",
|
||||
"cypress-hardhat": "^2.3.0",
|
||||
"env-cmd": "^10.1.0",
|
||||
"eslint": "^7.11.0",
|
||||
"eslint-plugin-import": "^2.27",
|
||||
"hardhat": "^2.14.0",
|
||||
"jest-fail-on-console": "^3.1.1",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"jest-styled-components": "^7.0.8",
|
||||
"ms.macro": "^2.0.0",
|
||||
"patch-package": "^6.4.7",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"prettier": "^2.7.1",
|
||||
"react-scripts": "^4.0.3",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"serve": "^11.3.2",
|
||||
"source-map-explorer": "^2.5.3",
|
||||
"ts-transform-graphql-tag": "^0.2.1",
|
||||
"typechain": "^5.0.0",
|
||||
"typescript": "^4.4.3",
|
||||
@@ -113,6 +127,7 @@
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.7.2",
|
||||
"@coinbase/wallet-sdk": "^3.6.4",
|
||||
"@cypress/code-coverage": "^3.10.0",
|
||||
"@fontsource/ibm-plex-mono": "^4.5.1",
|
||||
"@fontsource/inter": "^4.5.1",
|
||||
"@graphql-codegen/cli": "^2.15.0",
|
||||
@@ -126,17 +141,17 @@
|
||||
"@lingui/react": "^3.14.0",
|
||||
"@looksrare/sdk": "^0.10.2",
|
||||
"@metamask/jazzicon": "^2.0.0",
|
||||
"@opensea/seaport-js": "^1.0.10",
|
||||
"@opensea/seaport-js": "^1.2.0",
|
||||
"@popperjs/core": "^2.4.4",
|
||||
"@reach/dialog": "^0.10.3",
|
||||
"@reach/portal": "^0.10.3",
|
||||
"@react-hook/window-scroll": "^1.3.0",
|
||||
"@reduxjs/toolkit": "^1.6.1",
|
||||
"@sentry/react": "^7.40.0",
|
||||
"@sentry/tracing": "^7.40.0",
|
||||
"@sentry/react": "^7.45.0",
|
||||
"@sentry/tracing": "^7.45.0",
|
||||
"@sentry/types": "^7.45.0",
|
||||
"@types/react-window-infinite-loader": "^1.0.6",
|
||||
"@uniswap/analytics": "^1.3.1",
|
||||
"@uniswap/analytics-events": "^2.6.0",
|
||||
"@uniswap/analytics-events": "^2.10.0",
|
||||
"@uniswap/conedison": "^1.4.0",
|
||||
"@uniswap/governance": "^1.0.2",
|
||||
"@uniswap/liquidity-staker": "^1.0.2",
|
||||
@@ -144,17 +159,16 @@
|
||||
"@uniswap/permit2-sdk": "1.2.0",
|
||||
"@uniswap/redux-multicall": "^1.1.8",
|
||||
"@uniswap/router-sdk": "^1.3.0",
|
||||
"@uniswap/sdk-core": "^3.2.0",
|
||||
"@uniswap/sdk-core": "^3.2.2",
|
||||
"@uniswap/smart-order-router": "^3.6.1",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.30",
|
||||
"@uniswap/universal-router-sdk": "^1.3.6",
|
||||
"@uniswap/token-lists": "^1.0.0-beta.31",
|
||||
"@uniswap/universal-router-sdk": "^1.3.8",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "^1.1.0-beta.0",
|
||||
"@uniswap/v2-sdk": "^3.0.1",
|
||||
"@uniswap/v3-core": "1.0.0",
|
||||
"@uniswap/v3-periphery": "^1.1.1",
|
||||
"@uniswap/v3-sdk": "^3.9.0",
|
||||
"@uniswap/widgets": "^2.47.3",
|
||||
"@vanilla-extract/css": "^1.7.2",
|
||||
"@vanilla-extract/css-utils": "^0.1.2",
|
||||
"@vanilla-extract/dynamic": "^2.0.2",
|
||||
@@ -167,16 +181,18 @@
|
||||
"@visx/responsive": "^2.10.0",
|
||||
"@visx/shape": "^2.11.1",
|
||||
"@walletconnect/ethereum-provider": "^1.8.0",
|
||||
"@web3-react/coinbase-wallet": "8.1.2-beta.0",
|
||||
"@web3-react/core": "8.1.2-beta.0",
|
||||
"@web3-react/eip1193": "8.1.2-beta.0",
|
||||
"@web3-react/empty": "8.1.2-beta.0",
|
||||
"@web3-react/gnosis-safe": "8.1.2-beta.0",
|
||||
"@web3-react/metamask": "8.1.2-beta.0",
|
||||
"@web3-react/network": "8.1.2-beta.0",
|
||||
"@web3-react/types": "8.1.2-beta.0",
|
||||
"@web3-react/url": "8.1.2-beta.0",
|
||||
"@web3-react/walletconnect": "8.1.2-beta.0",
|
||||
"@web3-react/coinbase-wallet": "^8.2.0",
|
||||
"@web3-react/core": "^8.2.0",
|
||||
"@web3-react/eip1193": "^8.2.0",
|
||||
"@web3-react/empty": "^8.2.0",
|
||||
"@web3-react/gnosis-safe": "^8.2.0",
|
||||
"@web3-react/metamask": "^8.2.0",
|
||||
"@web3-react/network": "^8.2.0",
|
||||
"@web3-react/types": "^8.2.0",
|
||||
"@web3-react/url": "^8.2.0",
|
||||
"@web3-react/walletconnect": "^8.2.0",
|
||||
"ajv": "^8.11.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"array.prototype.flat": "^1.2.4",
|
||||
"array.prototype.flatmap": "^1.2.4",
|
||||
"cids": "^1.0.0",
|
||||
@@ -184,7 +200,6 @@
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"d3": "^7.6.1",
|
||||
"ethers": "^5.7.2",
|
||||
"firebase": "^9.1.3",
|
||||
"focus-visible": "^5.2.0",
|
||||
"get-graphql-schema": "^2.1.2",
|
||||
"graphql": "^16.5.0",
|
||||
@@ -200,7 +215,7 @@
|
||||
"numbro": "^2.3.6",
|
||||
"polished": "^3.3.2",
|
||||
"polyfill-object.fromentries": "^1.0.1",
|
||||
"popper-max-size-modifier": "^0.2.0",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"qs": "^6.9.4",
|
||||
"rc-slider": "^10.0.1",
|
||||
"react": "^18.2.0",
|
||||
@@ -223,12 +238,10 @@
|
||||
"rebass": "^4.0.7",
|
||||
"redux": "^4.1.2",
|
||||
"redux-localstorage-simple": "^2.3.1",
|
||||
"setimmediate": "^1.0.5",
|
||||
"statsig-react": "^1.22.0",
|
||||
"styled-components": "^5.3.5",
|
||||
"tiny-invariant": "^1.2.0",
|
||||
"ua-parser-js": "^0.7.28",
|
||||
"use-count-up": "^2.2.5",
|
||||
"use-resize-observer": "^9.0.2",
|
||||
"uuid": "^8.3.2",
|
||||
"video-extensions": "^1.2.0",
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
diff --git a/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js b/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js
|
||||
index 6e40061..10283a2 100644
|
||||
--- a/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js
|
||||
+++ b/node_modules/@vanilla-extract/css/dist/vanilla-extract-css.cjs.dev.js
|
||||
@@ -177,11 +177,13 @@ function generateIdentifier(debugId) {
|
||||
var fileScopeHash = hash__default["default"](packageName ? "".concat(packageName).concat(filePath) : filePath);
|
||||
var identifier = "".concat(fileScopeHash).concat(refCount);
|
||||
|
||||
- if (adapter_dist_vanillaExtractCssAdapter.getIdentOption() === 'debug') {
|
||||
- var devPrefix = getDevPrefix(debugId);
|
||||
+ if (process.env.VANILLA_EXTRACT_DEV_PREFIX) {
|
||||
+ if (adapter_dist_vanillaExtractCssAdapter.getIdentOption() === 'debug') {
|
||||
+ var devPrefix = getDevPrefix(debugId);
|
||||
|
||||
- if (devPrefix) {
|
||||
- identifier = "".concat(devPrefix, "__").concat(identifier);
|
||||
+ if (devPrefix) {
|
||||
+ identifier = "".concat(devPrefix, "__").concat(identifier);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,19 @@
|
||||
<meta name="theme-color" content="#FC72FF" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="script-src 'self' https://www.google-analytics.com https://www.googletagmanager.com 'unsafe-inline'"
|
||||
<% if (process.env.REACT_APP_CSP_ALLOW_UNSAFE_EVAL) { %>
|
||||
content="script-src 'self' https://www.google-analytics.com https://www.googletagmanager.com 'unsafe-inline' 'unsafe-eval'"
|
||||
<% } else { %>
|
||||
content="script-src 'self' https://www.google-analytics.com https://www.googletagmanager.com 'unsafe-inline'"
|
||||
<% } %>
|
||||
/>
|
||||
|
||||
<!--
|
||||
Apple Smart App Banner for Safari on iOS
|
||||
https://developer.apple.com/documentation/webkit/promoting_apps_with_smart_app_banners
|
||||
-->
|
||||
<meta name="apple-itunes-app" content="app-id=6443944476">
|
||||
|
||||
<!--
|
||||
manifest.json provides metadata used when the app is installed as a PWA.
|
||||
See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
|
||||
|
Before Width: | Height: | Size: 245 B |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.74219 20.29C6.74219 22.1885 7.71777 23.1729 9.59863 23.1729H18.959C20.8398 23.1729 21.8154 22.1885 21.8154 20.29V7.02734C21.8154 5.1377 20.8398 4.14453 18.959 4.14453H9.59863C7.71777 4.14453 6.74219 5.1377 6.74219 7.02734V20.29ZM8.46484 20.1846V7.13281C8.46484 6.31543 8.88672 5.86719 9.73926 5.86719H18.8096C19.6621 5.86719 20.0928 6.31543 20.0928 7.13281V20.1846C20.0928 21.002 19.6621 21.4502 18.8096 21.4502H9.73926C8.88672 21.4502 8.46484 21.002 8.46484 20.1846ZM10.5391 9.16309C10.9961 9.16309 11.3564 8.79395 11.3564 8.3457C11.3564 7.88867 10.9961 7.52832 10.5391 7.52832C10.0908 7.52832 9.72168 7.89746 9.72168 8.3457C9.72168 8.79395 10.0908 9.16309 10.5391 9.16309ZM12.9561 8.91699H18.2646C18.5811 8.91699 18.8359 8.66211 18.8359 8.3457C18.8359 8.0293 18.5811 7.77441 18.2646 7.77441H12.9561C12.6396 7.77441 12.3848 8.0293 12.3848 8.3457C12.3848 8.66211 12.6396 8.91699 12.9561 8.91699ZM10.5391 11.7822C10.9961 11.7822 11.3564 11.4131 11.3564 10.9648C11.3564 10.5078 10.9961 10.1562 10.5391 10.1562C10.082 10.1562 9.72168 10.5166 9.72168 10.9648C9.72168 11.4131 10.082 11.7822 10.5391 11.7822ZM12.9561 11.5361H18.2646C18.5898 11.5361 18.8359 11.2812 18.8359 10.9648C18.8359 10.6484 18.5898 10.4023 18.2646 10.4023H12.9561C12.6309 10.4023 12.3848 10.6484 12.3848 10.9648C12.3848 11.2812 12.6309 11.5361 12.9561 11.5361ZM10.5391 14.5332C10.9961 14.5332 11.3564 14.1641 11.3564 13.7158C11.3564 13.2588 10.9961 12.8896 10.5391 12.8896C10.0908 12.8896 9.72168 13.2676 9.72168 13.7158C9.72168 14.1553 10.0908 14.5332 10.5391 14.5332ZM12.9561 14.2871H18.2646C18.5811 14.2871 18.8359 14.0234 18.8359 13.7158C18.8359 13.3994 18.5898 13.1445 18.2646 13.1445H12.9561C12.6309 13.1445 12.3848 13.3994 12.3848 13.7158C12.3848 14.0234 12.6396 14.2871 12.9561 14.2871Z" fill="#0A0A3B"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 24 KiB |
@@ -1,4 +0,0 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.57471 23.4444H20.6489C22.4664 23.4444 23.5211 22.3897 23.5211 20.3745V10.6281C23.5211 8.61286 22.457 7.55818 20.4229 7.55818H19.0951C19.0481 5.4488 17.2494 3.69727 14.9988 3.69727C12.7576 3.69727 10.9496 5.4488 10.9025 7.55818H9.57471C7.54067 7.55818 6.47656 8.61286 6.47656 10.6281V20.3745C6.47656 22.3897 7.54067 23.4444 9.57471 23.4444ZM14.9988 5.36405C16.2889 5.36405 17.24 6.33398 17.2777 7.55818H12.7199C12.7576 6.33398 13.7087 5.36405 14.9988 5.36405Z" fill="#4673FA"/>
|
||||
<path d="M14.4407 20.3577C14.057 20.3577 13.744 20.1936 13.4613 19.814L10.9875 16.757C10.8057 16.5313 10.7148 16.2851 10.7148 16.0287C10.7148 15.5055 11.1187 15.0746 11.6438 15.0746C11.9568 15.0746 12.1991 15.1977 12.4516 15.526L14.4003 18.0393L18.5806 11.2483C18.7926 10.8995 19.0854 10.7148 19.3985 10.7148C19.9033 10.7148 20.3577 11.0739 20.3577 11.6073C20.3577 11.8433 20.2466 12.0997 20.0952 12.3357L15.3697 19.814C15.1475 20.173 14.8143 20.3577 14.4407 20.3577Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
|
||||
<path fill="#4673FA" d="M11.783 18.094c-.315.489-.78.763-1.32.763-.554 0-.988-.229-1.393-.763l-3.537-4.393c-.255-.32-.39-.656-.39-1.022 0-.763.57-1.358 1.304-1.358.45 0 .81.168 1.17.64l2.787 3.616 5.92-9.671c.315-.519.72-.763 1.17-.763.704 0 1.363.503 1.363 1.25 0 .352-.18.718-.374 1.038l-6.7 10.663z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 410 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.7783 21.1514C12.9805 21.3447 13.2266 21.4502 13.5078 21.4502C14.0967 21.4502 14.5537 20.9932 14.5537 20.4131C14.5537 20.123 14.4395 19.8594 14.2373 19.6572L8.08496 13.6455L14.2373 7.65137C14.4395 7.44922 14.5537 7.17676 14.5537 6.89551C14.5537 6.31543 14.0967 5.8584 13.5078 5.8584C13.2266 5.8584 12.9805 5.96387 12.7871 6.15723L5.94922 12.8369C5.70312 13.0654 5.58008 13.3467 5.58008 13.6543C5.58008 13.9619 5.70312 14.2256 5.94043 14.4629L12.7783 21.1514ZM20.6357 21.1514C20.8379 21.3447 21.0752 21.4502 21.3652 21.4502C21.9541 21.4502 22.4111 20.9932 22.4111 20.4131C22.4111 20.123 22.2881 19.8594 22.0947 19.6572L15.9336 13.6455L22.0947 7.65137C22.2969 7.44922 22.4111 7.17676 22.4111 6.89551C22.4111 6.31543 21.9541 5.8584 21.3652 5.8584C21.0752 5.8584 20.8379 5.96387 20.6357 6.15723L13.7979 12.8369C13.5605 13.0654 13.4375 13.3467 13.4287 13.6543C13.4287 13.9619 13.5518 14.2256 13.7979 14.4629L20.6357 21.1514Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,10 +0,0 @@
|
||||
<svg width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_564_11230)">
|
||||
<path d="M7.2207 16.0615L13.9092 9.22363C14.1377 8.97754 14.4102 8.86328 14.7178 8.86328C15.0254 8.86328 15.3066 8.98633 15.5352 9.22363L22.2148 16.0615C22.4082 16.2549 22.5137 16.501 22.5137 16.791C22.5137 17.3799 22.0566 17.8369 21.4766 17.8369C21.1953 17.8369 20.9229 17.7314 20.7207 17.5205L14.7266 11.3594L8.71484 17.5205C8.52148 17.7227 8.24902 17.8369 7.95898 17.8369C7.37891 17.8369 6.92188 17.3799 6.92188 16.791C6.92188 16.5098 7.02734 16.2549 7.2207 16.0615Z" fill="currentColor"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_564_11230">
|
||||
<rect width="28" height="28" fill="white" transform="translate(0.716797)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 774 B |
@@ -1,3 +0,0 @@
|
||||
<svg width="33" height="32" viewBox="0 0 33 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.12898 13.5458C4.33545 13.5458 4.13456 14.1484 4.56648 14.7411L7.02741 18.1362C7.35889 18.6083 7.89125 18.5982 8.21268 18.1362L10.6736 14.731C11.0854 14.1585 10.8946 13.5458 10.1111 13.5458H8.66469C9.56871 9.93973 12.8131 7.29799 16.7205 7.29799C19.4024 7.30804 21.7529 8.55357 23.2596 10.5324C23.6714 11.0045 24.2741 11.1551 24.7663 10.7935C25.2283 10.4621 25.3187 9.80915 24.8667 9.26674C23.0185 6.84598 19.9649 5.21875 16.7205 5.21875C11.7283 5.21875 7.49951 8.82478 6.53523 13.5458H5.12898ZM16.3488 9.78906C15.8567 9.78906 15.4649 10.1808 15.4649 10.6629V16.1071C15.4649 16.3482 15.5453 16.5692 15.716 16.8002L18.1167 19.9542C18.4683 20.4263 18.9906 20.5067 19.4225 20.1953C19.8343 19.904 19.8745 19.3717 19.5531 18.9397L17.2227 15.8058V10.6629C17.2227 10.1808 16.831 9.78906 16.3488 9.78906ZM16.7205 25.9911C21.7127 25.9911 25.9415 22.385 26.9058 17.6641H28.312C29.1055 17.6641 29.3064 17.0614 28.8745 16.4688L26.4236 13.0737C26.0821 12.6116 25.5497 12.6116 25.2283 13.0737L22.7674 16.4788C22.3555 17.0614 22.5464 17.6641 23.3299 17.6641H24.7863C23.8723 21.2701 20.6279 23.9219 16.7205 23.9219C14.0386 23.9118 11.6881 22.6563 10.1814 20.6775C9.7696 20.2054 9.17697 20.0547 8.67473 20.4163C8.21268 20.7478 8.12228 21.4007 8.57429 21.9531C10.4225 24.3638 13.4761 25.9911 16.7205 25.9911Z" fill="#4673FA"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,20 +0,0 @@
|
||||
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="13.75 11.75 46.5 46.5">
|
||||
<g filter="url(#filter0_d_12_3296)">
|
||||
<circle cx="37" cy="35" r="21" fill="#EEEEEE" shape-rendering="crispEdges"/>
|
||||
<circle cx="37" cy="35" r="21.75" stroke="black" stroke-opacity="0.1" stroke-width="1.5" shape-rendering="crispEdges"/>
|
||||
</g>
|
||||
<path d="M30 28L44.5 42.5" stroke="#A6A6A6" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M44.5 28L30 42.5" stroke="#A6A6A6" stroke-width="3" stroke-linecap="round"/>
|
||||
<defs>
|
||||
<filter id="filter0_d_12_3296" x="0.5" y="0.5" width="73" height="73" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="2"/>
|
||||
<feGaussianBlur stdDeviation="7"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_12_3296"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_12_3296" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.880867 0.48089C1.12465 0.237177 1.45525 0.100267 1.79997 0.100267C2.14468 0.100267 2.47528 0.237177 2.71907 0.48089L6.99997 4.76179L11.2809 0.48089C11.4008 0.356727 11.5442 0.25769 11.7028 0.189558C11.8614 0.121426 12.032 0.0855639 12.2046 0.084064C12.3773 0.082564 12.5484 0.115456 12.7082 0.180821C12.868 0.246186 13.0131 0.342715 13.1352 0.464776C13.2572 0.586836 13.3538 0.731984 13.4191 0.891749C13.4845 1.05151 13.5174 1.2227 13.5159 1.39531C13.5144 1.56792 13.4785 1.73851 13.4104 1.89712C13.3423 2.05572 13.2432 2.19917 13.1191 2.31909L7.91907 7.51909C7.67528 7.7628 7.34468 7.89971 6.99997 7.89971C6.65525 7.89971 6.32465 7.7628 6.08087 7.51909L0.880867 2.31909C0.637154 2.0753 0.500244 1.7447 0.500244 1.39999C0.500244 1.05528 0.637154 0.724676 0.880867 0.48089Z" fill="black"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 942 B |
@@ -1,10 +0,0 @@
|
||||
<svg width="15" height="24" viewBox="0 0 15 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.5">
|
||||
<path d="M7.49805 0V8.87184L14.995 12.2225L7.49805 0Z" fill="black" fill-opacity="0.602"/>
|
||||
<path d="M7.49797 0L0 12.2225L7.49797 8.87184V0Z" fill="black"/>
|
||||
<path d="M7.49805 17.9718V24L15 13.6189L7.49805 17.9718Z" fill="black" fill-opacity="0.602"/>
|
||||
<path d="M7.49797 24V17.9708L0 13.6189L7.49797 24Z" fill="black"/>
|
||||
<path d="M7.49805 16.5766L14.995 12.2227L7.49805 8.87402V16.5766Z" fill="black" fill-opacity="0.2"/>
|
||||
<path d="M0 12.2227L7.49797 16.5766V8.87402L0 12.2227Z" fill="black" fill-opacity="0.602"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 636 B |
@@ -1,4 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.85742 7.95904L8.00028 2.57129L11.1431 7.95904L8.00028 9.75496L4.85742 7.95904Z" fill="currentColor"/>
|
||||
<path d="M4.85742 8.85742L8.00028 10.6533L11.1431 8.85742L8.00028 13.3472L4.85742 8.85742Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 331 B |
@@ -1,4 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.85742 7.95904L8.00028 2.57129L11.1431 7.95904L8.00028 9.75496L4.85742 7.95904Z" fill="white"/>
|
||||
<path d="M4.85742 8.85742L8.00028 10.6533L11.1431 8.85742L8.00028 13.3472L4.85742 8.85742Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 317 B |
@@ -1,4 +0,0 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.85742 7.95904L8.00028 2.57129L11.1431 7.95904L8.00028 9.75496L4.85742 7.95904Z" fill="black"/>
|
||||
<path d="M4.85742 8.85742L8.00028 10.6533L11.1431 8.85742L8.00028 13.3472L4.85742 8.85742Z" fill="black"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 317 B |
@@ -1,8 +0,0 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.418 5.06609C10.4704 5.37012 10.5351 5.67294 10.6121 5.97456C10.6213 6.01097 10.6308 6.0473 10.6406 6.08354C10.6907 6.27249 10.7457 6.45997 10.8056 6.64599C10.813 6.66892 10.8213 6.69185 10.8313 6.71453C10.8599 6.78424 10.897 6.85018 10.9416 6.91085C10.9442 6.91446 10.9468 6.91807 10.9496 6.92142C11.0188 7.01331 11.1044 7.09143 11.2022 7.15176L11.2047 7.1533L11.2204 7.16232L11.2307 7.16824L11.7594 7.47485L11.7635 7.47716L11.7758 7.48438L11.7802 7.4867L11.7997 7.49803L11.8061 7.50164L11.8197 7.50808C11.8323 7.51478 11.8454 7.52097 11.8582 7.52689L11.887 7.53926C11.899 7.54415 11.9114 7.54905 11.9234 7.55343C11.9355 7.55781 11.9455 7.56116 11.9565 7.56477L11.9878 7.5743L12.0258 7.58409L12.0618 7.59208L12.0777 7.59543C12.1906 7.61595 12.3064 7.61508 12.4191 7.59285C12.6152 7.55094 12.8094 7.50396 13.0017 7.45192C13.0379 7.44238 13.0741 7.43233 13.11 7.42229C13.5952 7.28706 14.071 7.11965 14.5341 6.92116C14.6408 6.87564 14.7466 6.82841 14.8513 6.77945C15.1314 6.64925 15.4052 6.50823 15.6727 6.35639C15.4639 6.0239 15.2266 5.71035 14.9635 5.41933L14.9581 5.41314L14.9532 5.40773L14.9512 5.40541L14.9478 5.40181L14.9373 5.39021L14.9358 5.38867L14.9334 5.38609C14.9077 5.35861 14.8803 5.3328 14.8513 5.30879C14.7435 5.21963 14.617 5.15611 14.4812 5.123C14.3454 5.08989 14.204 5.08805 14.0674 5.11762L14.0605 5.11942C14.0428 5.12329 14.0253 5.12767 14.0076 5.13282L13.9968 5.13617C13.9812 5.14107 13.9655 5.14596 13.9499 5.15163L13.8698 5.18693L13.8392 5.20032C13.5675 5.31798 13.2895 5.42328 13.0053 5.5162C12.8224 5.57598 12.637 5.63034 12.4491 5.67929C12.3962 5.49379 12.3499 5.30656 12.3102 5.11762C12.3102 5.11427 12.3089 5.11118 12.3082 5.10783C12.3054 5.09443 12.3025 5.08206 12.3 5.06738C12.2433 4.79255 12.1987 4.51601 12.1662 4.23775C12.1614 4.19653 12.1567 4.1553 12.1526 4.11382C12.1388 4.0336 12.1143 3.95561 12.0797 3.88194C12.0205 3.75564 11.9337 3.64435 11.8258 3.55631C11.7178 3.46827 11.5915 3.40573 11.4563 3.37334C11.398 3.36046 11.3397 3.34757 11.281 3.33701C10.9415 3.27093 10.5975 3.23139 10.252 3.21875C10.2557 3.60683 10.2789 3.99447 10.3213 4.38023C10.3468 4.60833 10.379 4.83695 10.418 5.06609Z" fill="#30B465"/>
|
||||
<path d="M9.74715 3.2151C9.34195 3.22987 8.93894 3.28162 8.54308 3.36969C8.36795 3.41102 8.2087 3.50292 8.085 3.63405C7.96129 3.76518 7.87855 3.9298 7.84698 4.1076C7.8439 4.13748 7.84056 4.16711 7.83723 4.19674C7.83594 4.20937 7.83466 4.22251 7.83312 4.23436C7.8285 4.27533 7.82285 4.31629 7.81746 4.35726C7.78666 4.59344 7.74731 4.8291 7.69939 5.06425C7.6958 5.08099 7.69246 5.09774 7.68887 5.11449C7.64883 5.30343 7.60305 5.49168 7.55155 5.67925C7.36332 5.63047 7.17791 5.57611 6.99533 5.51616L6.95786 5.50354C6.76501 5.43964 6.57499 5.36999 6.38778 5.29458L6.36622 5.28608C6.33825 5.27474 6.31078 5.26289 6.28306 5.2513C6.24174 5.23404 6.20041 5.21729 6.1596 5.19977C6.12315 5.18397 6.08688 5.16799 6.05077 5.15185C6.03948 5.14772 6.02818 5.14437 6.01689 5.14077L5.99276 5.13278C5.97916 5.12892 5.96555 5.12582 5.95221 5.12247L5.93296 5.11784C5.91627 5.11397 5.89959 5.11114 5.8829 5.10856L5.87315 5.10676C5.74517 5.08773 5.61464 5.09582 5.48998 5.13053C5.36531 5.16523 5.24926 5.22577 5.14933 5.30824C5.12035 5.33224 5.09292 5.35806 5.06719 5.38553L5.06386 5.38914L5.05821 5.39532L5.04794 5.40666L5.03767 5.41826C4.77372 5.70935 4.53564 6.02308 4.32617 6.35584C4.59346 6.50768 4.86724 6.6487 5.14753 6.7789C5.25225 6.82785 5.358 6.87509 5.46478 6.9206C5.92737 7.11929 6.40258 7.28696 6.88727 7.4225C6.92321 7.43255 6.9594 7.4426 6.99559 7.45213L7.04487 7.46502C7.22061 7.51191 7.39831 7.55485 7.57799 7.59384C7.69062 7.61607 7.80641 7.61695 7.91936 7.59642C7.92809 7.59487 7.93682 7.59255 7.94503 7.59075L7.96608 7.58611C7.98225 7.5825 7.99791 7.57812 8.01382 7.57374L8.03949 7.56601L8.07516 7.5539L8.11059 7.53999L8.13831 7.52814C8.15242 7.5217 8.16654 7.51474 8.1804 7.50779L8.19093 7.50263L8.19632 7.4998L8.21018 7.49181H8.21146L8.21377 7.49052L8.23764 7.47661L8.75099 7.1798L8.79334 7.15532L8.79591 7.15378C8.89417 7.09278 8.98005 7.01366 9.04899 6.9206C9.05181 6.91725 9.05438 6.91365 9.05695 6.91004C9.11592 6.82962 9.16159 6.74019 9.19221 6.64518C9.20299 6.61168 9.21326 6.57819 9.22378 6.54469C9.27153 6.39131 9.31602 6.23732 9.35726 6.08273C9.36701 6.04632 9.37651 6.00999 9.38575 5.97375C9.46292 5.6723 9.5276 5.36947 9.57979 5.06528C9.61898 4.83631 9.65132 4.60666 9.67682 4.37632C9.7192 3.99057 9.74233 3.60292 9.74612 3.21484L9.74715 3.2151Z" fill="#30B465"/>
|
||||
<path d="M5.57074 12.0222C5.81355 11.8202 6.04866 11.6074 6.27608 11.3838C6.30303 11.358 6.32947 11.3322 6.35616 11.3047C6.40596 11.2549 6.45472 11.2039 6.50349 11.1532C6.52762 11.1274 6.55175 11.1032 6.57562 11.0779C6.6344 11.0156 6.69292 10.9527 6.75041 10.8891C6.90195 10.7247 6.98935 10.511 6.99656 10.2872V10.2246C6.99656 10.2055 6.99451 10.1862 6.99246 10.1671H6.99656V9.70337H6.99271C6.99477 9.68327 6.99605 9.66292 6.99656 9.64256V9.57995C6.98858 9.36819 6.90901 9.16546 6.77095 9.00514C6.7635 8.99664 6.7558 8.98839 6.7481 8.97937L6.73014 8.96005C6.67298 8.89684 6.61515 8.8344 6.55662 8.77274L6.54713 8.76269C6.48398 8.69622 6.42007 8.63052 6.35539 8.56559C6.32972 8.53983 6.30226 8.51277 6.27531 8.48649C6.04824 8.2632 5.81312 8.05038 5.56997 7.84804C5.4317 7.73278 5.29096 7.62114 5.14774 7.5131C4.8683 7.30182 4.58048 7.10429 4.28428 6.9205C4.21481 6.87722 4.14482 6.83471 4.07432 6.79297C4.05191 6.83522 4.03009 6.87773 4.00887 6.9205C3.85905 7.21965 3.73189 7.52971 3.62848 7.84804H3.63412C3.59393 7.95218 3.57297 8.0628 3.57227 8.17448C3.57227 8.18221 3.57227 8.1902 3.57227 8.19793C3.57227 8.2005 3.57227 8.20308 3.57227 8.20566C3.57741 8.44434 3.67425 8.6718 3.84254 8.8405L3.85743 8.85416C3.87001 8.86627 3.8831 8.87838 3.89593 8.88997C3.91621 8.90466 3.93649 8.91986 3.95676 8.9348C3.97114 8.94537 3.98551 8.95567 3.99963 8.96649C4.17708 9.09927 4.35025 9.23831 4.51914 9.38363C4.57527 9.43241 4.63113 9.48179 4.68675 9.53177L4.72859 9.56991C4.76127 9.59979 4.79378 9.62994 4.82612 9.66034L4.87027 9.70182C4.94984 9.77757 5.0283 9.85486 5.10564 9.93371C5.0283 10.0125 4.94984 10.0898 4.87027 10.1656C4.8097 10.2233 4.74852 10.28 4.68675 10.3356C4.46549 10.5356 4.23645 10.724 3.99963 10.9009L3.9465 10.9403C3.92956 10.9527 3.91287 10.9661 3.89516 10.9774C3.79706 11.0615 3.71762 11.1653 3.662 11.2821C3.60638 11.3989 3.57581 11.5261 3.57227 11.6556C3.57227 11.6602 3.57227 11.6646 3.57227 11.6692C3.57227 11.6739 3.57227 11.6829 3.57227 11.6896C3.57232 11.8033 3.59329 11.9161 3.63412 12.0222H3.63053C3.73394 12.3405 3.8611 12.6506 4.01092 12.9497C4.03231 12.9925 4.05413 13.0355 4.07637 13.0786C4.1467 13.0368 4.21669 12.9939 4.28634 12.9497C4.58202 12.7661 4.86984 12.5686 5.14979 12.3571C5.29199 12.2493 5.4323 12.1376 5.57074 12.0222Z" fill="#30B465"/>
|
||||
<path d="M16.4283 11.6721C16.4283 11.6695 16.4283 11.6669 16.4283 11.6643C16.4232 11.4256 16.3263 11.1982 16.158 11.0295C16.1534 11.0248 16.148 11.0205 16.1434 11.0158C16.1306 11.0037 16.1177 10.9916 16.1046 10.98L16.0438 10.9352L16.0009 10.9035C15.8235 10.7707 15.6503 10.6317 15.4814 10.4864C15.4253 10.4376 15.3694 10.3882 15.3138 10.3382L15.2715 10.2996C15.2391 10.2699 15.207 10.2403 15.1752 10.2102L15.1282 10.1669C15.0487 10.0911 14.9702 10.0138 14.8929 9.93499C14.9701 9.85615 15.0485 9.77886 15.1282 9.70311C15.1888 9.6454 15.25 9.58871 15.3118 9.53306C15.533 9.33313 15.7621 9.1447 15.9989 8.96778L16.052 8.92836C16.069 8.91599 16.0856 8.9026 16.1034 8.89126C16.2015 8.80719 16.2809 8.7034 16.3366 8.58659C16.3922 8.46978 16.4229 8.34256 16.4265 8.21313C16.4265 8.20849 16.4265 8.20411 16.4265 8.19947C16.4265 8.19484 16.4265 8.18582 16.4265 8.17912C16.4266 8.06595 16.406 7.95373 16.3657 7.84804H16.3693C16.2659 7.52971 16.1387 7.21966 15.9889 6.92051C15.9675 6.87774 15.9457 6.83522 15.9234 6.79297C15.8531 6.83471 15.7831 6.87722 15.7135 6.92051C15.4178 7.10429 15.13 7.30182 14.85 7.5131C14.707 7.62097 14.5662 7.73261 14.4278 7.84804C14.185 8.05004 13.9499 8.26285 13.7224 8.48649C13.6955 8.51226 13.6691 8.53802 13.6424 8.56559C13.591 8.61712 13.5417 8.66865 13.4917 8.72018C13.4691 8.74388 13.4463 8.76733 13.4239 8.79103C13.3649 8.85356 13.3063 8.91685 13.2481 8.98092C13.0965 9.14533 13.0091 9.35913 13.002 9.58305V9.64565C13.002 9.66472 13.004 9.68404 13.0061 9.70311H13.002V10.1669H13.0058C13.0038 10.187 13.0025 10.2073 13.002 10.2277V10.2903C13.0101 10.502 13.0896 10.7047 13.2276 10.8651C13.235 10.8736 13.2427 10.8819 13.2504 10.8909L13.2684 10.9102C13.3257 10.9734 13.3835 11.0358 13.4419 11.0975L13.4514 11.1076C13.5148 11.174 13.5784 11.2397 13.6431 11.3047C13.6688 11.3304 13.6963 11.3575 13.7232 11.3838C13.9503 11.607 14.1854 11.8199 14.4286 12.0222C14.567 12.1375 14.7077 12.2491 14.8508 12.3571C15.1304 12.5684 15.4182 12.766 15.7142 12.9497C15.7839 12.993 15.8539 13.036 15.9242 13.0786C15.9464 13.0363 15.9683 12.9934 15.9896 12.9497C16.1395 12.6506 16.2666 12.3405 16.37 12.0222H16.3664C16.4067 11.9181 16.4278 11.8075 16.4286 11.6958C16.4278 11.687 16.4283 11.6798 16.4283 11.6721Z" fill="#30B465"/>
|
||||
<path d="M9.58082 14.8058C9.52846 14.5018 9.46377 14.199 9.38677 13.8974C9.37753 13.8609 9.36803 13.8246 9.35828 13.7884C9.30814 13.5994 9.25313 13.4119 9.19324 13.2259C9.18579 13.203 9.17758 13.1801 9.16757 13.1574C9.13871 13.0877 9.10156 13.0218 9.05694 12.9611C9.05438 12.9575 9.05181 12.9538 9.04899 12.9505C8.97982 12.8586 8.89416 12.7805 8.79642 12.7202L8.79385 12.7186L8.7782 12.7096L8.76793 12.7037L8.23918 12.3971L8.23507 12.3948L8.22275 12.3875L8.21864 12.3852L8.19888 12.3739L8.19246 12.3703L8.17912 12.3638C8.16628 12.3571 8.15345 12.351 8.14036 12.345L8.11161 12.3327C8.09955 12.3278 8.08748 12.3229 8.07516 12.3185L8.04205 12.3072L8.01074 12.2976L7.97301 12.2878L7.93682 12.2798L7.9209 12.2765C7.80795 12.256 7.69216 12.2568 7.57952 12.2791C7.38342 12.321 7.18921 12.368 6.99687 12.42C6.96068 12.4295 6.92449 12.4396 6.88856 12.4496C6.40344 12.5849 5.9278 12.7523 5.46478 12.9508C5.358 12.9963 5.25225 13.0435 5.14753 13.0925C4.86741 13.2225 4.59363 13.3635 4.32617 13.5155C4.53493 13.848 4.77223 14.1616 5.03536 14.4526L5.04101 14.4588L5.04563 14.4642L5.04768 14.4665L5.05102 14.4701L5.06154 14.4817L5.06309 14.4832L5.0654 14.4858C5.09112 14.5133 5.11856 14.5391 5.14753 14.5631C5.25539 14.6523 5.38197 14.7158 5.51777 14.7489C5.65357 14.7821 5.79507 14.7839 5.93167 14.7543L5.93835 14.7525C5.95606 14.7486 5.97377 14.7442 5.99122 14.7391L6.002 14.7357C6.01766 14.7309 6.03332 14.726 6.04897 14.7203L6.12906 14.685L6.1596 14.6716C6.4315 14.5538 6.70948 14.4485 6.99354 14.3557C7.17646 14.2959 7.36186 14.2416 7.54975 14.1926C7.60108 14.3802 7.64686 14.5684 7.68707 14.7574C7.68707 14.7607 7.68835 14.7638 7.68912 14.7672C7.69195 14.7806 7.69477 14.7942 7.69734 14.8076C7.75398 15.0833 7.79855 15.3599 7.83107 15.6373C7.83594 15.6785 7.84056 15.7197 7.84467 15.7612C7.85853 15.8414 7.88305 15.9194 7.91757 15.9931C7.97682 16.1194 8.06362 16.2307 8.17155 16.3187C8.27947 16.4067 8.40575 16.4693 8.54103 16.5017V16.503C8.59929 16.5158 8.65781 16.5287 8.71634 16.5393C9.05578 16.6053 9.39981 16.6449 9.74535 16.6575C9.74156 16.2695 9.71843 15.8818 9.67604 15.4961C9.65157 15.265 9.61983 15.035 9.58082 14.8058Z" fill="#30B465"/>
|
||||
<path d="M14.5333 12.9504C14.0707 12.7517 13.5955 12.584 13.1108 12.4485C13.0749 12.4384 13.0387 12.4284 13.0025 12.4188L12.9532 12.4059C12.7773 12.359 12.5996 12.3161 12.4201 12.2771C12.3074 12.2549 12.1917 12.254 12.0787 12.2745C12.07 12.2761 12.0613 12.2784 12.053 12.2802L12.032 12.2848C12.0161 12.2885 12.0002 12.2928 11.9843 12.2972L11.9586 12.3049L11.9229 12.3171C11.9111 12.3214 11.8993 12.3261 11.8877 12.331L11.8598 12.3428C11.8456 12.3493 11.8315 12.3562 11.8177 12.3632L11.8071 12.3683L11.8018 12.3712L11.7879 12.3791H11.7866L11.7843 12.3804L11.7604 12.3943L11.2473 12.6927L11.205 12.7185L11.2024 12.72C11.1043 12.7801 11.0184 12.8583 10.9491 12.9504C10.9463 12.9537 10.9437 12.9573 10.9411 12.9609C10.8822 13.0413 10.8365 13.1308 10.8059 13.2258C10.7951 13.2593 10.7848 13.2928 10.7743 13.3263C10.7265 13.4795 10.6821 13.6335 10.6408 13.7882C10.6311 13.8246 10.6216 13.861 10.6123 13.8972C10.5352 14.1987 10.4705 14.5015 10.4183 14.8057C10.3791 15.0348 10.3468 15.2645 10.3213 15.4946C10.2789 15.8804 10.2557 16.268 10.252 16.6561C10.6571 16.6413 11.0602 16.5896 11.456 16.5015V16.5002C11.6312 16.4589 11.7904 16.367 11.9141 16.2359C12.0378 16.1047 12.1205 15.9401 12.1521 15.7623C12.1552 15.7324 12.1585 15.7028 12.1619 15.6732C12.1632 15.6606 12.1644 15.6474 12.166 15.6356C12.1709 15.5946 12.1762 15.5536 12.1816 15.5127C12.2124 15.2763 12.2518 15.0407 12.2997 14.8057C12.3033 14.7889 12.3066 14.7722 12.3102 14.7541C12.3503 14.5652 12.396 14.377 12.4475 14.1894C12.6358 14.238 12.8212 14.2924 13.0038 14.3525L13.0412 14.3651C13.2339 14.429 13.4239 14.4986 13.6113 14.5741L13.6331 14.5826L13.7163 14.6173C13.7574 14.6346 13.7987 14.6513 13.8395 14.6689C13.8759 14.6845 13.9122 14.7005 13.9483 14.7168H13.9496C13.9609 14.7209 13.9722 14.7243 13.9835 14.7279L14.0079 14.7359C14.0212 14.7397 14.0348 14.7428 14.0482 14.7462L14.0674 14.7508C14.0841 14.7547 14.1008 14.7575 14.1188 14.7601L14.1283 14.7619C14.2562 14.7809 14.3868 14.7728 14.5114 14.7381C14.6361 14.7034 14.7521 14.6429 14.8521 14.5604C14.8811 14.5364 14.9085 14.5106 14.9342 14.4831L14.9376 14.4795L14.9432 14.4733L14.9535 14.462L14.9637 14.4504C15.2264 14.1599 15.4634 13.8469 15.6719 13.5151C15.4046 13.3634 15.1308 13.2224 14.8505 13.0921C14.7458 13.0431 14.6401 12.9959 14.5333 12.9504Z" fill="#30B465"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 13 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.91211 12.6289H10.5146C11.877 12.6289 12.5801 11.9258 12.5801 10.5823V2.9421C12.5801 1.59863 11.877 0.895508 10.5146 0.895508H2.91211C1.55608 0.895508 0.84668 1.59235 0.84668 2.9421V10.5823C0.84668 11.9258 1.55608 12.6289 2.91211 12.6289ZM8.76939 8.46666C8.43666 8.46666 8.22321 8.24065 8.22321 7.87653V6.84696L8.31111 5.91155L7.44475 6.86579L5.18471 9.12584C5.05915 9.2514 4.90848 9.31417 4.72642 9.31417C4.39369 9.31417 4.14886 9.08189 4.14886 8.73033C4.14886 8.57338 4.21791 8.41643 4.33719 8.30343L6.60979 6.03083L7.57031 5.1582L6.68513 5.25865H5.59277C5.23493 5.25865 4.99637 5.0452 4.99637 4.71247C4.99637 4.37974 5.22866 4.16629 5.5865 4.16629H8.63756C9.0519 4.16629 9.30929 4.37974 9.30929 4.83803V7.86398C9.30929 8.22182 9.09584 8.46666 8.76939 8.46666Z" fill="#30B465"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 895 B |
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="52" height="52" fill="none" viewBox="0 0 52 52">
|
||||
<path fill="#70757A" d="M12.949 18.023h26.085c.882 0 1.591-.776 1.591-1.7 0-.96-.71-1.698-1.591-1.698H12.949c-.882 0-1.574.757-1.574 1.699 0 .923.692 1.699 1.574 1.699zm0 9.676h26.085c.882 0 1.591-.776 1.591-1.699 0-.96-.71-1.699-1.591-1.699H12.949c-.882 0-1.574.757-1.574 1.699 0 .923.692 1.699 1.574 1.699zm0 9.676h26.085c.882 0 1.591-.776 1.591-1.699 0-.942-.71-1.699-1.591-1.699H12.949c-.882 0-1.574.757-1.574 1.7 0 .922.692 1.698 1.574 1.698z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 556 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 3C10.5597 3 8.3102 3.20126 6.50463 3.43972C4.87965 3.65433 3.65433 4.87965 3.43972 6.50463C3.20126 8.3102 3 10.5597 3 13C3 15.4403 3.20126 17.6898 3.43972 19.4954C3.65433 21.1203 4.87965 22.3457 6.50463 22.5603C8.3102 22.7987 10.5597 23 13 23C15.4403 23 17.6898 22.7987 19.4954 22.5603C21.1203 22.3457 22.3457 21.1203 22.5603 19.4954C22.7987 17.6898 23 15.4403 23 13C23 10.5597 22.7987 8.3102 22.5603 6.50463C22.3457 4.87965 21.1203 3.65433 19.4954 3.43972C17.6898 3.20126 15.4403 3 13 3ZM6.1773 0.961241C3.4313 1.32391 1.32391 3.4313 0.961241 6.1773C0.711918 8.0651 0.5 10.4266 0.5 13C0.5 15.5734 0.711918 17.9349 0.961241 19.8227C1.32391 22.5687 3.4313 24.6761 6.1773 25.0388C8.0651 25.2881 10.4266 25.5 13 25.5C15.5734 25.5 17.9349 25.2881 19.8227 25.0388C22.5687 24.6761 24.6761 22.5687 25.0388 19.8227C25.2881 17.9349 25.5 15.5734 25.5 13C25.5 10.4266 25.2881 8.0651 25.0388 6.1773C24.6761 3.4313 22.5687 1.32391 19.8227 0.961241C17.9349 0.711918 15.5734 0.5 13 0.5C10.4266 0.5 8.0651 0.711918 6.1773 0.961241Z" fill="#B1B5C3"/>
|
||||
<path d="M9.25022 11.75C10.6309 11.75 11.7502 10.6307 11.7502 9.25C11.7502 7.86929 10.6309 6.75 9.25022 6.75C7.86951 6.75 6.75022 7.86929 6.75022 9.25C6.75022 10.6307 7.86951 11.75 9.25022 11.75Z" fill="#B1B5C3"/>
|
||||
<path d="M20.6519 13.8841L22.8948 16.127C22.8174 17.2973 22.7044 18.3832 22.5798 19.3476L18.8841 15.6519C18.3959 15.1637 17.6045 15.1637 17.1163 15.6519L14.4019 18.3663C12.9374 19.8308 10.563 19.8308 9.09856 18.3663L8.8841 18.1519C8.39594 17.6637 7.60449 17.6637 7.11633 18.1519L4.105 21.1632C3.75616 20.6902 3.52255 20.1226 3.43972 19.4954C3.39454 19.1533 3.3509 18.7954 3.30952 18.4232L5.34857 16.3841C6.81303 14.9196 9.1874 14.9197 10.6519 16.3841L10.8663 16.5986C11.3545 17.0867 12.1459 17.0867 12.6341 16.5986L15.3486 13.8841C16.813 12.4196 19.1874 12.4197 20.6519 13.8841Z" fill="#B1B5C3"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1 +0,0 @@
|
||||
<svg role="img" width="29" height="28" viewBox="0 0 29 28" fill="none" xmlns="http://www.w3.org/2000/svg" > <path d="M6.74219 20.29C6.74219 22.1885 7.71777 23.1729 9.59863 23.1729H18.959C20.8398 23.1729 21.8154 22.1885 21.8154 20.29V7.02734C21.8154 5.1377 20.8398 4.14453 18.959 4.14453H9.59863C7.71777 4.14453 6.74219 5.1377 6.74219 7.02734V20.29ZM8.46484 20.1846V7.13281C8.46484 6.31543 8.88672 5.86719 9.73926 5.86719H18.8096C19.6621 5.86719 20.0928 6.31543 20.0928 7.13281V20.1846C20.0928 21.002 19.6621 21.4502 18.8096 21.4502H9.73926C8.88672 21.4502 8.46484 21.002 8.46484 20.1846ZM10.5391 9.16309C10.9961 9.16309 11.3564 8.79395 11.3564 8.3457C11.3564 7.88867 10.9961 7.52832 10.5391 7.52832C10.0908 7.52832 9.72168 7.89746 9.72168 8.3457C9.72168 8.79395 10.0908 9.16309 10.5391 9.16309ZM12.9561 8.91699H18.2646C18.5811 8.91699 18.8359 8.66211 18.8359 8.3457C18.8359 8.0293 18.5811 7.77441 18.2646 7.77441H12.9561C12.6396 7.77441 12.3848 8.0293 12.3848 8.3457C12.3848 8.66211 12.6396 8.91699 12.9561 8.91699ZM10.5391 11.7822C10.9961 11.7822 11.3564 11.4131 11.3564 10.9648C11.3564 10.5078 10.9961 10.1562 10.5391 10.1562C10.082 10.1562 9.72168 10.5166 9.72168 10.9648C9.72168 11.4131 10.082 11.7822 10.5391 11.7822ZM12.9561 11.5361H18.2646C18.5898 11.5361 18.8359 11.2812 18.8359 10.9648C18.8359 10.6484 18.5898 10.4023 18.2646 10.4023H12.9561C12.6309 10.4023 12.3848 10.6484 12.3848 10.9648C12.3848 11.2812 12.6309 11.5361 12.9561 11.5361ZM10.5391 14.5332C10.9961 14.5332 11.3564 14.1641 11.3564 13.7158C11.3564 13.2588 10.9961 12.8896 10.5391 12.8896C10.0908 12.8896 9.72168 13.2676 9.72168 13.7158C9.72168 14.1553 10.0908 14.5332 10.5391 14.5332ZM12.9561 14.2871H18.2646C18.5811 14.2871 18.8359 14.0234 18.8359 13.7158C18.8359 13.3994 18.5898 13.1445 18.2646 13.1445H12.9561C12.6309 13.1445 12.3848 13.3994 12.3848 13.7158C12.3848 14.0234 12.6396 14.2871 12.9561 14.2871Z" fill="currentColor" /> </svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: rgba(0, 0, 0, 0) none repeat scroll 0% 0%; display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
|
||||
<circle cx="50" cy="50" fill="none" stroke="#ffffff" stroke-width="10" r="35" stroke-dasharray="164.93361431346415 56.97787143782138">
|
||||
<animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" dur="1s" values="0 50 50;360 50 50" keyTimes="0;1"></animateTransform>
|
||||
</circle>
|
||||
<!-- [ldio] generated by https://loading.io/ --></svg>
|
||||
|
Before Width: | Height: | Size: 676 B |
|
Before Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
@@ -1,12 +0,0 @@
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_5348_11251)">
|
||||
<path d="M13.75 22.5C18.5825 22.5 22.5 18.5825 22.5 13.75C22.5 8.91751 18.5825 5 13.75 5C8.91751 5 5 8.91751 5 13.75C5 18.5825 8.91751 22.5 13.75 22.5Z" fill="#0CE466"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7501 14.6959C12.7492 14.6959 11.937 13.8845 11.937 12.8828C11.937 11.8811 12.7492 11.0697 13.7501 11.0697C14.751 11.0697 15.5631 11.8811 15.5631 12.8828C15.5631 13.8845 14.751 14.6959 13.7501 14.6959ZM12.9618 12.8828C12.9618 13.3183 13.3149 13.6711 13.7501 13.6711C14.1853 13.6711 14.5384 13.3183 14.5384 12.8828C14.5384 12.4473 14.1853 12.0945 13.7501 12.0945C13.3149 12.0945 12.9618 12.4473 12.9618 12.8828Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.91675 12.8852L11.3852 9.41434H16.1149L19.5834 12.8852L13.7501 18.7161L7.91675 12.8852ZM16.4302 11.6215C14.9565 10.1413 12.5436 10.1413 11.0699 11.6215L9.80864 12.8828L11.0699 14.1441C12.5436 15.6243 14.9565 15.6243 16.4302 14.1441L17.6915 12.8828L16.4302 11.6215Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_5348_11251">
|
||||
<rect width="17.5" height="17.5" fill="white" transform="translate(5 5)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="34" height="32" fill="none" viewBox="0 0 34 32">
|
||||
<path fill="#0A0A3B" d="M15.228 21.753a7.952 7.952 0 004.37-1.306l4.431 4.41a1.52 1.52 0 001.073.421c.84 0 1.456-.653 1.456-1.476 0-.382-.131-.754-.414-1.035l-4.401-4.39a7.802 7.802 0 001.447-4.53c0-4.349-3.582-7.905-7.962-7.905-4.37 0-7.962 3.556-7.962 7.905 0 4.35 3.581 7.906 7.962 7.906zm0-2.11c-3.207 0-5.838-2.611-5.838-5.796 0-3.184 2.63-5.795 5.838-5.795 3.207 0 5.837 2.611 5.837 5.796 0 3.184-2.63 5.795-5.837 5.795z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 535 B |
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="33" height="32" fill="none" viewBox="0 0 33 32">
|
||||
<path fill="#FDFEFF" d="M15.04 21.752a7.856 7.856 0 004.339-1.306l4.4 4.41c.29.28.662.421 1.064.421.834 0 1.446-.653 1.446-1.476 0-.382-.13-.754-.411-1.035l-4.37-4.39a7.84 7.84 0 001.437-4.53c0-4.349-3.556-7.905-7.906-7.905-4.339 0-7.905 3.556-7.905 7.906 0 4.349 3.556 7.905 7.905 7.905zm0-2.11c-3.185 0-5.796-2.611-5.796-5.795 0-3.185 2.611-5.796 5.795-5.796 3.185 0 5.796 2.611 5.796 5.796 0 3.184-2.611 5.795-5.796 5.795z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 534 B |
@@ -1,11 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 364 364">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#0052ff;}.cls-2{fill:#fff;}</style>
|
||||
</defs>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<rect class="cls-1" width="364" height="364" />
|
||||
<path class="cls-2" d="M232.77,192.91a53.51,53.51,0,1,1-.08-22.16,1.38,1.38,0,0,0,1.34,1.09h51.48a1.38,1.38,0,0,0,1.37-1.52A107.12,107.12,0,0,0,178.39,74.9c-57.49,1-104.27,48-105.1,105.51a107.12,107.12,0,0,0,213.63,12.92,1.39,1.39,0,0,0-1.38-1.52H234.11A1.36,1.36,0,0,0,232.77,192.91Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 593 B |
@@ -1,14 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 364 364">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#0083c9;}.cls-2{fill:#f89c1b;}.cls-3{fill:#80c241;}.cls-4{fill:#ed1c24;}</style>
|
||||
</defs>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<rect width="364" height="364" />
|
||||
<rect class="cls-1" x="140.51" y="105.4" width="39.59" height="153.99"/>
|
||||
<rect class="cls-2" x="190.26" y="106.12" width="39.59" height="103.62"/>
|
||||
<rect class="cls-3" x="226.65" y="183.4" width="39.59" height="112.38" transform="translate(6.86 486.04) rotate(-90)"/>
|
||||
<rect class="cls-4" x="75.34" y="204.11" width="39.59" height="70.95" transform="translate(-144.45 334.72) rotate(-90)"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 743 B |
@@ -1,6 +0,0 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="32" height="32" fill="black"/>
|
||||
<path d="M8.4264 12.5C8.50338 12.3667 8.69583 12.3667 8.77281 12.5L12.7565 19.4C12.8335 19.5333 12.7373 19.7 12.5833 19.7H4.61589C4.46193 19.7 4.36571 19.5333 4.44269 19.4L8.4264 12.5Z" fill="white"/>
|
||||
<circle cx="15.9" cy="16" r="3.9" fill="white"/>
|
||||
<rect x="20.25" y="12.35" width="7.25" height="7.25" rx="0.2" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 474 B |
@@ -1,5 +0,0 @@
|
||||
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="200" height="200" fill="#04CD58"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M100 112C87.3026 112 77 101.708 77 89C77 76.2923 87.3026 66 100 66C112.697 66 123 76.2923 123 89C123 101.708 112.697 112 100 112ZM90 89C90 94.5251 94.4794 99 100 99C105.521 99 110 94.5251 110 89C110 83.4749 105.521 79 100 79C94.4794 79 90 83.4749 90 89Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M26 89.0304L70 45H130L174 89.0304L100 163L26 89.0304ZM134 72.9998C115.305 54.2224 84.6953 54.2225 66 72.9999L50 89.0001L66 105C84.6953 123.778 115.305 123.777 134 105L150 89.0001L134 72.9998Z" fill="black"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 731 B |
@@ -1,17 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 363 364">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#3d3d3d;}.cls-2{fill:#24ddd8;}</style>
|
||||
</defs>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<rect class="cls-1" width="363" height="364"/>
|
||||
<path class="cls-2" d="M218.1,115.8V102.1S187,101,177.94,115.76l-.22-13.45s-28.93-1.57-42.2,15.37c-3.53,4.51-6.09,8.14-7.88,12.7-3.86,9.81-3.27,24.88-3.27,24.88l0,66s-8.86-1.72-14.32,3a1.68,1.68,0,0,1-2,.23,1.4,1.4,0,0,1-.57-.8c-1.52-5.38-12.33-8.91-18.74-2.09-6.87,7.31-3.2,20.58,8.72,21.06,0,0-2.49,19.78,13.62,27.16,13.38,6.13,22,0,22,0s5,6.92,17.61,6.92h86.71c6.24,0,20.32-7.19,20.32-21.73V102.27S225,101.08,218.1,115.8ZM250.17,256c0,10.83-13.41,13.94-15.23,13.94H149.49a18.79,18.79,0,0,1-15.24-7.75c-5.62,4.89-13.89,3.68-13.89,3.68-16-1.09-16.76-19.32-15.67-22.93s.26-6.87-6.28-6.77-8.08-6.93-3.77-9.69,8.41-.16,8.44,3.19c0,2,2,4.43,5.19,4.53,4.23.13,6.21-5.52,12.68-5.88a19.4,19.4,0,0,1,10.77,2.4s.82-52.07.82-85.95c0-7.69,3.83-15.12,7.79-20.3a36.74,36.74,0,0,1,8-7.8,37.65,37.65,0,0,1,22.83-7.12v37.59h0c0-34.3,39.88-37.59,39.88-37.59v36.93h0c0-34.61,39.1-36.93,39.1-36.93Z"/>
|
||||
<path class="cls-2" d="M238.24,214.27v31.47a11,11,0,0,1-11,11h-1.9a.65.65,0,0,1-.65-.65v-6.49a6.8,6.8,0,0,0-5.76-6.81,6.65,6.65,0,0,0-7.52,6.59v6.29a1.06,1.06,0,0,1-1.06,1.07h-37.8a1.07,1.07,0,0,1-1.07-1.07v-6a6.82,6.82,0,0,0-6.44-6.91,6.64,6.64,0,0,0-6.84,6.64v6.71a.65.65,0,0,1-.65.65h-1.45a11,11,0,0,1-11-11V214.28a11,11,0,0,1,11-11h1.45a.65.65,0,0,1,.65.65v6.84a6.64,6.64,0,0,0,13.28,0V204.3a1.06,1.06,0,0,1,1.07-1.06h37.8a1.05,1.05,0,0,1,1.06,1.06v6.14a6.82,6.82,0,0,0,6.44,6.91,6.63,6.63,0,0,0,6.84-6.62v-6.84a.65.65,0,0,1,.65-.65h1.91A11,11,0,0,1,238.24,214.27Z"/>
|
||||
<rect class="cls-2" x="158.03" y="170.28" width="13.88" height="20.1" rx="3.72"/>
|
||||
<rect class="cls-2" x="211.11" y="170.28" width="13.88" height="20.1" rx="3.72"/>
|
||||
<path class="cls-2" d="M242.71,116.19s-29.8,2.46-25.51,37.8a92,92,0,0,1,8.33-23.15C229.06,124.34,237.32,119.4,242.71,116.19Z"/>
|
||||
<path class="cls-2" d="M202.64,116.19s-29.8,2.46-25.51,37.8a92,92,0,0,1,8.33-23.15C189,124.34,197.25,119.4,202.64,116.19Z"/>
|
||||
<path class="cls-2" d="M163.31,116.19s-29.8,2.46-25.51,37.8a92,92,0,0,1,8.33-23.15C149.65,124.34,157.92,119.4,163.31,116.19Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1,30 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 364 364.73">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#fffdfd;}.cls-2{fill:url(#linear-gradient);}.cls-3{isolation:isolate;opacity:0.5;fill:url(#linear-gradient-2);}</style>
|
||||
<linearGradient id="linear-gradient" x1="235.4" y1="269.1" x2="133.56" y2="167.26" gradientTransform="matrix(1, 0, 0, -1, 0, 364.73)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.09" stop-color="#ff6d41"/>
|
||||
<stop offset="0.5" stop-color="#fa297f"/>
|
||||
<stop offset="1" stop-color="#fa297f"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear-gradient-2" x1="207.26" y1="295.68" x2="105" y2="193.43" gradientTransform="matrix(1, 0, 0, -1, 0, 364.73)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#652cb4"/><stop offset="0.07" stop-color="#652cb4" stop-opacity="0.82"/>
|
||||
<stop offset="0.19" stop-color="#652cb4" stop-opacity="0.53"/>
|
||||
<stop offset="0.3" stop-color="#652cb4" stop-opacity="0.3"/>
|
||||
<stop offset="0.4" stop-color="#652cb4" stop-opacity="0.14"/>
|
||||
<stop offset="0.49" stop-color="#652cb4" stop-opacity="0.04"/>
|
||||
<stop offset="0.55" stop-color="#652cb4" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<g id="NFTX"><rect class="cls-1" width="364" height="364.73" />
|
||||
<polygon class="cls-2" points="182.52 38.79 73.06 148.78 182.52 254.16 291.97 148.78 182.52 38.79"/>
|
||||
<polygon class="cls-3" points="182.52 148.78 73.06 148.78 182.52 38.79 182.52 148.78"/>
|
||||
<path d="M127.44,242.37a116.42,116.42,0,0,1,7.42-9.31C102,241,79.31,257.71,79.31,277c0,.72,0,1.44.1,2.15,3.23-1.23,6.64-2.7,10.25-4.49C111.39,263.94,119.92,252.48,127.44,242.37Z"/>
|
||||
<path d="M161.7,277.86C168.28,269,174,261.38,190,253.45s25.54-7.77,36.53-7.6c7.36.11,15.3.24,25-1.85-11.81-6.37-27-11.2-44.15-13.78l-24.86,23.93-17.38-16.73c-12.39,8.38-18.73,16.9-24.49,24.63-6.58,8.85-12.26,16.49-28.32,24.41C101.07,292,93,293.61,85.45,294c6,8,16.06,15,29,20.35,3-1.17,6.16-2.56,9.5-4.2C145.65,299.43,154.17,288,161.7,277.86Z"/>
|
||||
<path d="M261.22,255c-12.57-.21-26.8-.43-48.53,10.3s-30.26,22.19-37.78,32.3C168.33,306.4,162.65,314,146.6,322c-.73.36-1.43.7-2.14,1a193,193,0,0,0,33.94,2.95c2.5,0,5-.05,7.45-.14A135,135,0,0,0,196,313.35c6.58-8.85,12.26-16.49,28.32-24.41s25.55-7.77,36.53-7.6a130.72,130.72,0,0,0,16.37-.47,23.62,23.62,0,0,0,.31-3.85c0-7.92-3.82-15.4-10.59-22C265,255,263.14,255,261.22,255Z"/>
|
||||
<path d="M218.12,321.83c26-5.61,46.1-16.56,54.75-30A97.82,97.82,0,0,0,247,300.73C232.93,307.66,224.42,314.87,218.12,321.83Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -1,5 +0,0 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="100" height="100" fill="#2081E2"/>
|
||||
<path d="M24.6679 51.6802L24.8836 51.3411L37.8906 30.9933C38.0807 30.6954 38.5276 30.7262 38.6714 31.0498C40.8444 35.9197 42.7194 41.9763 41.841 45.7469C41.466 47.2983 40.4386 49.3993 39.2827 51.3411C39.1338 51.6237 38.9694 51.9011 38.7947 52.1682C38.7125 52.2915 38.5738 52.3634 38.4248 52.3634H25.048C24.6884 52.3634 24.4778 51.973 24.6679 51.6802Z" fill="white"/>
|
||||
<path d="M82.6444 55.461V58.6819C82.6444 58.8668 82.5314 59.0312 82.367 59.1031C81.3602 59.5346 77.9132 61.1168 76.48 63.11C72.8224 68.2008 70.0279 75.48 63.7812 75.48H37.721C28.4847 75.48 21 67.9697 21 58.7024V58.4045C21 58.1579 21.2003 57.9576 21.4469 57.9576H35.9745C36.2621 57.9576 36.4727 58.2247 36.4471 58.5072C36.3443 59.4524 36.519 60.4182 36.9659 61.2966C37.8289 63.0484 39.6166 64.1426 41.5481 64.1426H48.74V58.5278H41.6303C41.2656 58.5278 41.0499 58.1065 41.2605 57.8086C41.3375 57.6904 41.4249 57.5672 41.5173 57.4285C42.1903 56.473 43.1509 54.9884 44.1064 53.2983C44.7588 52.1579 45.3906 50.9404 45.8992 49.7178C46.002 49.4969 46.0841 49.2708 46.1663 49.0499C46.305 48.6595 46.4489 48.2948 46.5516 47.9301C46.6544 47.6218 46.7365 47.2982 46.8187 46.9951C47.0602 45.9574 47.1629 44.8581 47.1629 43.7177C47.1629 43.2708 47.1424 42.8033 47.1013 42.3564C47.0807 41.8684 47.0191 41.3803 46.9574 40.8923C46.9163 40.4608 46.8393 40.0344 46.7571 39.5875C46.6544 38.9351 46.5105 38.2879 46.3461 37.6354L46.2896 37.3889C46.1663 36.9419 46.0636 36.5156 45.9198 36.0687C45.5139 34.6662 45.0465 33.2998 44.5533 32.0207C44.3735 31.5121 44.168 31.0241 43.9625 30.5361C43.6595 29.8015 43.3512 29.1337 43.0687 28.5018C42.9249 28.2141 42.8016 27.9521 42.6783 27.685C42.5396 27.3819 42.3958 27.0788 42.2519 26.7912C42.1492 26.5703 42.031 26.3648 41.9488 26.1593L41.0704 24.536C40.9471 24.3151 41.1526 24.0531 41.394 24.1199L46.8907 25.6096H46.9061C46.9163 25.6096 46.9215 25.6148 46.9266 25.6148L47.6509 25.8151L48.4472 26.0412L48.74 26.1233V22.8562C48.74 21.2791 50.0037 20 51.5654 20C52.3462 20 53.0551 20.3185 53.5637 20.8373C54.0722 21.3562 54.3907 22.0651 54.3907 22.8562V27.7056L54.9764 27.8699C55.0226 27.8854 55.0688 27.9059 55.1099 27.9367C55.2538 28.0446 55.4592 28.2038 55.7212 28.3991C55.9267 28.5634 56.1476 28.7638 56.4147 28.9693C56.9438 29.3956 57.5757 29.9453 58.2692 30.5772C58.4541 30.7364 58.6339 30.9008 58.7983 31.0652C59.6922 31.8974 60.6939 32.8734 61.6494 33.9522C61.9165 34.2553 62.1785 34.5635 62.4456 34.8871C62.7127 35.2159 62.9953 35.5395 63.2418 35.8632C63.5655 36.2947 63.9148 36.7416 64.2179 37.2091C64.3617 37.43 64.5261 37.656 64.6648 37.8769C65.0552 38.4676 65.3994 39.079 65.7282 39.6903C65.8669 39.9728 66.0107 40.281 66.134 40.5841C66.4987 41.4009 66.7864 42.2331 66.9713 43.0653C67.0278 43.2451 67.0689 43.4403 67.0895 43.615V43.6561C67.1511 43.9026 67.1717 44.1646 67.1922 44.4317C67.2744 45.2845 67.2333 46.1372 67.0484 46.9951C66.9713 47.3599 66.8686 47.704 66.7453 48.0688C66.622 48.4181 66.4987 48.7828 66.3395 49.127C66.0313 49.841 65.6665 50.5551 65.235 51.2229C65.0963 51.4695 64.9319 51.7315 64.7675 51.9781C64.5877 52.24 64.4028 52.4866 64.2384 52.7281C64.0124 53.0363 63.771 53.3599 63.5244 53.6476C63.3035 53.9507 63.0775 54.2538 62.8309 54.5209C62.4867 54.9267 62.1579 55.312 61.8137 55.6819C61.6083 55.9233 61.3874 56.1699 61.1613 56.3908C60.9405 56.6373 60.7144 56.8582 60.5089 57.0637C60.1648 57.4079 59.8771 57.675 59.6356 57.8959L59.0706 58.4148C58.9884 58.4867 58.8805 58.5278 58.7675 58.5278H54.3907V64.1426H59.8976C61.1305 64.1426 62.3018 63.7059 63.247 62.9045C63.5706 62.622 64.9833 61.3994 66.6528 59.5552C66.7093 59.4935 66.7813 59.4473 66.8635 59.4268L82.0742 55.0295C82.3568 54.9473 82.6444 55.163 82.6444 55.461Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.7 KiB |
@@ -1,12 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 363 364">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#fcd903;}.cls-2{fill:#020100;}</style>
|
||||
</defs>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<path class="cls-1" d="M285.85,364H77.15A77.15,77.15,0,0,1,0,286.85V77.15A77.15,77.15,0,0,1,77.15,0h208.7A77.15,77.15,0,0,1,363,77.15v209.7A77.15,77.15,0,0,1,285.85,364ZM74.09,107.52V267.34H137.6V213.41c26.76,0,53-.21,79.21.11,9.71.12,14.67,5.83,14.84,15.73.18,10.13,0,20.27,0,30.4v7.72h63.37c0-15.84.82-31.47-.24-47-1-14.64-9.32-25.25-22.75-31.56-1.29-.61-2.62-1.12-4-1.71.27-.36.35-.54.48-.61a8.62,8.62,0,0,1,1.32-.67c15-5.49,22.36-16.53,23.09-32.17.77-16.55-4.49-30-20-38.08-12.45-6.44-26-8.1-39.64-8.17-52.49-.26-105-.18-157.48-.21A11.44,11.44,0,0,0,74.09,107.52Z"/>
|
||||
<path class="cls-2" d="M74.09,107.52a11.44,11.44,0,0,1,1.73-.3c52.5,0,105,0,157.48.21,13.65.07,27.19,1.73,39.64,8.17,15.55,8,20.81,21.53,20,38.08-.73,15.64-8.11,26.68-23.09,32.17a8.62,8.62,0,0,0-1.32.67c-.13.07-.21.25-.48.61,1.37.59,2.7,1.1,4,1.71,13.43,6.31,21.75,16.92,22.75,31.56,1.06,15.5.24,31.13.24,47H231.7v-7.72c0-10.13.13-20.27,0-30.4-.17-9.9-5.13-15.61-14.84-15.73-26.22-.32-52.45-.11-79.21-.11v53.93H74.09Zm63.65,62.39c23.83,0,47.39,0,70.95,0,3.66,0,7.32,0,11-.28,5.58-.49,6.71-2.27,7.14-8.08.41-5.37-2.86-6.48-6.93-7a49.5,49.5,0,0,0-6-.25q-35.89,0-71.78,0c-1.44,0-2.87.13-4.37.2Z"/>
|
||||
<path class="cls-1" d="M137.74,169.91v-15.4c1.5-.07,2.93-.2,4.37-.2q35.89,0,71.78,0a49.5,49.5,0,0,1,6,.25c4.07.5,7.34,1.61,6.93,7-.43,5.81-1.56,7.59-7.14,8.08-3.64.32-7.3.28-11,.28C185.13,169.92,161.57,169.91,137.74,169.91Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,6 +0,0 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 14.4C0 6.4471 6.4471 0 14.4 0H33.6C41.5529 0 48 6.4471 48 14.4V33.6C48 41.5529 41.5529 48 33.6 48H14.4C6.4471 48 0 41.5529 0 33.6V14.4Z" fill="#B9B9FF"/>
|
||||
<path d="M9.29999 15H12L15.3 24L12 33H9.29999L12.6 24L9.29999 15Z" fill="#121212"/>
|
||||
<path d="M23.5727 33.192C22.1807 33.192 21.0367 32.872 20.1407 32.232C19.2607 31.592 18.8207 30.72 18.8207 29.616H21.2447C21.2447 30.112 21.4607 30.496 21.8927 30.768C22.3407 31.024 22.9167 31.152 23.6207 31.152H24.6287C25.4607 31.152 26.0767 30.992 26.4767 30.672C26.8767 30.336 27.0767 29.896 27.0767 29.352C27.0767 28.808 26.8847 28.384 26.5007 28.08C26.1167 27.776 25.5727 27.56 24.8687 27.432L23.0687 27.168C20.4447 26.752 19.1327 25.48 19.1327 23.352C19.1327 22.136 19.5327 21.208 20.3327 20.568C21.1327 19.928 22.2767 19.608 23.7647 19.608H24.6767C26.0527 19.608 27.1567 19.92 27.9887 20.544C28.8207 21.152 29.2367 21.96 29.2367 22.968H26.8127C26.8127 22.568 26.6127 22.248 26.2127 22.008C25.8287 21.768 25.3007 21.648 24.6287 21.648H23.7167C22.9807 21.648 22.4207 21.8 22.0367 22.104C21.6527 22.392 21.4607 22.808 21.4607 23.352C21.4607 24.28 22.1167 24.848 23.4287 25.056L25.2287 25.344C26.6687 25.568 27.7247 25.992 28.3967 26.616C29.0687 27.224 29.4047 28.104 29.4047 29.256C29.4047 30.488 28.9967 31.456 28.1807 32.16C27.3807 32.848 26.1807 33.192 24.5807 33.192H23.5727Z" fill="#121212"/>
|
||||
<path d="M38.7 15H36L32.7 24L36 33H38.7L35.4 24L38.7 15Z" fill="#121212"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,34 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 364 364">
|
||||
<defs>
|
||||
<style>.cls-1{fill:#fff;}.cls-2{fill:#0d0f23;}.cls-3{fill:#2f2f2f;}.cls-4{fill:url(#linear-gradient);}.cls-5{fill:url(#linear-gradient-2);}</style>
|
||||
<linearGradient id="linear-gradient" x1="177.18" y1="64.07" x2="177.18" y2="292.45" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#1bace4"/>
|
||||
<stop offset="0.7" stop-color="#fa52a0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="linear-gradient-2" x1="164.69" y1="109.21" x2="242.53" y2="187.05" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#8f82c4"/>
|
||||
<stop offset="1" stop-color="#e65ba7"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<g id="Layer_1-2" data-name="Layer 1">
|
||||
<rect class="cls-1" width="364" height="364" />
|
||||
<path class="cls-2" d="M306.26,190.34h0a58.84,58.84,0,0,0-2.07-7.38c-1-2.39-10.24-2.92-11.31-3q.81,2.07,1.5,4.2c2.1,6.46,3.42,13.7,2.81,20.39-1,10.66-9.86,16.75-23.12,17.78-1.82.14-3.65,0-5.48,0a101.74,101.74,0,0,1-16.07-1.56l-1.17-.21c-1-.18-1.72-.32-2.06-.4l-.13,0-.11,0a80.19,80.19,0,0,1-5.48,7.6l.22,0c.76,0,2.81.49,2.3,2.4-.56,2.08-3.55,1.64-5.55,1.17-6.58,7.34-16.39,15.73-27.36,16.53-19.84,1.44-30-20.5-47-39.29S122.52,218.3,112,228.86s-20.79,9.39-20.79,9.39.38.39,1.12,1.08l0,0a249,249,0,0,0,42.51,33.08c7.21,4.5,14.53,8.74,22,12.53,5.1,2.45,10.56,4.92,16.42,7.4l.18.08.13.05A135.84,135.84,0,0,0,215.92,302c15.06.83,28.52-2.82,37.92-16.29l1.1-1.57,0-.05.84-1.19c13.11-18.76,26.36-37.44,40-55.84l.74-1C307.51,211.05,307.64,198.11,306.26,190.34Zm-30.21,43.22c-1.38.18-12.9,1.8-23.57-.17,0,0-2.84,0-2.84-2.5,0,0,.22-2.36,4.14-1.36s16.31,1.41,21.63.16c0,0,2.19-.72,2.89,1S277.43,233.39,276.05,233.56Z"/>
|
||||
<path class="cls-3" d="M255.81,282.93c13.11-18.76,26.36-37.44,40-55.84C280.76,247.75,260.19,276.75,255.81,282.93Z"/>
|
||||
<path class="cls-1" d="M249.64,230.89s.22-2.36,4.14-1.36,16.31,1.41,21.63.16c0,0,2.19-.72,2.89,1s-.87,2.67-2.25,2.84-12.9,1.8-23.57-.17C252.48,233.39,249.64,233.34,249.64,230.89Z"/>
|
||||
<path class="cls-3" d="M252.52,220.8c.78.14,1.57.25,2.36.37C254,221.05,253.22,220.92,252.52,220.8Z"/>
|
||||
<path class="cls-3" d="M306.26,190.33a58.84,58.84,0,0,0-2.07-7.38.3.3,0,0,1,0,.08A44.32,44.32,0,0,1,306.26,190.33Z"/>
|
||||
<path class="cls-4" d="M173.32,292.37c-5.86-2.48-11.32-4.95-16.42-7.4l2,1,2.05,1,1,.49q.76.38,1.53.72,1.16.54,2.31,1.05c2.52,1.13,5.06,2.19,7.63,3.2Zm76.49-72.08-.38-.07,1.22.24Zm53.89-38.72c-6.17-16.66-16.37-30.79-28.17-43.74a252.85,252.85,0,0,0-83.44-60.35c-16.76-7.58-34.22-12.78-49.8-13.41-19.83,0-29.84,4.43-38.43,16.31q-24.23,33.54-48.19,67.27c-4.61,6.48-6.07,13.87-5.31,21.77,1.46,15.06,8.21,28,16.64,40.05.56.81,1.13,1.61,1.71,2.41,1.35,1.89,2.73,3.74,4.15,5.57.5.65,1,1.3,1.51,1.94,1.69,2.15,3.43,4.25,5.2,6.31.63.74,1.26,1.47,1.91,2.2.35.41.71.81,1.08,1.21s.87,1,1.32,1.47c.6.65,1.19,1.3,1.79,1.94,1.07,1.14,2.14,2.26,3.23,3.37l1.71,1.73c.58.58.06.09.63.65.61,0,.93,0,.93,0s9.33,1.13,19.84-9.42,37.05-39.07,54.13-20.29,27.2,40.73,47,39.29c11-.8,20.78-9.19,27.36-16.53l-.72-.18c-1.91-.5-5-1.84-3.91-4s4-.33,4-.33c.4.17.79.3,1.16.42l.54.15.5.12c.32.07.61.12.86.16l.28,0,.35,0a80.19,80.19,0,0,0,5.48-7.6c-1.86-.39-3.72-.83-5.56-1.3-.93-.24-1.86-.49-2.78-.76s-2-.58-3-.89-2-.62-3-1-2-.67-3-1q-3-1-5.87-2.24l-2.28-.94c-1.19-.5-2.36-1-3.54-1.53a243.23,243.23,0,0,1-78.8-55.73c-12.58-13.35-23.67-27.87-29.42-45.7a43.18,43.18,0,0,1-2-8.71c-1.7-16.83,7.61-26.45,26.48-27.06,13.66-.45,26.76,2.92,39.4,7.6,36.7,13.6,67.77,35.63,94.09,64.38.66.72,1.31,1.43,1.95,2.16.52.58,1,1.17,1.55,1.76.37.43.74.85,1.1,1.28s.77.9,1.14,1.36c.56.66,1.11,1.33,1.65,2l1.41,1.79c.32.4.63.81.94,1.22s.78,1,1.16,1.56l.31.42c.48.65.95,1.3,1.4,2l.22.32c.44.63.88,1.26,1.3,1.91q1.47,2.2,2.82,4.48c.16.27.33.55.48.83s.2.33.29.5c.29.51.58,1,.86,1.54l.26.47c.35.65.69,1.3,1,2s.63,1.27.94,1.91l.33.7c.32.71.64,1.42.95,2.14s.58,1.38.86,2.08c.05.12.1.23.14.35,1.07.06,10.33.59,11.31,3C304,182.49,303.87,182,303.7,181.57ZM99,102.88a2.27,2.27,0,0,1,2.17-2.1,2.11,2.11,0,0,1,1.76,2.79,4.47,4.47,0,0,0,.07,2.28c.11.38.29,2.87-1.83,2.87C98.46,108.72,98.89,104.88,99,102.88Zm20.73,40.6c12.15,18.19,35.9,37.84,36.79,38.66s3.51,2,2,3.94-4.56-.5-4.56-.5c-6.44-4.67-24.83-23.05-36.73-38.23S101.1,117,100.53,114.44a8,8,0,0,1-.14-1,2.07,2.07,0,0,1,2.88-2.15,2.36,2.36,0,0,1,1.18,1.63C105.09,115.88,107.61,125.28,119.76,143.48Z"/>
|
||||
<path class="cls-1" d="M246.09,230.16c-.63,2.35-4.37,1.48-6.27,1s-5-1.84-3.91-4,4-.33,4-.33a13,13,0,0,0,3.91.95C244.55,227.76,246.6,228.25,246.09,230.16Z"/>
|
||||
<path class="cls-1" d="M276.05,233.56c-1.38.18-12.9,1.8-23.57-.17,0,0-2.84,0-2.84-2.5,0,0,.22-2.36,4.14-1.36s16.31,1.41,21.63.16c0,0,2.19-.72,2.89,1S277.43,233.39,276.05,233.56Z"/>
|
||||
<path class="cls-1" d="M101.2,108.72c-2.74,0-2.31-3.84-2.17-5.84a2.27,2.27,0,0,1,2.17-2.1,2.11,2.11,0,0,1,1.76,2.79,4.47,4.47,0,0,0,.07,2.28C103.14,106.23,103.32,108.72,101.2,108.72Z"/>
|
||||
<path class="cls-1" d="M158.55,186.08c-1.51,2-4.56-.5-4.56-.5-6.44-4.67-24.83-23.05-36.73-38.23S101.1,117,100.53,114.44a8,8,0,0,1-.14-1,2.07,2.07,0,0,1,2.88-2.15,2.36,2.36,0,0,1,1.18,1.63c.64,2.92,3.16,12.32,15.31,30.52s35.9,37.84,36.79,38.66S160.06,184.11,158.55,186.08Z"/>
|
||||
<path class="cls-1" d="M294.38,184.17q-.69-2.13-1.5-4.2c0-.12-.09-.23-.14-.35-.28-.7-.56-1.4-.86-2.08s-.63-1.43-.95-2.14l-.33-.7c-.31-.64-.62-1.28-.94-1.91s-.67-1.31-1-2l-.26-.47c-.28-.52-.57-1-.86-1.54-.09-.17-.19-.34-.29-.5l-.48-.83q-1.35-2.28-2.82-4.48c-.42-.64-.85-1.28-1.3-1.91l-.22-.32c-.46-.66-.92-1.31-1.4-2l-.31-.42c-.38-.53-.77-1-1.16-1.56s-.62-.82-.94-1.22l-1.41-1.79c-.55-.67-1.09-1.34-1.65-2-.37-.46-.76-.91-1.14-1.36s-.73-.85-1.1-1.28c-.51-.59-1-1.18-1.55-1.76q-1-1.1-1.95-2.16c-26.32-28.75-57.39-50.78-94.09-64.38-12.64-4.68-25.74-8-39.4-7.6-18.87.61-28.18,10.23-26.48,27.06a43.18,43.18,0,0,0,2,8.71c5.75,17.83,16.84,32.35,29.42,45.7a243.23,243.23,0,0,0,78.8,55.73c1.18.52,2.35,1,3.54,1.53l2.28.94c1.95.79,3.9,1.53,5.87,2.24,1,.35,2,.7,3,1s2,.65,3,1,2,.61,3,.89,1.85.52,2.78.76c1.84.47,3.7.91,5.56,1.3l.11,0,.13,0,.14,0,.38.07.13,0,.71.14.33.06a102.69,102.69,0,0,0,17.61,1.84c1.83,0,3.66.12,5.48,0,13.26-1,22.16-7.12,23.12-17.78C297.8,197.87,296.48,190.63,294.38,184.17ZM247,182.45a11.09,11.09,0,0,1-1,.65,5.7,5.7,0,0,1-.54.3,12.23,12.23,0,0,1-1.15.55l-.62.24c-.42.16-.86.3-1.31.43l-.71.19c-.47.12-1,.22-1.48.31l-.78.13-.81.11-1.27.13q-.65.06-1.32.09l-.15,0A70.87,70.87,0,0,1,220.6,182c-23.73-8.6-43.49-22.66-57.57-43.88-2.77-4.18-4.52-9.23-5.9-14.11-.08-.3-.16-.59-.21-.89s-.07-.36-.1-.54a10.55,10.55,0,0,1-.12-1.56c0-.17,0-.33,0-.49a10,10,0,0,1,.16-1.35,3.12,3.12,0,0,1,.09-.44l.12-.42a2.58,2.58,0,0,1,.13-.42c0-.14.1-.27.16-.4s.11-.27.17-.4a2.4,2.4,0,0,1,.2-.39c.06-.13.14-.25.21-.38a3.17,3.17,0,0,1,.23-.36c.08-.12.16-.24.25-.35a3,3,0,0,1,.27-.35c.09-.11.19-.22.29-.32a1.79,1.79,0,0,1,.3-.32c.16-.16.33-.32.51-.47s.36-.29.55-.43.38-.27.59-.4l.63-.36.67-.32c.22-.1.46-.19.7-.28s.49-.17.74-.24l.52-.14.26-.06c.66-.15,1.32-.27,2-.38s1.35-.19,2-.25a35.2,35.2,0,0,1,4.11-.12l1.16.07q.66,0,1.32.12c.39,0,.78.1,1.17.16.65.1,1.31.22,1.95.34l1,.19,1.23.26c.19,0,.39.08.57.12,1.43.32,2.84.67,4.23,1.07l.16,0c.53.15,1.06.3,1.58.47.33.09.66.19,1,.3,1.75.55,3.48,1.16,5.17,1.83.5.2,1,.39,1.47.6s.88.36,1.32.55c.62.26,1.23.54,1.85.82l1.75.83,1.32.66c.51.25,1,.51,1.52.78,1,.51,1.92,1,2.87,1.58,1.53.87,3,1.78,4.52,2.73,1,.61,1.91,1.24,2.86,1.89h0c.94.64,1.88,1.3,2.81,2s1.62,1.2,2.43,1.81q3.61,2.76,7.08,5.77l1.18,1,1.17,1.07,1.53,1.45c1,1,2.09,2.06,3.1,3.12l1,1c.91,1,1.8,1.94,2.67,2.92.53.59,1.06,1.18,1.58,1.78l1.56,1.8c.52.6,1,1.21,1.54,1.81l2,2.43.29.36c.17.19.31.38.45.57s.38.49.55.74.47.66.69,1,.52.79.75,1.2c.14.22.28.45.4.68s.21.35.31.53.31.56.46.85c.38.72.75,1.44,1.09,2.18.57,1.17,1.08,2.36,1.57,3.55C251.92,173.82,251.21,179.29,247,182.45Z"/>
|
||||
<path class="cls-5" d="M247,182.45a11.09,11.09,0,0,1-1,.65,5.7,5.7,0,0,1-.54.3,12.23,12.23,0,0,1-1.15.55l-.62.24c-.42.16-.86.3-1.31.43l-.71.19c-.47.11-1,.22-1.48.31l-.78.13-.81.11-1.27.13-1.32.08h-.15A70.87,70.87,0,0,1,220.6,182c-23.73-8.6-43.49-22.66-57.57-43.88-2.77-4.18-4.52-9.23-5.9-14.11-.08-.3-.16-.59-.21-.89s-.07-.36-.1-.54a10.55,10.55,0,0,1-.12-1.56c0-.17,0-.33,0-.49a10,10,0,0,1,.16-1.35,3.12,3.12,0,0,1,.09-.44l.12-.42a2.58,2.58,0,0,1,.13-.42c0-.14.1-.27.16-.4s.11-.27.17-.4a2.4,2.4,0,0,1,.2-.39c.06-.13.14-.25.21-.38a3.17,3.17,0,0,1,.23-.36c.08-.12.16-.24.25-.35a3,3,0,0,1,.27-.35c.09-.11.19-.22.29-.32a1.79,1.79,0,0,1,.3-.32c.16-.16.33-.32.51-.47s.36-.29.55-.43.38-.27.59-.4l.63-.36.67-.32c.22-.1.46-.19.7-.28s.49-.17.74-.24l.52-.14.26-.06c.66-.15,1.32-.27,2-.38s1.35-.19,2-.25a35.2,35.2,0,0,1,4.11-.12l1.16.07q.66,0,1.32.12c.39,0,.78.1,1.17.16.65.1,1.31.22,1.95.34l1,.19,1.23.26c.19,0,.39.08.57.12,1.43.32,2.84.67,4.23,1.07l.16,0c.53.15,1.06.3,1.58.47.33.09.66.19,1,.3,1.75.55,3.48,1.16,5.17,1.83.5.2,1,.39,1.47.6s.88.36,1.32.55c.62.26,1.23.54,1.85.82l1.75.83,1.32.66c.51.25,1,.51,1.52.78,1,.51,1.92,1,2.87,1.58,1.53.87,3,1.78,4.52,2.73,1,.61,1.91,1.24,2.86,1.89h0c.94.64,1.88,1.3,2.81,2s1.62,1.2,2.43,1.81q3.61,2.76,7.08,5.77l1.18,1,1.17,1.07,1.53,1.45c1,1,2.09,2.06,3.1,3.12l1,1c.91,1,1.8,1.94,2.67,2.92.53.59,1.06,1.18,1.58,1.78l1.56,1.8c.52.6,1,1.21,1.54,1.81l2,2.43.29.36c.17.19.31.38.45.57s.37.49.55.74.47.66.69,1,.52.79.75,1.2c.14.22.28.45.4.68s.21.35.31.53.31.56.46.85c.38.72.75,1.44,1.09,2.18.57,1.17,1.08,2.36,1.57,3.55C251.92,173.82,251.21,179.29,247,182.45Z"/>
|
||||
<path d="M156.87,119.18a10,10,0,0,0-.16,1.35A6.73,6.73,0,0,1,156.87,119.18Z"/>
|
||||
<path d="M166.45,111.55c-.67.11-1.33.23-2,.38l-.26.06.26-.07C165.12,111.78,165.78,111.65,166.45,111.55Z"/>
|
||||
<path d="M226.1,137.19l-1.18-1q-3.46-3-7.08-5.77c2.41,1.83,4.77,3.76,7.08,5.76C225.32,136.49,225.71,136.84,226.1,137.19Z"/>
|
||||
<path d="M235.87,185.56A55.12,55.12,0,0,1,220.6,182,70.87,70.87,0,0,0,235.87,185.56Z"/><path d="M242.53,154.94l-.29-.36-2-2.43q1,1.22,2,2.43A4.39,4.39,0,0,1,242.53,154.94Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 9.7 KiB |