From b3c26dfaf8b1782ef5a85eccc6966b4512f4a13c Mon Sep 17 00:00:00 2001
From: 021-projects <20326979+021-projects@users.noreply.github.com>
Date: Fri, 22 Sep 2023 14:39:55 +0300
Subject: [PATCH 1/4] feat(ui-tabs): add support for subtabs
---
src/js/components/UiTabs.vue | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/src/js/components/UiTabs.vue b/src/js/components/UiTabs.vue
index 8a3e897..779c9cb 100644
--- a/src/js/components/UiTabs.vue
+++ b/src/js/components/UiTabs.vue
@@ -19,7 +19,7 @@
@@ -59,7 +59,11 @@ export default {
$route: {
immediate: true,
handler({ hash }) {
- this.activeTab = (hash || this.tabs[0].key).replace('#', '');
+ const cleanedHash = hash?.replace('#', '');
+
+ const tab = this.findTabByKey(cleanedHash);
+
+ this.activeTab = (tab?.key || this.tabs[0].key);
},
},
@@ -69,6 +73,18 @@ export default {
}
},
},
+
+ methods: {
+ findTabByKey ( key ) {
+ return this.tabs.find(tab => {
+ if (tab?.subtabKey && key.startsWith(tab?.subtabKey)) {
+ return true;
+ }
+
+ return tab.key === key;
+ });
+ }
+ }
};
From 5544560bc38935579a80c7eadac8e83b16d0cdf4 Mon Sep 17 00:00:00 2001
From: 021-projects <20326979+021-projects@users.noreply.github.com>
Date: Sat, 23 Sep 2023 12:41:16 +0300
Subject: [PATCH 2/4] chore(tonapi): update api to v2, add getAccount method,
refactor
---
src/js/api/tonapi.js | 16 +++++++++-------
src/js/config.js | 4 +++-
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/js/api/tonapi.js b/src/js/api/tonapi.js
index 9049c58..5add84c 100644
--- a/src/js/api/tonapi.js
+++ b/src/js/api/tonapi.js
@@ -1,4 +1,4 @@
-import { TONAPI_ENDPOINT, TONAPI_KEY } from '~/config.js';
+import { TONAPI_ENDPOINT } from '~/config.js';
import { canonizeAddress } from '~/tonweb.js';
import axios from 'axios';
@@ -11,11 +11,9 @@ const http = axios.create({
* @return {Promise
}
*/
export const getJettonBalances = async function(address) {
- const response = await http.get('jetton/getBalances', { params: {
- account: address,
- }});
+ const response = await http.get(`accounts/${address}/jettons`);
- const balances = (response.data.balances || []).map((balance) => Object.freeze({
+ return (response.data.balances || []).map(( balance ) => Object.freeze({
address: canonizeAddress(balance.wallet_address.address),
balance: balance.balance,
jetton_address: canonizeAddress(balance.jetton_address),
@@ -32,6 +30,10 @@ export const getJettonBalances = async function(address) {
}),
}),
}));
-
- return balances;
};
+
+export const getAccount = async function(address) {
+ const response = await http.get(`accounts/${address}`);
+
+ return response.data;
+}
diff --git a/src/js/config.js b/src/js/config.js
index e0393b0..369c476 100644
--- a/src/js/config.js
+++ b/src/js/config.js
@@ -5,13 +5,15 @@ export const IS_TESTNET = process.env.TESTNET || false;
export const ADDRBOOK_LOCATION = process.env.ADDRBOOK_LOCATION || 'https://catchain.github.io/tonscan/src/addrbook.json';
export const COINGECKO_ENDPOINT = 'https://api.coingecko.com/api/v3';
export const DEV_EXPLORER_ADDRESS = process.env.DEV_EXPLORER_ADDRESS || 'https://explorer.toncoin.org';
+// Todo: This is not actually an endpoint for the TonCenter API, const must be renamed
export const TONCENTER_API_ENDPOINT = process.env.TONCENTER_API_ENDPOINT || 'https://api.ton.cat/v2/explorer';
+export const TONCENTER_API_JSON_RPC_ENDPOINT = process.env.TONCENTER_API_JSON_RPC_ENDPOINT || 'https://toncenter.com/api/v2/jsonRPC';
export const TONCENTER_API_KEY = process.env.TONCENTER_API_KEY;
export const TONCENTER_INDEX_API_ENDPOINT = process.env.TONCENTER_INDEX_API_ENDPOINT || 'https://toncenter.com/api/index';
export const TONCENTER_INDEX_API_KEY = process.env.TONCENTER_INDEX_API_KEY || TONCENTER_API_KEY;
export const EXTENDER_CONTRACTS_API_ENDPOINT = process.env.EXTENDER_CONTRACTS_API_ENDPOINT || 'https://api.ton.cat/v2/explorer/nft';
export const GETGEMS_GRAPHQL_ENDPOINT = process.env.GETGEMS_GRAPHQL_ENDPOINT || 'https://api.getgems.io/graphql';
-export const TONAPI_ENDPOINT = process.env.TONAPI_ENDPOINT || 'https://tonapi.io/v1';
+export const TONAPI_ENDPOINT = process.env.TONAPI_ENDPOINT || 'https://tonapi.io/v2';
export const TONAPI_KEY = process.env.TONAPI_KEY;
export const TYPESENSE_API_KEY = process.env.TYPESENSE_API_KEY;
From 722f28997b1bc5f3e1a75bc5b7af75ae4d41871d Mon Sep 17 00:00:00 2001
From: 021-projects <20326979+021-projects@users.noreply.github.com>
Date: Sat, 23 Sep 2023 12:41:42 +0300
Subject: [PATCH 3/4] feat(components): add ui-select component
---
src/js/components/UiSelect.vue | 195 +++++++++++++++++++++++++++++++++
1 file changed, 195 insertions(+)
create mode 100644 src/js/components/UiSelect.vue
diff --git a/src/js/components/UiSelect.vue b/src/js/components/UiSelect.vue
new file mode 100644
index 0000000..c2a1393
--- /dev/null
+++ b/src/js/components/UiSelect.vue
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+
From 0b0b59376797b3bd33216f412f8c91f6aa280d54 Mon Sep 17 00:00:00 2001
From: 021-projects <20326979+021-projects@users.noreply.github.com>
Date: Sat, 23 Sep 2023 12:50:12 +0300
Subject: [PATCH 4/4] feat(contract): get methods, ton lib added
---
package.json | 4 +
src/js/components/address/AddressTabs.vue | 8 +-
.../address/Verifier/VerifierGetMethods.vue | 297 ++++++++++++++++++
.../address/Verifier/VerifierTabs.vue | 81 +++++
src/js/directives/outsideClick.js | 16 +
src/js/i18n/en.js | 14 +-
src/js/ton.js | 16 +
src/sass/app.scss | 76 ++++-
webpack.config.js | 5 +
9 files changed, 511 insertions(+), 6 deletions(-)
create mode 100644 src/js/components/address/Verifier/VerifierGetMethods.vue
create mode 100644 src/js/components/address/Verifier/VerifierTabs.vue
create mode 100644 src/js/directives/outsideClick.js
create mode 100644 src/js/ton.js
diff --git a/package.json b/package.json
index 7134ecb..44dca7a 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"@primer/octicons": "^17.11.1",
"axios": "^0.24.0",
"axios-retry": "^3.2.5",
+ "buffer": "^6.0.3",
"chart.js": "^4.0.1",
"chartjs-adapter-date-fns": "^2.0.1",
"chartjs-plugin-gradient": "^0.5.1",
@@ -28,6 +29,9 @@
"highlightjs-func": "^0.3.1",
"qrcode.vue": "^1.7.0",
"timeago.js": "^4.0.2",
+ "ton": "^13.6.1",
+ "ton-core": "^0.52.0",
+ "ton-crypto": "^3.2.0",
"vue": "^2.6.14",
"vue-clipboard2": "^0.3.3",
"vue-i18n": "^8.26.8",
diff --git a/src/js/components/address/AddressTabs.vue b/src/js/components/address/AddressTabs.vue
index 5690dfc..fdeea4c 100644
--- a/src/js/components/address/AddressTabs.vue
+++ b/src/js/components/address/AddressTabs.vue
@@ -5,8 +5,7 @@ import IconNft from '@img/icons/material-duotone/view-in-ar.svg?inline';
import IconToken from '@img/icons/material-duotone/toll.svg?inline';
import TabUserNfts from './TabUserNfts.vue';
import TabUserTokens from './TabUserTokens.vue';
-import TabContractSources from './Verifier/Verifier.vue';
-import ContractInfo from './ContractInfo.vue';
+import TabContractTabs from './Verifier/VerifierTabs.vue';
import TxHistory from './TxHistory.vue';
import UiTabs from '~/components/UiTabs.vue';
@@ -57,12 +56,13 @@ export default {
icon: IconToken,
content: { key, props, component: TabUserTokens },
}, {
- key: 'source',
+ key: 'contract_source',
+ subtabKey: 'contract_',
text: this.$t('address.tab_contract'),
icon: IconContract,
content: {
key,
- component: TabContractSources,
+ component: TabContractTabs,
props: {
address: this.address,
isActive: this.isActive,
diff --git a/src/js/components/address/Verifier/VerifierGetMethods.vue b/src/js/components/address/Verifier/VerifierGetMethods.vue
new file mode 100644
index 0000000..f45f38c
--- /dev/null
+++ b/src/js/components/address/Verifier/VerifierGetMethods.vue
@@ -0,0 +1,297 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/js/components/address/Verifier/VerifierTabs.vue b/src/js/components/address/Verifier/VerifierTabs.vue
new file mode 100644
index 0000000..2e2a667
--- /dev/null
+++ b/src/js/components/address/Verifier/VerifierTabs.vue
@@ -0,0 +1,81 @@
+
+
+
diff --git a/src/js/directives/outsideClick.js b/src/js/directives/outsideClick.js
new file mode 100644
index 0000000..520d7b6
--- /dev/null
+++ b/src/js/directives/outsideClick.js
@@ -0,0 +1,16 @@
+// Taken from https://stackoverflow.com/a/42389266
+export default {
+ bind: function ( el, binding, vnode ) {
+ el.clickOutsideEvent = function ( event ) {
+ // here I check that click was outside the el and his children
+ if (!(el === event.target || el.contains(event.target))) {
+ // and if it did, call method provided in attribute value
+ vnode.context[binding.expression](event);
+ }
+ };
+ document.body.addEventListener('click', el.clickOutsideEvent)
+ },
+ unbind: function ( el ) {
+ document.body.removeEventListener('click', el.clickOutsideEvent)
+ }
+}
diff --git a/src/js/i18n/en.js b/src/js/i18n/en.js
index fef6831..9002c7a 100644
--- a/src/js/i18n/en.js
+++ b/src/js/i18n/en.js
@@ -19,6 +19,18 @@ export default {
},
},
+ contract: {
+ tab_source: 'Source',
+ tab_get_methods: 'Get methods',
+ tab_address_formats: 'Address formats',
+ call_method: 'Call method',
+ arbitrary_method: 'Arbitrary method',
+ enter_method_name: 'Enter method name',
+ execute: 'Execute',
+ add_argument: 'Add argument',
+ value: 'Value',
+ },
+
address: {
info: {
address: 'Address',
@@ -73,7 +85,7 @@ export default {
tab_nfts: 'NFTs',
tab_transactions: 'History',
tab_contract: 'Contract',
- tab_tokens: 'Jettons',
+ tab_tokens: 'Jettons',
tx_table: {
empty: 'No transaction history',
diff --git a/src/js/ton.js b/src/js/ton.js
new file mode 100644
index 0000000..7e352a3
--- /dev/null
+++ b/src/js/ton.js
@@ -0,0 +1,16 @@
+import { Address, TonClient } from 'ton';
+import { TONCENTER_API_JSON_RPC_ENDPOINT, TONCENTER_API_KEY } from '~/config.js';
+
+const client = new TonClient({
+ endpoint: TONCENTER_API_JSON_RPC_ENDPOINT,
+ apiKey: TONCENTER_API_KEY
+});
+
+export const runGetMethod = async function ( address, method, args ) {
+ // todo: need builder for args
+
+ return await client.runMethod(
+ Address.parse(address),
+ method,
+ );
+}
diff --git a/src/sass/app.scss b/src/sass/app.scss
index 5602926..3311758 100644
--- a/src/sass/app.scss
+++ b/src/sass/app.scss
@@ -80,6 +80,8 @@ $blueBright: #0069ff;
--chart-grid-color: #2a2a2a;
--chart-pie-border-color: var(--card-background);
--chart-skeleton-background-color: #2c2c2c; //var(--body-light-muted-color);
+
+ --input-bg: #232323;
}
@media (prefers-color-scheme: dark) {
@@ -159,7 +161,7 @@ $blueBright: #0069ff;
html {
font-size: 22px;
}
-
+
@media screen and (max-width: 479px) {
html {
font-size: 14px;
@@ -230,6 +232,78 @@ input[type=search] {
}
}
+.btn-material {
+ color: #FFF;
+ font-weight: 500;
+ padding: 10px 15px;
+ font-size: 14px;
+ text-transform: uppercase;
+ border-radius: 6px;
+ text-align: center;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: .15s background ease;
+ text-decoration: none;
+ background: var(--bg-color);
+ &:hover {
+ text-decoration: none;
+ background: var(--bg-color-hover);
+ }
+ &--blue {
+ --bg-color: var(--big-blue-button-background);
+ --bg-color-hover: var(--big-blue-button-hover-background);
+ }
+ &--grey {
+ --bg-color: var(--body-light-muted-color);
+ --bg-color-hover: rgba(209, 217, 234, 0.14);
+ }
+ &--disabled {
+ --bg-color: var(--big-blue-button-disabled-background);
+ color: var(--big-blue-button-disabled-color);
+ pointer-events: none;
+ }
+
+ .icon {
+ width: 18px;
+ height: 18px;
+ margin-right: 8px;
+ fill: currentColor;
+ }
+}
+
+.input-group {
+ display: flex;
+ flex-direction: row;
+ &--full {
+ flex: 1 1 auto;
+ }
+ .input-material {
+ &:first-child {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-right: 1px solid var(--body-light-muted-color);
+ }
+ &:last-child {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+ }
+}
+
+.input-material {
+ padding: 9px;
+ background: var(--input-bg);
+ color: var(--body-text-color);
+ border-radius: 6px;
+ border: 2px solid transparent;
+ transition: 0.2s all ease;
+ &::placeholder {
+ color: var(--body-muted-text-color);
+ }
+}
+
h1, h2, h3, h4, h5, h6, strong, b {
font-weight: 500;
}
diff --git a/webpack.config.js b/webpack.config.js
index 0043554..08b92f9 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -2,6 +2,7 @@ const dotenv = require('dotenv').config({ path: __dirname + '/.env' + (process.e
const Encore = require('@symfony/webpack-encore');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
+const webpack = require('webpack');
if (!Encore.isRuntimeEnvironmentConfigured()) {
Encore.configureRuntimeEnvironment(process.env.NODE_ENV || 'dev');
@@ -56,6 +57,10 @@ Encore.addPlugin(new HtmlWebpackPlugin({
},
}));
+Encore.addPlugin(new webpack.ProvidePlugin({
+ Buffer: ['buffer', 'Buffer'],
+}))
+
Encore.isProduction() && Encore.enableVersioning();
Encore.isProduction() && Encore.cleanupOutputBeforeBuild();