diff --git a/package.json b/package.json
index 14cb4beac..11ba8d0f2 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
- "@soramitsu/soramitsu-js-ui": "^0.5.3",
+ "@soramitsu/soramitsu-js-ui": "^0.5.4",
"axios": "^0.19.2",
"core-js": "^3.6.4",
"lodash": "^4.17.15",
diff --git a/src/assets/img/ksm.svg b/src/assets/img/ksm.svg
new file mode 100644
index 000000000..c6607f8cf
--- /dev/null
+++ b/src/assets/img/ksm.svg
@@ -0,0 +1,9 @@
+
diff --git a/src/assets/img/token-logo-default.svg b/src/assets/img/token-logo-default.svg
new file mode 100644
index 000000000..1f1439c70
--- /dev/null
+++ b/src/assets/img/token-logo-default.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/img/xor.svg b/src/assets/img/xor.svg
new file mode 100644
index 000000000..a666dda19
--- /dev/null
+++ b/src/assets/img/xor.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/components/ConfirmSwap.vue b/src/components/ConfirmSwap.vue
new file mode 100644
index 000000000..eb8fe4133
--- /dev/null
+++ b/src/components/ConfirmSwap.vue
@@ -0,0 +1,197 @@
+
+
+
+
+ {{ formattedFromValue }}
+
+ {{ formattedToValue }}
+
+
+
+
+ {{ tokenFrom ? tokenFrom.symbol : '' }}
+
+
+
+ {{ tokenTo ? tokenTo.symbol : '' }}
+
+
+
+
+
+
+
+
+ {{ t('swap.confirmSwap') }}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/Swap.vue b/src/components/Swap.vue
index 3c2222a7d..f8848b006 100644
--- a/src/components/Swap.vue
+++ b/src/components/Swap.vue
@@ -1,6 +1,6 @@
@@ -9,26 +9,27 @@
{{ t('exchange.from') }}
{{ t('exchange.balance') }}
- {{ formatNumber(tokenFrom.balance, 2) }}
+ {{ getTokenBalance(tokenFrom) }}
-
+
-
-
- {{ t('exchange.price') }}
- {{ priceValue }}
-
-
-
- {{ t('swap.slippageTolerance') }}
- {{ slippageToleranceValue }}%
-
-
+
{{ t('swap.connectWallet') }}
-
-
+
+
{{ t('swap.chooseTokens') }}
{{ t('swap.enterAmount') }}
- {{ t('swap.insufficientBalance') }}
+ {{ t('swap.insufficientBalance', { tokenSymbol: tokenFrom.symbol }) }}
{{ t('exchange.swap') }}
-
-
-
-
-
- {{ t('swap.minReceived') }}
- {{ minReceived }}
-
-
-
-
-
- {{ t('swap.priceImpact') }}
- {{ priceImpact }}%
-
-
-
-
-
- {{ t('swap.liquidityProviderFee') }}
- {{ liquidityProviderFee }}
-
-
+
+
+
+
diff --git a/src/components/TransactionSubmit.vue b/src/components/TransactionSubmit.vue
new file mode 100644
index 000000000..7acff30cf
--- /dev/null
+++ b/src/components/TransactionSubmit.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
{{ t('exchange.swap') }}
+
{{ transactionInfo }}
+
+
+
+
+
+
+ {{ t('exchange.ok') }}
+
+
+
+
+
+
+
diff --git a/src/lang/en/index.ts b/src/lang/en/index.ts
index d6f3631eb..1df96da6a 100644
--- a/src/lang/en/index.ts
+++ b/src/lang/en/index.ts
@@ -38,7 +38,9 @@ export default {
to: 'To',
balance: 'Balance',
max: 'MAX',
- price: 'Price'
+ price: 'Price',
+ transactionSubmitted: 'Transaction submitted',
+ ok: 'OK'
},
swap: {
chooseToken: 'Choose token',
@@ -51,9 +53,12 @@ export default {
priceImpact: 'Price Impact',
priceImpactTooltip: 'The difference between the market price and estimated price due to trade size.',
liquidityProviderFee: 'Liquidity Provider Fee',
- liquidityProviderFeeTooltip: 'A portion of each trade (0.3%) goes to liquidity providers as a protocol incentive.',
+ liquidityProviderFeeTooltip: 'A portion of each trade ({liquidityProviderFee}%) goes to liquidity providers as a protocol incentive.',
enterAmount: 'Enter an amount',
- insufficientBalance: 'Insufficient XOR balance'
+ insufficientBalance: 'Insufficient {tokenSymbol} balance',
+ confirmSwap: 'Confirm Swap',
+ swapOutputMessage: 'Output is estimated. You will receive at least {transactionValue} or the transaction will revert.',
+ transactionMessage: '{tokenFromValue} for {tokenToValue}'
},
selectToken: {
title: 'Select a token',
diff --git a/src/mocks/swap.ts b/src/mocks/swap.ts
new file mode 100644
index 000000000..73e27ea7c
--- /dev/null
+++ b/src/mocks/swap.ts
@@ -0,0 +1,2 @@
+export const slippageTolerance = 0.5
+export const liquidityProviderFee = 0.3
diff --git a/src/store/swap.ts b/src/store/swap.ts
new file mode 100644
index 000000000..7afac61a7
--- /dev/null
+++ b/src/store/swap.ts
@@ -0,0 +1,125 @@
+import map from 'lodash/fp/map'
+import flatMap from 'lodash/fp/flatMap'
+import fromPairs from 'lodash/fp/fromPairs'
+import flow from 'lodash/fp/flow'
+import concat from 'lodash/fp/concat'
+
+import * as storage from '@/utils/storage'
+
+const types = flow(
+ flatMap(x => [x + '_REQUEST', x + '_SUCCESS', x + '_FAILURE']),
+ concat([
+ 'GET_WALLET_CONNECTED',
+ 'GET_TOKEN_FROM',
+ 'GET_TOKEN_TO',
+ 'GET_FROM_VALUE',
+ 'GET_TO_VALUE',
+ 'GET_TOKEN_FROM_PRICE',
+ 'GET_SWAP_CONFIRM'
+ ]),
+ map(x => [x, x]),
+ fromPairs
+)([])
+
+function initialState () {
+ return {
+ isWalletConnected: false,
+ tokenFrom: null,
+ tokenTo: null,
+ fromValue: 0,
+ toValue: 0,
+ isTokenFromPrice: true,
+ slippageTolerance: 0.5,
+ liquidityProviderFee: 0.3,
+ isSwapConfirmed: false
+ }
+}
+
+const state = initialState()
+
+const getters = {
+ isWalletConnected (state) {
+ // TODO: Add Connect Wallet functionality
+ return !!storage.getItem('address')
+ },
+ tokenFrom (state) {
+ return state.tokenFrom
+ },
+ tokenTo (state) {
+ return state.tokenTo
+ },
+ fromValue (state) {
+ return state.fromValue
+ },
+ toValue (state) {
+ return state.toValue
+ },
+ isTokenFromPrice (state) {
+ return state.isTokenFromPrice
+ },
+ slippageTolerance (state) {
+ return state.slippageTolerance
+ },
+ liquidityProviderFee (state) {
+ return state.liquidityProviderFee
+ },
+ isSwapConfirmed (state) {
+ return state.isSwapConfirmed
+ }
+}
+
+const mutations = {
+ [types.GET_WALLET_CONNECTED] (state, isWalletConnected: boolean) {
+ state.isWalletConnected = isWalletConnected
+ },
+ [types.GET_TOKEN_FROM] (state, tokenFrom: any) {
+ state.tokenFrom = tokenFrom
+ },
+ [types.GET_TOKEN_TO] (state, tokenTo: any) {
+ state.tokenTo = tokenTo
+ },
+ [types.GET_FROM_VALUE] (state, fromValue: string | number) {
+ state.fromValue = fromValue
+ },
+ [types.GET_TO_VALUE] (state, toValue: string | number) {
+ state.toValue = toValue
+ },
+ [types.GET_TOKEN_FROM_PRICE] (state, isTokenFromPrice: boolean) {
+ state.isTokenFromPrice = isTokenFromPrice
+ },
+ [types.GET_SWAP_CONFIRM] (state, isSwapConfirmed: boolean) {
+ state.isSwapConfirmed = isSwapConfirmed
+ }
+}
+
+const actions = {
+ connectWallet ({ commit }, address: string) {
+ storage.setItem('address', address)
+ commit(types.GET_WALLET_CONNECTED, true)
+ },
+ setTokenFrom ({ commit }, token: any) {
+ commit(types.GET_TOKEN_FROM, token)
+ },
+ setTokenTo ({ commit }, token: any) {
+ commit(types.GET_TOKEN_TO, token)
+ },
+ setFromValue ({ commit }, fromValue: string | number) {
+ commit(types.GET_FROM_VALUE, fromValue)
+ },
+ setToValue ({ commit }, toValue: string | number) {
+ commit(types.GET_TO_VALUE, toValue)
+ },
+ setTokenFromPrice ({ commit }, isTokenFromPrice: boolean) {
+ commit(types.GET_TOKEN_FROM_PRICE, isTokenFromPrice)
+ },
+ setSwapConfirm ({ commit }, isSwapConfirmed: boolean) {
+ commit(types.GET_SWAP_CONFIRM, isSwapConfirmed)
+ }
+}
+
+export default {
+ state,
+ getters,
+ mutations,
+ actions
+}
diff --git a/src/styles/_layout.scss b/src/styles/_layout.scss
index a56338e48..7995e4a1e 100644
--- a/src/styles/_layout.scss
+++ b/src/styles/_layout.scss
@@ -1,7 +1,7 @@
$basic-spacing: 10px;
$inner-window-width: 448px;
-$inner-window-height: 352px;
+$inner-window-height: 298px;
$inner-spacing-mini: 8px;
$inner-spacing-small: $inner-spacing-mini * 1.5;
diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss
new file mode 100644
index 000000000..8f5c93e40
--- /dev/null
+++ b/src/styles/_mixins.scss
@@ -0,0 +1,26 @@
+// This file includes mixins for usage in different components
+
+@mixin token-logo-styles($size: 40px) {
+ height: $size;
+ width: $size;
+ background-color: $s-color-base-border-secondary;
+ background-image: url("~@/assets/img/token-logo-default.svg");
+ background-size: 60%;
+ background-repeat: no-repeat;
+ background-position: 50%;
+ border: 1px solid $s-color-base-border-secondary;
+ border-radius: 50%;
+ box-shadow: $s-shadow-tooltip;
+
+ &--ksm,
+ &--xor {
+ background-size: 100%;
+ }
+
+ &--ksm {
+ background-image: url("~@/assets/img/ksm.svg");
+ }
+ &--xor {
+ background-image: url("~@/assets/img/xor.svg");
+ }
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
new file mode 100644
index 000000000..0873fd5ac
--- /dev/null
+++ b/src/utils/index.ts
@@ -0,0 +1,4 @@
+export const formatNumber = (value: string | number, decimalLendth: number): string => {
+ const valueNumber = +value
+ return valueNumber.toFixed(decimalLendth || 4)
+}
diff --git a/src/utils/storage.ts b/src/utils/storage.ts
new file mode 100644
index 000000000..25625976a
--- /dev/null
+++ b/src/utils/storage.ts
@@ -0,0 +1,15 @@
+export const getItem = (key) => {
+ return localStorage.getItem(key)
+}
+
+export const setItem = (key, value) => {
+ localStorage.setItem(key, value)
+}
+
+export const removeItem = (key) => {
+ localStorage.removeItem(key)
+}
+
+export const clear = () => {
+ localStorage.clear()
+}
diff --git a/tests/unit/views/ConfirmSwap.spec.ts b/tests/unit/views/ConfirmSwap.spec.ts
new file mode 100644
index 000000000..30eaf1b30
--- /dev/null
+++ b/tests/unit/views/ConfirmSwap.spec.ts
@@ -0,0 +1,41 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils'
+import Vuex from 'vuex'
+import SoramitsuElements from '@soramitsu/soramitsu-js-ui'
+import ConfirmSwap from '@/components/ConfirmSwap.vue'
+import { tokens } from '@/mocks/tokens'
+import { TranslationMock } from '../../utils'
+
+const localVue = createLocalVue()
+localVue.use(Vuex)
+localVue.use(SoramitsuElements)
+
+describe('ConfirmSwap.vue', () => {
+ let actions
+ let getters
+ let store
+
+ beforeEach(() => {
+ TranslationMock(ConfirmSwap)
+
+ actions = {
+ setSwapConfirm: jest.fn()
+ }
+
+ getters = {
+ tokenFrom: () => tokens[0],
+ tokenTo: () => tokens[1],
+ fromValue: () => 150.123654,
+ toValue: () => 0
+ }
+
+ store = new Vuex.Store({
+ actions,
+ getters
+ })
+ })
+
+ it('should renders correctly', () => {
+ const wrapper = shallowMount(ConfirmSwap, { localVue, store })
+ expect(wrapper.element).toMatchSnapshot()
+ })
+})
diff --git a/tests/unit/views/Exchange.spec.ts b/tests/unit/views/Exchange.spec.ts
index a5ea78a58..9fbc75742 100644
--- a/tests/unit/views/Exchange.spec.ts
+++ b/tests/unit/views/Exchange.spec.ts
@@ -1,9 +1,11 @@
import { shallowMount, createLocalVue } from '@vue/test-utils'
+import SoramitsuElements from '@soramitsu/soramitsu-js-ui'
import Exchange from '@/views/Exchange.vue'
import { TranslationMock } from '../../utils'
const localVue = createLocalVue()
+localVue.use(SoramitsuElements)
describe('Exchange.vue', () => {
beforeEach(() => {
diff --git a/tests/unit/views/Swap.spec.ts b/tests/unit/views/Swap.spec.ts
index f967601d1..9062ddabc 100644
--- a/tests/unit/views/Swap.spec.ts
+++ b/tests/unit/views/Swap.spec.ts
@@ -1,17 +1,48 @@
import { shallowMount, createLocalVue } from '@vue/test-utils'
-
+import Vuex from 'vuex'
+import SoramitsuElements from '@soramitsu/soramitsu-js-ui'
import Swap from '@/components/Swap.vue'
+import { tokens } from '@/mocks/tokens'
import { TranslationMock } from '../../utils'
const localVue = createLocalVue()
+localVue.use(Vuex)
+localVue.use(SoramitsuElements)
describe('Swap.vue', () => {
+ let actions
+ let getters
+ let store
+
beforeEach(() => {
TranslationMock(Swap)
+
+ actions = {
+ connectWallet: jest.fn(),
+ setTokenFrom: jest.fn(),
+ setTokenTo: jest.fn(),
+ setFromValue: jest.fn(),
+ setToValue: jest.fn(),
+ setTokenFromPrice: jest.fn()
+ }
+
+ getters = {
+ isWalletConnected: () => false,
+ tokenFrom: () => tokens[0],
+ tokenTo: () => tokens[1],
+ fromValue: () => 100,
+ toValue: () => 45.4545,
+ isSwapConfirmed: () => false
+ }
+
+ store = new Vuex.Store({
+ actions,
+ getters
+ })
})
it('should renders correctly', () => {
- const wrapper = shallowMount(Swap, { localVue })
+ const wrapper = shallowMount(Swap, { localVue, store })
expect(wrapper.element).toMatchSnapshot()
})
})
diff --git a/tests/unit/views/SwapInfo.spec.ts b/tests/unit/views/SwapInfo.spec.ts
new file mode 100644
index 000000000..cb5374939
--- /dev/null
+++ b/tests/unit/views/SwapInfo.spec.ts
@@ -0,0 +1,38 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils'
+import Vuex from 'vuex'
+import SoramitsuElements from '@soramitsu/soramitsu-js-ui'
+import SwapInfo from '@/components/SwapInfo.vue'
+import { slippageTolerance, liquidityProviderFee } from '@/mocks/swap'
+import { tokens } from '@/mocks/tokens'
+import { TranslationMock } from '../../utils'
+
+const localVue = createLocalVue()
+localVue.use(Vuex)
+localVue.use(SoramitsuElements)
+
+describe('SwapInfo.vue', () => {
+ let getters
+ let store
+
+ beforeEach(() => {
+ TranslationMock(SwapInfo)
+
+ getters = {
+ tokenFrom: () => tokens[0],
+ tokenTo: () => tokens[1],
+ toValue: () => 100,
+ isTokenFromPrice: () => true,
+ slippageTolerance: () => slippageTolerance,
+ liquidityProviderFee: () => liquidityProviderFee
+ }
+
+ store = new Vuex.Store({
+ getters
+ })
+ })
+
+ it('should renders correctly', () => {
+ const wrapper = shallowMount(SwapInfo, { localVue, store })
+ expect(wrapper.element).toMatchSnapshot()
+ })
+})
diff --git a/tests/unit/views/TransactionSubmit.spec.ts b/tests/unit/views/TransactionSubmit.spec.ts
new file mode 100644
index 000000000..084d2ebd6
--- /dev/null
+++ b/tests/unit/views/TransactionSubmit.spec.ts
@@ -0,0 +1,35 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils'
+import Vuex from 'vuex'
+import SoramitsuElements from '@soramitsu/soramitsu-js-ui'
+import TransactionSubmit from '@/components/TransactionSubmit.vue'
+import { tokens } from '@/mocks/tokens'
+import { TranslationMock } from '../../utils'
+
+const localVue = createLocalVue()
+localVue.use(Vuex)
+localVue.use(SoramitsuElements)
+
+describe('TransactionSubmit.vue', () => {
+ let getters
+ let store
+
+ beforeEach(() => {
+ TranslationMock(TransactionSubmit)
+
+ getters = {
+ tokenFrom: () => tokens[0],
+ tokenTo: () => tokens[1],
+ fromValue: () => 100,
+ toValue: () => 45.4545
+ }
+
+ store = new Vuex.Store({
+ getters
+ })
+ })
+
+ it('should renders correctly', () => {
+ const wrapper = shallowMount(TransactionSubmit, { localVue, store })
+ expect(wrapper.element).toMatchSnapshot()
+ })
+})
diff --git a/tests/unit/views/__snapshots__/ConfirmSwap.spec.ts.snap b/tests/unit/views/__snapshots__/ConfirmSwap.spec.ts.snap
new file mode 100644
index 000000000..9c5117765
--- /dev/null
+++ b/tests/unit/views/__snapshots__/ConfirmSwap.spec.ts.snap
@@ -0,0 +1,83 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ConfirmSwap.vue should renders correctly 1`] = `
+
+
+
+
+ 150.1237
+
+
+
+
+
+ 0.0000
+
+
+
+
+
+
+
+ XOR
+
+
+
+
+
+
+ KSM
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/tests/unit/views/__snapshots__/Exchange.spec.ts.snap b/tests/unit/views/__snapshots__/Exchange.spec.ts.snap
index 413cd77e5..6c9ea5ae8 100644
--- a/tests/unit/views/__snapshots__/Exchange.spec.ts.snap
+++ b/tests/unit/views/__snapshots__/Exchange.spec.ts.snap
@@ -4,20 +4,22 @@ exports[`Exchange.vue should renders correctly 1`] = `
`;
diff --git a/tests/unit/views/__snapshots__/Swap.spec.ts.snap b/tests/unit/views/__snapshots__/Swap.spec.ts.snap
index 17423e5f2..5044cc672 100644
--- a/tests/unit/views/__snapshots__/Swap.spec.ts.snap
+++ b/tests/unit/views/__snapshots__/Swap.spec.ts.snap
@@ -1,8 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Swap.vue should renders correctly 1`] = `
-
-
-
+
-
+
-
+
+
+
+
+
+ XOR
-
-
-
+
+
-
@@ -62,7 +92,11 @@ exports[`Swap.vue should renders correctly 1`] = `
-
+
+ ()
+
@@ -71,37 +105,72 @@ exports[`Swap.vue should renders correctly 1`] = `
-
-
+
-
+
-
+
+
+
+ KSM
-
-
-
+
+
-
+
-
-
+
+
+
+
+
+
+
-
-
+
+
`;
diff --git a/tests/unit/views/__snapshots__/SwapInfo.spec.ts.snap b/tests/unit/views/__snapshots__/SwapInfo.spec.ts.snap
new file mode 100644
index 000000000..b8fa03074
--- /dev/null
+++ b/tests/unit/views/__snapshots__/SwapInfo.spec.ts.snap
@@ -0,0 +1,100 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SwapInfo.vue should renders correctly 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+ 100.0000 KSM
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.02%
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.0006 KSM
+
+
+
+`;
diff --git a/tests/unit/views/__snapshots__/TransactionSubmit.spec.ts.snap b/tests/unit/views/__snapshots__/TransactionSubmit.spec.ts.snap
new file mode 100644
index 000000000..6076ffcf9
--- /dev/null
+++ b/tests/unit/views/__snapshots__/TransactionSubmit.spec.ts.snap
@@ -0,0 +1,53 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`TransactionSubmit.vue should renders correctly 1`] = `
+
+
+
+
+
+
+
+
+
+`;
diff --git a/yarn.lock b/yarn.lock
index cef14e633..7a30db5f0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1141,10 +1141,10 @@
resolved "https://registry.yarnpkg.com/@soda/get-current-script/-/get-current-script-1.0.2.tgz#a53515db25d8038374381b73af20bb4f2e508d87"
integrity sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==
-"@soramitsu/soramitsu-js-ui@^0.5.3":
- version "0.5.3"
- resolved "https://nexus.iroha.tech/repository/npm-group/@soramitsu/soramitsu-js-ui/-/soramitsu-js-ui-0.5.3.tgz#34cea9f502321c1d7174a3a7a57221c62c3c9078"
- integrity sha512-5sswp4HD17h2shmglSXecHRceKn9aSHAGRp3Ik7brXL/FaZ+HtdK9C1HMb1q3tAYLU6sAX1Xz4znQXuqRob/ug==
+"@soramitsu/soramitsu-js-ui@^0.5.4":
+ version "0.5.4"
+ resolved "https://nexus.iroha.tech/repository/npm-group/@soramitsu/soramitsu-js-ui/-/soramitsu-js-ui-0.5.4.tgz#172192bc54410aa7b649ee8bbfed514a5dbc6030"
+ integrity sha512-gKhKOnpAg53opdo+9DOIwKdsEKQuslF2Oel0phwLad02UhRpTuS9MA6isE6s9JnxARNgV5hY74WEl2NQBdScvw==
dependencies:
core-js "^3.6.4"
element-ui "^2.13.2"