Compare commits
122 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f28cba9e82 | |||
| b5a4a60759 | |||
| e06ccc8576 | |||
| 82452079a6 | |||
| acf1eaeae8 | |||
| 94715d2501 | |||
| 2eb0f38742 | |||
| 369a688981 | |||
| 222078790b | |||
| 83e5ed7eb3 | |||
|
|
c3e59b2282 | ||
|
|
014f00b2d3 | ||
|
|
b758b165e2 | ||
|
|
9c0d810854 | ||
|
|
f72dccd59f | ||
|
|
88f2def1d1 | ||
|
|
8cdc7c2e1d | ||
|
|
f7d9ae88e1 | ||
|
|
4b6f61f878 | ||
|
|
5330553fd2 | ||
|
|
518ace32b3 | ||
|
|
6d85f2f441 | ||
|
|
20325c05bb | ||
|
|
d1db35c2c1 | ||
|
|
c469a91c9a | ||
|
|
af806f1054 | ||
|
|
78c9597b19 | ||
|
|
8f02a1f65f | ||
|
|
e598d01b69 | ||
|
|
df50cb1253 | ||
|
|
9e3f190a66 | ||
|
|
1bfc76cd3b | ||
|
|
4578301396 | ||
|
|
19fafbe789 | ||
|
|
4622de55d6 | ||
|
|
78a8b74d0d | ||
|
|
949595c2db | ||
|
|
dbd01f2301 | ||
|
|
21a50c1882 | ||
|
|
4949df61b0 | ||
|
|
63abc74816 | ||
|
|
abebe228bf | ||
|
|
8254924a24 | ||
|
|
1e8f4d3d8d | ||
|
|
acb93c2512 | ||
|
|
9a9eb8e1f2 | ||
|
|
98a71606f9 | ||
|
|
89ff672638 | ||
|
|
7001a728b4 | ||
|
|
28f56fb715 | ||
|
|
9f2fe03f33 | ||
|
|
d8876c55b6 | ||
|
|
62c8895f9e | ||
|
|
d61287abb6 | ||
|
|
d7f41d10a7 | ||
|
|
58e68077b0 | ||
|
|
f4a702e766 | ||
|
|
e4dd3c7e43 | ||
|
|
33fd63c3b0 | ||
|
|
e3cd2bc6d0 | ||
|
|
9026aa8579 | ||
|
|
4d8af8bc0e | ||
|
|
aab9b8716a | ||
|
|
6f0cb3582a | ||
|
|
1482cbc306 | ||
|
|
e1f63340b7 | ||
|
|
67fd8f452f | ||
|
|
d62497da77 | ||
|
|
06b79653da | ||
|
|
49799ca649 | ||
|
|
9981eee44f | ||
|
|
90a20deceb | ||
|
|
9039c08a5f | ||
|
|
b434ac1303 | ||
|
|
b7079bba17 | ||
|
|
176422d15d | ||
|
|
e348a8872c | ||
|
|
274a915f32 | ||
|
|
6ee073e2eb | ||
|
|
9313153620 | ||
|
|
cca765b4ab | ||
|
|
210ba99bf5 | ||
|
|
291ed4502e | ||
|
|
069b07e88c | ||
|
|
2d7cd0dda9 | ||
|
|
4d2d093560 | ||
|
|
c42bdef530 | ||
|
|
5522c76d0a | ||
|
|
5fc827fcf7 | ||
|
|
257a5ab596 | ||
|
|
b8edde9bc0 | ||
|
|
ede54e4a7a | ||
|
|
ae5153c22f | ||
|
|
c02b0ccb50 | ||
|
|
277c68d30a | ||
|
|
a15a3ace8e | ||
|
|
ad05cba5bd | ||
|
|
ccbf4a96b7 | ||
|
|
bb14598978 | ||
|
|
8d7d0b1d54 | ||
|
|
b46e704bfa | ||
|
|
29fb061ce5 | ||
|
|
ef3838cbea | ||
|
|
2855ae7941 | ||
|
|
7de0a562a6 | ||
|
|
ab264e4f20 | ||
|
|
85401658ec | ||
|
|
e5b29807d4 | ||
|
|
6edeccb497 | ||
|
|
d97a7a2096 | ||
|
|
3688045165 | ||
|
|
fbdc9648f6 | ||
|
|
54f066d5ad | ||
|
|
49f7564f6c | ||
|
|
38044edff7 | ||
|
|
fac4c1a543 | ||
|
|
1243306c69 | ||
|
|
ff111eb57e | ||
|
|
3662b55443 | ||
|
|
0960ad6596 | ||
|
|
f0120a4a9f | ||
|
|
65995ee8b5 |
22
.env.example
Normal file
22
.env.example
Normal file
@ -0,0 +1,22 @@
|
||||
# rename to .env.local or .env.<your_environment>
|
||||
|
||||
# Refresh interval for the app to query api for new blocks
|
||||
VITE_REFRESH_INTERVAL=6000
|
||||
|
||||
# Enable fetching all blocks (this can increase api calls to public nodes resulting in rate limiting)
|
||||
VITE_FETCH_ALL_BLOCKS=false
|
||||
|
||||
# Limit for recent blocks and associated transactions displayed in the UI
|
||||
VITE_RECENT_BLOCK_LIMIT=50
|
||||
|
||||
# URL for CoinGecko API or custom proxy
|
||||
VITE_COINGECKO_URL=https://api.coingecko.com
|
||||
|
||||
# GITHUB_API_URL default
|
||||
VITE_GITHUB_API_URL=https://api.github.com/repos/cosmos/chain-registry/contents
|
||||
|
||||
# PINGPUB_API_URL default
|
||||
VITE_PINGPUB_API_URL=https://registry.ping.pub
|
||||
|
||||
# IBC use PINGPUB_API_URL by Default (false) or GITHUB_API_URL (true)
|
||||
VITE_IBC_USE_GITHUB_API=false
|
||||
29
.github/workflows/docker-image.yml
vendored
Normal file
29
.github/workflows/docker-image.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Publish cosmos-explorer docker image on release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Run docker build and publish
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Run docker build
|
||||
run: docker build -t cerc/cosmos-explorer -f Dockerfile .
|
||||
- name: Get the version
|
||||
id: vars
|
||||
run: |
|
||||
echo "sha=$(echo ${GITHUB_SHA:0:7})" >> $GITHUB_OUTPUT
|
||||
echo "tag=$(echo ${GITHUB_REF#refs/tags/})" >> $GITHUB_OUTPUT
|
||||
- name: Tag docker image with SHA
|
||||
run: docker tag cerc/cosmos-explorer git.vdb.to/laconicnetwork/cerc/cosmos-explorer:${{steps.vars.outputs.sha}}
|
||||
- name: Tag docker image with release tag
|
||||
run: docker tag git.vdb.to/laconicnetwork/cerc/cosmos-explorer:${{steps.vars.outputs.sha}} git.vdb.to/laconicnetwork/cerc/cosmos-explorer:${{steps.vars.outputs.tag}}
|
||||
- name: Docker Login
|
||||
run: echo ${{ secrets.CICD_PUBLISH_TOKEN }} | docker login https://git.vdb.to -u laconiccicd --password-stdin
|
||||
- name: Docker Push SHA
|
||||
run: docker push git.vdb.to/laconicnetwork/cerc/cosmos-explorer:${{steps.vars.outputs.sha}}
|
||||
- name: Docker Push TAGGED
|
||||
run: docker push git.vdb.to/laconicnetwork/cerc/cosmos-explorer:${{steps.vars.outputs.tag}}
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -2,4 +2,7 @@ node_modules/
|
||||
**/.vscode
|
||||
yarn-error.log
|
||||
dist
|
||||
.idea
|
||||
.idea
|
||||
|
||||
.env*
|
||||
!.env.example
|
||||
|
||||
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@ -0,0 +1,4 @@
|
||||
dist/
|
||||
.github/
|
||||
*.md
|
||||
auto-imports.d.ts
|
||||
@ -1,9 +1,12 @@
|
||||
{
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"semi": true,
|
||||
"endOfLine": "auto",
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"TrailingCooma": true,
|
||||
"arrowParens": "always"
|
||||
"endOfLine": "auto",
|
||||
"printWidth": 80,
|
||||
"semi": true,
|
||||
"singleAttributePerLine": false,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": false
|
||||
}
|
||||
|
||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
# Originally from: https://github.com/devcontainers/images/blob/main/src/javascript-node/.devcontainer/Dockerfile
|
||||
# [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster
|
||||
ARG VARIANT=20-bullseye
|
||||
|
||||
FROM node:${VARIANT}
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN yarn install
|
||||
|
||||
# Expose port for http
|
||||
EXPOSE 4173
|
||||
26
chains/Kiichain
Normal file
26
chains/Kiichain
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"chain_name": "kiichain",
|
||||
"registry_name": "kiichain",
|
||||
"api": [
|
||||
{ "provider": "kiichain", "address": "https://api-kiichain.gon.world" },
|
||||
{ "provider": "gamisama", "address": "https://kiichain-api.gamisama.com" }
|
||||
],
|
||||
"rpc": [
|
||||
{ "provider": "kiichain", "address": "https://rpc-kiichain.gon.world" },
|
||||
{ "provider": "gamisama", "address": "https://kiichain-rpc.gamisama.com" }
|
||||
],
|
||||
"sdk_version": "0.47.0",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "100",
|
||||
"addr_prefix": "ki",
|
||||
"logo": "/logos/kiichain.svg",
|
||||
"assets": [
|
||||
{
|
||||
"base": "uki",
|
||||
"symbol": "KII",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "kiichain",
|
||||
"logo": "/logos/kiichain.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,74 +1,70 @@
|
||||
{
|
||||
"chain_name": "axelar",
|
||||
"api": [
|
||||
"https://rest.axelar.lava.build/lava-referer-97409c72-1a82-4861-8651-119c15151cbe"
|
||||
],
|
||||
"rpc": [
|
||||
"https://tm.axelar.lava.build/lava-referer-97409c72-1a82-4861-8651-119c15151cbe"
|
||||
],
|
||||
"snapshot_provider": "",
|
||||
"sdk_version": "0.45.6",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "800",
|
||||
"addr_prefix": "axelar",
|
||||
"logo": "/logos/axelar.svg",
|
||||
"theme_color": "#161723",
|
||||
"assets": [
|
||||
{
|
||||
"base": "uaxl",
|
||||
"symbol": "AXL",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "axelar",
|
||||
"logo": "/logos/axelar.svg"
|
||||
},
|
||||
{
|
||||
"base": "uusdc",
|
||||
"symbol": "axlUSDC",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "usd-coin",
|
||||
"logo": "/logos/usdc.svg"
|
||||
},
|
||||
{
|
||||
"base": "uusdt",
|
||||
"symbol": "axlUSDT",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "tether",
|
||||
"logo": "/logos/usdt.svg"
|
||||
},
|
||||
{
|
||||
"base": "dai-wei",
|
||||
"symbol": "axlDAI",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "dai",
|
||||
"logo": "/logos/dai.svg"
|
||||
},
|
||||
{
|
||||
"base": "weth-wei",
|
||||
"symbol": "axlWETH",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "ethereum",
|
||||
"logo": "/logos/weth.svg"
|
||||
},
|
||||
{
|
||||
"base": "wmatic-wei",
|
||||
"symbol": "axlWMATIC",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "matic-network",
|
||||
"logo": "/logos/wmatic.svg"
|
||||
},
|
||||
{
|
||||
"base": "wavax-wei",
|
||||
"symbol": "axlWAVAX",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "avalanche-2",
|
||||
"logo": "/logos/wavax.svg"
|
||||
},
|
||||
{
|
||||
"base": "dot-planck",
|
||||
"symbol": "axlDOT",
|
||||
"exponent": "10",
|
||||
"coingecko_id": "polkadot",
|
||||
"logo": "/logos/dot.svg"
|
||||
}
|
||||
]
|
||||
"chain_name": "axelar",
|
||||
"api": ["https://rest.axelar.lava.build/lava-referer-97409c72-1a82-4861-8651-119c15151cbe"],
|
||||
"rpc": ["https://tm.axelar.lava.build/lava-referer-97409c72-1a82-4861-8651-119c15151cbe"],
|
||||
"snapshot_provider": "",
|
||||
"sdk_version": "0.45.6",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "800",
|
||||
"addr_prefix": "axelar",
|
||||
"logo": "/logos/axelar.svg",
|
||||
"theme_color": "#161723",
|
||||
"assets": [
|
||||
{
|
||||
"base": "uaxl",
|
||||
"symbol": "AXL",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "axelar",
|
||||
"logo": "/logos/axelar.svg"
|
||||
},
|
||||
{
|
||||
"base": "uusdc",
|
||||
"symbol": "axlUSDC",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "usd-coin",
|
||||
"logo": "/logos/usdc.svg"
|
||||
},
|
||||
{
|
||||
"base": "uusdt",
|
||||
"symbol": "axlUSDT",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "tether",
|
||||
"logo": "/logos/usdt.svg"
|
||||
},
|
||||
{
|
||||
"base": "dai-wei",
|
||||
"symbol": "axlDAI",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "dai",
|
||||
"logo": "/logos/dai.svg"
|
||||
},
|
||||
{
|
||||
"base": "weth-wei",
|
||||
"symbol": "axlWETH",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "ethereum",
|
||||
"logo": "/logos/weth.svg"
|
||||
},
|
||||
{
|
||||
"base": "wmatic-wei",
|
||||
"symbol": "axlWMATIC",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "matic-network",
|
||||
"logo": "/logos/wmatic.svg"
|
||||
},
|
||||
{
|
||||
"base": "wavax-wei",
|
||||
"symbol": "axlWAVAX",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "avalanche-2",
|
||||
"logo": "/logos/wavax.svg"
|
||||
},
|
||||
{
|
||||
"base": "dot-planck",
|
||||
"symbol": "axlDOT",
|
||||
"exponent": "10",
|
||||
"coingecko_id": "polkadot",
|
||||
"logo": "/logos/dot.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,31 +1,28 @@
|
||||
{
|
||||
"chain_name": "cosmos",
|
||||
"registry_name": "cosmoshub",
|
||||
"api": [
|
||||
{"provider": "notional", "address": "https://api-cosmoshub-ia.cosmosia.notional.ventures"},
|
||||
{"provider": "blockapsis", "address": "https://lcd-cosmoshub.blockapsis.com:443"},
|
||||
{"provider": "WhisperNode🤐", "address": "https://lcd-cosmoshub.whispernode.com:443"},
|
||||
{"provider": "pupmos", "address": "https://api-cosmoshub.pupmos.network"},
|
||||
{"provider": "publicnode", "address": "https://cosmos-rest.publicnode.com"},
|
||||
{"provider": "staketab", "address": "https://cosmos-rest.staketab.org"},
|
||||
{"provider": "nodestake", "address": "https://api.cosmos.nodestake.top"},
|
||||
{"provider": "Golden Ratio Staking", "address": "https://rest-cosmoshub.goldenratiostaking.net"}
|
||||
],
|
||||
"rpc": [
|
||||
{"provider": "icycro", "address": "https://cosmos-rpc.icycro.org"},
|
||||
{"provider": "dragonstake", "address": "https://rpc.cosmos.dragonstake.io"},
|
||||
{"provider": "Golden Ratio Staking", "address": "https://rpc-cosmoshub.goldenratiostaking.net"}
|
||||
],
|
||||
"sdk_version": "0.45.1",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "800",
|
||||
"addr_prefix": "cosmos",
|
||||
"logo": "/logos/cosmos.svg",
|
||||
"assets": [{
|
||||
"base": "uatom",
|
||||
"symbol": "ATOM",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "cosmos",
|
||||
"logo": "/logos/cosmos.svg"
|
||||
}]
|
||||
"chain_name": "cosmos",
|
||||
"registry_name": "cosmoshub",
|
||||
"api": [
|
||||
{ "provider": "cosmos.directory", "address": "https://rest.cosmos.directory/cosmoshub" },
|
||||
{ "provider": "publicnode", "address": "https://cosmos-rest.publicnode.com" },
|
||||
{ "provider": "silknode", "address": "https://cosmos.api.silknodes.io" }
|
||||
],
|
||||
"rpc": [
|
||||
{ "provider": "icycro", "address": "https://cosmos-rpc.icycro.org" },
|
||||
{ "provider": "dragonstake", "address": "https://rpc.cosmos.dragonstake.io" },
|
||||
{ "provider": "Golden Ratio Staking", "address": "https://rpc-cosmoshub.goldenratiostaking.net" }
|
||||
],
|
||||
"sdk_version": "0.45.1",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "800",
|
||||
"addr_prefix": "cosmos",
|
||||
"logo": "/logos/cosmos.svg",
|
||||
"assets": [
|
||||
{
|
||||
"base": "uatom",
|
||||
"symbol": "ATOM",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "cosmos",
|
||||
"logo": "/logos/cosmos.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,30 +1,43 @@
|
||||
{
|
||||
"chain_name": "neutron",
|
||||
"api": [
|
||||
{"provider": "Polkachu", "address": "https://neutron-api.polkachu.com"},
|
||||
{"provider": "NodeStake", "address": "https://api.neutron.nodestake.top"},
|
||||
{"provider": "Allnodes", "address": "https://neutron-rest.publicnode.com"}
|
||||
],
|
||||
"rpc": [
|
||||
{"provider": "Polkachu", "address": "https://neutron-rpc.polkachu.com"},
|
||||
{"provider": "NodeStake", "address": "https://rpc.neutron.nodestake.top"},
|
||||
{"provider": "Allnodes", "address": "https://neutron-rpc.publicnode.com:443"}
|
||||
],
|
||||
"provider_chain": {
|
||||
"api": ["https://api-cosmoshub-ia.cosmosia.notional.ventures"]
|
||||
},
|
||||
"features": ["dashboard", "blocks", "ibc", "cosmwasm", "uptime", "parameters", "state-sync", "consensus", "supply", "widget"],
|
||||
"sdk_version": "0.45.1",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "8000",
|
||||
"assets": [{
|
||||
"base": "untrn",
|
||||
"symbol": "NTRN",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "neutron",
|
||||
"logo": "/logos/neutron.svg"
|
||||
}],
|
||||
"addr_prefix": "neutron",
|
||||
"theme_color": "#161723",
|
||||
"logo": "/logos/neutron.svg"
|
||||
"chain_name": "neutron",
|
||||
"api": [
|
||||
{ "provider": "Polkachu", "address": "https://neutron-api.polkachu.com" },
|
||||
{ "provider": "NodeStake", "address": "https://api.neutron.nodestake.top" },
|
||||
{ "provider": "Allnodes", "address": "https://neutron-rest.publicnode.com" }
|
||||
],
|
||||
"rpc": [
|
||||
{ "provider": "Polkachu", "address": "https://neutron-rpc.polkachu.com" },
|
||||
{ "provider": "NodeStake", "address": "https://rpc.neutron.nodestake.top" },
|
||||
{ "provider": "Allnodes", "address": "https://neutron-rpc.publicnode.com:443" }
|
||||
],
|
||||
"provider_chain": {
|
||||
"api": ["https://rest.cosmos.directory/cosmoshub"]
|
||||
},
|
||||
"features": [
|
||||
"dashboard",
|
||||
"blocks",
|
||||
"ibc",
|
||||
"cosmwasm",
|
||||
"uptime",
|
||||
"parameters",
|
||||
"state-sync",
|
||||
"consensus",
|
||||
"supply",
|
||||
"widget"
|
||||
],
|
||||
"sdk_version": "0.45.1",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "8000",
|
||||
"assets": [
|
||||
{
|
||||
"base": "untrn",
|
||||
"symbol": "NTRN",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "neutron",
|
||||
"logo": "/logos/neutron.svg"
|
||||
}
|
||||
],
|
||||
"addr_prefix": "neutron",
|
||||
"theme_color": "#161723",
|
||||
"logo": "/logos/neutron.svg"
|
||||
}
|
||||
|
||||
@ -1,27 +1,29 @@
|
||||
{
|
||||
"chain_name": "nolus",
|
||||
"coingecko": "nolus",
|
||||
"api": [
|
||||
{"provider": "Nolus", "address": "https://pirin-cl.nolus.network:1317"},
|
||||
{"provider": "LavenderFive", "address": "https://nolus-api.lavenderfive.com:443"},
|
||||
{"provider": "Allnodes", "address": "https://nolus-rest.publicnode.com"}
|
||||
],
|
||||
"rpc": [
|
||||
{"provider": "Nolus", "address": "https://pirin-cl.nolus.network:26657"},
|
||||
{"provider": "LavenderFive", "address": "https://nolus-rpc.lavenderfive.com:443"},
|
||||
{"provider": "Allnodes", "address": "https://nolus-rpc.publicnode.com:443"}
|
||||
],
|
||||
"snapshot_provider": "",
|
||||
"sdk_version": "v0.47.6",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "0",
|
||||
"addr_prefix": "nolus",
|
||||
"logo": "/logos/nolus.svg",
|
||||
"assets": [{
|
||||
"base": "unls",
|
||||
"symbol": "NLS",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "nolus",
|
||||
"logo": "/logos/nolus.svg"
|
||||
}]
|
||||
"chain_name": "nolus",
|
||||
"coingecko": "nolus",
|
||||
"api": [
|
||||
{ "provider": "Nolus", "address": "https://pirin-cl.nolus.network:1317" },
|
||||
{ "provider": "LavenderFive", "address": "https://nolus-api.lavenderfive.com:443" },
|
||||
{ "provider": "Allnodes", "address": "https://nolus-rest.publicnode.com" }
|
||||
],
|
||||
"rpc": [
|
||||
{ "provider": "Nolus", "address": "https://pirin-cl.nolus.network:26657" },
|
||||
{ "provider": "LavenderFive", "address": "https://nolus-rpc.lavenderfive.com:443" },
|
||||
{ "provider": "Allnodes", "address": "https://nolus-rpc.publicnode.com:443" }
|
||||
],
|
||||
"snapshot_provider": "",
|
||||
"sdk_version": "v0.47.6",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "0",
|
||||
"addr_prefix": "nolus",
|
||||
"logo": "/logos/nolus.svg",
|
||||
"assets": [
|
||||
{
|
||||
"base": "unls",
|
||||
"symbol": "NLS",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "nolus",
|
||||
"logo": "/logos/nolus.svg"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -1,32 +1,47 @@
|
||||
{
|
||||
"chain_name": "osmosis",
|
||||
"coingecko": "osmosis",
|
||||
"api": ["https://lcd.osmosis.zone","https://api-osmosis-ia.cosmosia.notional.ventures", "https://osmosis-api.polkachu.com", "https://lcd-osmosis.blockapsis.com"],
|
||||
"rpc": ["https://rpc.osmosis.zone", "https://rpc-osmosis-ia.cosmosia.notional.ventures:443", "https://osmosis-rpc.polkachu.com:443", "https://osmosis.validator.network:443", "https://rpc-osmosis.blockapsis.com:443"],
|
||||
"snapshot_provider": "",
|
||||
"sdk_version": "0.46.1",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "800",
|
||||
"addr_prefix": "osmo",
|
||||
"logo": "/logos/osmosis.jpg",
|
||||
"theme_color": "#812cd6",
|
||||
"assets": [{
|
||||
"base": "uosmo",
|
||||
"symbol": "OSMO",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "osmosis",
|
||||
"logo": "/logos/osmosis.jpg"
|
||||
},{
|
||||
"base": "uion",
|
||||
"symbol": "ION",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "ion",
|
||||
"logo": "/logos/osmosis.jpg"
|
||||
},{
|
||||
"base": "usomm",
|
||||
"symbol": "SOMM",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "somm",
|
||||
"logo": ""
|
||||
}]
|
||||
"chain_name": "osmosis",
|
||||
"coingecko": "osmosis",
|
||||
"api": [
|
||||
"https://lcd.osmosis.zone",
|
||||
"https://api-osmosis-ia.cosmosia.notional.ventures",
|
||||
"https://osmosis-api.polkachu.com",
|
||||
"https://lcd-osmosis.blockapsis.com"
|
||||
],
|
||||
"rpc": [
|
||||
"https://rpc.osmosis.zone",
|
||||
"https://rpc-osmosis-ia.cosmosia.notional.ventures:443",
|
||||
"https://osmosis-rpc.polkachu.com:443",
|
||||
"https://osmosis.validator.network:443",
|
||||
"https://rpc-osmosis.blockapsis.com:443"
|
||||
],
|
||||
"snapshot_provider": "",
|
||||
"sdk_version": "0.46.1",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "800",
|
||||
"addr_prefix": "osmo",
|
||||
"logo": "/logos/osmosis.jpg",
|
||||
"theme_color": "#812cd6",
|
||||
"assets": [
|
||||
{
|
||||
"base": "uosmo",
|
||||
"symbol": "OSMO",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "osmosis",
|
||||
"logo": "/logos/osmosis.jpg"
|
||||
},
|
||||
{
|
||||
"base": "uion",
|
||||
"symbol": "ION",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "ion",
|
||||
"logo": "/logos/osmosis.jpg"
|
||||
},
|
||||
{
|
||||
"base": "usomm",
|
||||
"symbol": "SOMM",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "somm",
|
||||
"logo": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
150
chains/mainnet/xion.json
Normal file
150
chains/mainnet/xion.json
Normal file
@ -0,0 +1,150 @@
|
||||
{
|
||||
"chain_name": "xion",
|
||||
"chain_id": "xion-mainnet-1",
|
||||
"registry_name": "xion",
|
||||
"coingecko": "xion",
|
||||
"network_type": "mainnet",
|
||||
"rpc": [
|
||||
{
|
||||
"address": "https://rpc.xion-mainnet-1.burnt.com",
|
||||
"provider": "🔥BurntLabs🔥"
|
||||
},
|
||||
{
|
||||
"address": "https://rpc-burnt.imperator.co/",
|
||||
"provider": "Imperator.co"
|
||||
},
|
||||
{
|
||||
"address": "https://xion-rpc.polkachu.com",
|
||||
"provider": "Polkachu"
|
||||
}
|
||||
],
|
||||
"api": [
|
||||
{
|
||||
"address": "https://api.xion-mainnet-1.burnt.com",
|
||||
"provider": "🔥BurntLabs🔥"
|
||||
},
|
||||
{
|
||||
"address": "https://lcd-burnt.imperator.co/",
|
||||
"provider": "Imperator.co"
|
||||
},
|
||||
{
|
||||
"address": "https://xion-api.polkachu.com",
|
||||
"provider": "Polkachu"
|
||||
}
|
||||
],
|
||||
"snapshot_provider": "",
|
||||
"sdk_version": "0.53.3",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "100",
|
||||
"addr_prefix": "xion",
|
||||
"theme_color": "#96b325",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/xion/images/burnt-round.png",
|
||||
"assets": [
|
||||
{
|
||||
"base": "uxion",
|
||||
"symbol": "XION",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "xion-2",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/xion/images/burnt-round.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/0471F1C4E7AFD3F07702BEF6DC365268D64570F7C1FDC98EA6098DD6DE59817B",
|
||||
"symbol": "OSMO",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "osmosis",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/osmosis/images/osmo.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/F082B65C88E4B6D5EF1DB243CDA1D331D002759E938A0F5CD3FFDC5D53B3E349",
|
||||
"symbol": "USDC",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "usd-coin",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/noble/images/USDCoin.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/CC7B293B3F08EA7DB96AFD4765BD0C7F95ABD7ECEAF21C74F3ACCBF7CEFB6591",
|
||||
"symbol": "OSMO",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "osmosis",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/osmosis/images/osmo.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/9463E39D230614B313B487836D13A392BD1731928713D4C8427A083627048DB3",
|
||||
"symbol": "AXL",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "axelar",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/axl.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/6490A7EAB61059BFC1CDDEB05917DD70BDF3A611654162A1A47DB930D40D8AF4",
|
||||
"symbol": "axlUSDC",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "usd-coin",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/axlusdc.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"symbol": "axlUSDT",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "tether",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/axlusdc.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"symbol": "axlDAI",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "dai",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/axldai.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"symbol": "axlFRAX",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "frax",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/axlfrax.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/AAD7136DD626569C3DDE7C5F764968BB2E939875EFC568AE5712B62081850814",
|
||||
"symbol": "axlWETH",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "axlweth",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/axelar/images/weth.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/056EA54C3D9B49B3C0418955A27980A91DD4F210914BFE240A1DB19E27895ECA",
|
||||
"symbol": "KYVE",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "kyve-network",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/kyve/images/kyve-token.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/DBE9697AC1044255A305A2034AD360B4152632BFBFB5785234731F60196B9645",
|
||||
"symbol": "ELYS",
|
||||
"exponent": "6",
|
||||
"coingecko_id": "elys",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/elys/images/elys.png"
|
||||
},
|
||||
{
|
||||
"base": "ibc/E706A0C6CACB374ADC2BCF6A74FE1B260840FC822E45DCB776DEA962A57FED30",
|
||||
"symbol": "axlARB",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "arb",
|
||||
"logo": "https://raw.githubusercontent.com/cosmos/chain-registry/master/_non-cosmos/arbitrum/images/arb.png"
|
||||
}
|
||||
],
|
||||
"features": [
|
||||
"dashboard",
|
||||
"governance",
|
||||
"staking",
|
||||
"blocks",
|
||||
"tx",
|
||||
"uptime",
|
||||
"ibc",
|
||||
"supply",
|
||||
"parameters",
|
||||
"consensus",
|
||||
"cosmwasm",
|
||||
"account"
|
||||
],
|
||||
"keplr_features": ["ibc-go", "ibc-transfer", "no-legacy-stdTx"]
|
||||
}
|
||||
21
chains/testnet/bfhevm.json
Normal file
21
chains/testnet/bfhevm.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"chain_name": "bfhevm_777-1",
|
||||
"api": ["https://rest-testnet-bfhevm.xyz:443"],
|
||||
"rpc": ["https://rpc-bfhevm.xyz:8443"],
|
||||
"coingecko": "",
|
||||
"snapshot_provider": "",
|
||||
"sdk_version": "0.45.7",
|
||||
"coin_type": "60",
|
||||
"min_tx_fee": "700",
|
||||
"addr_prefix": "bfh",
|
||||
"logo": "",
|
||||
"assets": [
|
||||
{
|
||||
"base": "abfh",
|
||||
"symbol": "BFH",
|
||||
"exponent": "18",
|
||||
"coingecko_id": "",
|
||||
"logo": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
{
|
||||
"chain_name": "crossfi-testnet-1",
|
||||
"api": ["https://crossfi-testnet-api.forpeaky.xyz"],
|
||||
"rpc": ["https://crossfi-testnet-rpc.forpeaky.xyz"],
|
||||
"coingecko": "",
|
||||
"snapshot_provider": "",
|
||||
"sdk_version": "0.47.1",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "500",
|
||||
"addr_prefix": "crossfi"
|
||||
}
|
||||
"chain_name": "crossfi-testnet-1",
|
||||
"api": ["https://crossfi-testnet-api.forpeaky.xyz"],
|
||||
"rpc": ["https://crossfi-testnet-rpc.forpeaky.xyz"],
|
||||
"coingecko": "",
|
||||
"snapshot_provider": "",
|
||||
"sdk_version": "0.47.1",
|
||||
"coin_type": "118",
|
||||
"min_tx_fee": "500",
|
||||
"addr_prefix": "crossfi"
|
||||
}
|
||||
|
||||
15
env.d.ts
vendored
15
env.d.ts
vendored
@ -1,3 +1,16 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '@personaxyz/ad-sdk';
|
||||
declare module '@personaxyz/ad-sdk';
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_REFRESH_INTERVAL?: number,
|
||||
readonly VITE_FETCH_ALL_BLOCKS?: boolean,
|
||||
readonly VITE_RECENT_BLOCK_LIMIT?: number,
|
||||
readonly VITE_COINGECKO_URL?: string,
|
||||
readonly VITE_GITHUB_API_URL?: string,
|
||||
readonly VITE_PINGPUB_API_URL?: string,
|
||||
readonly VITE_IBC_USE_GITHUB_API?: string,
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
|
||||
24
index.html
24
index.html
@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Ping Dashboard - Cosmos Blockchain Explorer And Web Wallet</title>
|
||||
<meta name="description" content="Ping Dashboard is a block explorer/web wallet for blockchains built on Cosmos SDK, Cosmoshub, Osmosis, Juno, Evmos, Injective, Canto and 70+ blockchains listed on ping.pub" />
|
||||
<title>Zenith Blockchain Explorer And Web Wallet</title>
|
||||
<meta name="description" content="Zenith Explorer is a block explorer/web wallet for zenithd blockchain" />
|
||||
<link rel="stylesheet" type="text/css" href="/loader.css" />
|
||||
</head>
|
||||
<body>
|
||||
@ -23,22 +23,26 @@
|
||||
</div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SSBKVF3GMX"></script>
|
||||
<script
|
||||
async
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-SSBKVF3GMX"
|
||||
></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
function gtag() {
|
||||
dataLayer.push(arguments);
|
||||
}
|
||||
gtag('js', new Date());
|
||||
// Set default consent to 'denied' as a placeholder
|
||||
// Determine actual values based on your own requirements
|
||||
gtag('consent', 'default', {
|
||||
'ad_storage': 'denied',
|
||||
'ad_user_data': 'denied',
|
||||
'ad_personalization': 'denied',
|
||||
'analytics_storage': 'denied'
|
||||
ad_storage: 'denied',
|
||||
ad_user_data: 'denied',
|
||||
ad_personalization: 'denied',
|
||||
analytics_storage: 'denied',
|
||||
});
|
||||
gtag('config', 'G-SSBKVF3GMX');
|
||||
</script>
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/ping-widget@v0.3.8/dist/ping-widget.min.js"></script>
|
||||
|
||||
<script type="module" src="https://cdn.jsdelivr.net/npm/@ping-pub/widget@latest/dist/widget.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -79,4 +79,4 @@ server {
|
||||
}
|
||||
}
|
||||
```
|
||||
3. config your blockchain in [./src/chains]()
|
||||
3. config your blockchain in [./chains/mainnet]()
|
||||
|
||||
26
package.json
26
package.json
@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "ping.pub",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1-zenith-0.1.2",
|
||||
"private": true,
|
||||
"target": "",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"format": "prettier --write .",
|
||||
"serve": "vite",
|
||||
"build": "run-p type-check build-only",
|
||||
"preview": "vite preview",
|
||||
@ -12,18 +13,20 @@
|
||||
"type-check": "vue-tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chain-registry/client": "^1.53.184",
|
||||
"@chain-registry/types": "^0.50.184",
|
||||
"@chenfengyuan/vue-countdown": "2",
|
||||
"@cosmjs/crypto": "^0.32.3",
|
||||
"@cosmjs/amino": "^0.32.3",
|
||||
"@cosmjs/crypto": "^0.32.3",
|
||||
"@cosmjs/encoding": "^0.32.3",
|
||||
"@cosmjs/stargate": "^0.32.3",
|
||||
"@cosmjs/cosmwasm-stargate": "^0.30.0",
|
||||
"@iconify/vue": "^4.1.0",
|
||||
"@intlify/unplugin-vue-i18n": "^0.8.2",
|
||||
"@leapwallet/cosmos-snap-provider": "^0.1.20",
|
||||
"@leapwallet/name-matcha": "^1.1.0",
|
||||
"@leapwallet/name-matcha": "^2.0.0",
|
||||
"@osmonauts/lcd": "^0.8.0",
|
||||
"@personaxyz/ad-sdk": "0.0.25",
|
||||
"@ping-pub/chain-registry-client": "^0.0.25",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.0",
|
||||
"@vueuse/core": "^9.12.0",
|
||||
"@vueuse/integrations": "^10.1.2",
|
||||
@ -31,11 +34,14 @@
|
||||
"apexcharts": "^3.37.1",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"axios": "^1.3.2",
|
||||
"bech32": "^1.1.4",
|
||||
"buffer": "^6.0.3",
|
||||
"build": "^0.1.4",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"daisyui": "^3.1.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"idna-uts46-hx": "^5.0.7",
|
||||
"js-sha3": "^0.8.0",
|
||||
"lazy-load-vue3": "^1.3.0",
|
||||
"long": "^5.2.1",
|
||||
"md-editor-v3": "^2.8.1",
|
||||
@ -63,8 +69,10 @@
|
||||
"@types/semver": "7.5.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"husky": "^9.1.7",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier": "^3.0.0",
|
||||
"pretty-quick": "^4.2.2",
|
||||
"sass": "^1.58.0",
|
||||
"shiki": "^1.0.0-beta.0",
|
||||
"typescript": "~4.9.5",
|
||||
@ -75,5 +83,11 @@
|
||||
"vite-plugin-pages": "^0.28.0",
|
||||
"vue-json-viewer": "3",
|
||||
"vue-tsc": "^1.0.12"
|
||||
}
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "pretty-quick --staged"
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
|
||||
}
|
||||
|
||||
@ -3,4 +3,4 @@ module.exports = {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Widget Test</title>
|
||||
<script src="https://unpkg.com/ping-widget@latest/dist/ping-widget.js" type="module" ></script>
|
||||
</head>
|
||||
<body>
|
||||
<head>
|
||||
<title>Widget Test</title>
|
||||
<script src="https://unpkg.com/ping-widget@latest/dist/ping-widget.js" type="module"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="p-5">
|
||||
<div>
|
||||
<ping-connect-wallet chain-id="kava_2222-10" hd-path="m/44'/118/0'/0/0"/>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<ping-connect-wallet chain-id="kava_2222-10" hd-path="m/44'/118/0'/0/0" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="PingTokenConvert" class="btn">Buy Kava</label>
|
||||
<ping-token-convert chain-name="kava" endpoint="https://api.data.kava.io" hd-path="m/44'/118/0'/0/0"/>
|
||||
<ping-token-convert chain-name="kava" endpoint="https://api.data.kava.io" hd-path="m/44'/118/0'/0/0" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 7.1 KiB |
@ -13,8 +13,8 @@ function calculateValue(value: any) {
|
||||
if (Array.isArray(value)) {
|
||||
return (value[0] && value[0].amount) || '-';
|
||||
}
|
||||
if(String(value).search(/^\d+s$/g) > -1) {
|
||||
return formatSeconds(value)
|
||||
if (String(value).search(/^\d+s$/g) > -1) {
|
||||
return formatSeconds(value);
|
||||
}
|
||||
const newValue = Number(value);
|
||||
if (`${newValue}` === 'NaN' || typeof value === 'boolean') {
|
||||
@ -28,8 +28,8 @@ function calculateValue(value: any) {
|
||||
}
|
||||
|
||||
function formatTitle(v: string) {
|
||||
if(!v) return ""
|
||||
return v.replace(/_/g, " ")
|
||||
if (!v) return '';
|
||||
return v.replace(/_/g, ' ');
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
@ -38,14 +38,8 @@ function formatTitle(v: string) {
|
||||
v-if="props.cardItem?.items && props.cardItem?.items?.length > 0"
|
||||
>
|
||||
<div class="text-base mb-3 text-main">{{ props.cardItem?.title }}</div>
|
||||
<div
|
||||
class="grid grid-cols-2 md:!grid-cols-4 lg:!grid-cols-5 2xl:!grid-cols-6 gap-4"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) of props.cardItem?.items"
|
||||
:key="index"
|
||||
class="rounded-sm bg-active px-4 py-2"
|
||||
>
|
||||
<div class="grid grid-cols-2 md:!grid-cols-4 lg:!grid-cols-5 2xl:!grid-cols-6 gap-4">
|
||||
<div v-for="(item, index) of props.cardItem?.items" :key="index" class="rounded-sm bg-active px-4 py-2">
|
||||
<div class="text-xs mb-2 text-secondary capitalize">{{ formatTitle(item?.subtitle) }}</div>
|
||||
<div class="text-base text-main">{{ calculateValue(item?.value) }}</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { controlledComputed } from '@vueuse/core'
|
||||
import { controlledComputed } from '@vueuse/core';
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@ -24,15 +24,9 @@ const isPositive = controlledComputed(
|
||||
<template>
|
||||
<div class="bg-base-100 shadow rounded p-4">
|
||||
<div class="flex items-center justify-center">
|
||||
<div
|
||||
v-if="props.icon"
|
||||
class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center"
|
||||
>
|
||||
<div v-if="props.icon" class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center">
|
||||
<Icon :class="[`text-${props?.color}`]" :icon="props.icon" size="32" />
|
||||
<div
|
||||
class="absolute top-0 left-0 bottom-0 right-0 opacity-20"
|
||||
:class="[`bg-${props?.color}`]"
|
||||
></div>
|
||||
<div class="absolute top-0 left-0 bottom-0 right-0 opacity-20" :class="[`bg-${props?.color}`]"></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@ -47,7 +41,7 @@ const isPositive = controlledComputed(
|
||||
|
||||
<div class="">
|
||||
<h6 class="text-lg text-center font-semibold mt-2 mb-1">
|
||||
{{ props.stats || '-'}}
|
||||
{{ props.stats || '-' }}
|
||||
</h6>
|
||||
<p class="text-sm text-center">
|
||||
{{ props.title }}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { useDashboard } from '@/stores/useDashboard';
|
||||
import { useDashboard } from '@/stores';
|
||||
import { computed } from 'vue';
|
||||
import { Icon } from '@iconify/vue';
|
||||
|
||||
@ -16,12 +16,8 @@ const conf = computed(() => dashboardStore.chains[props.name] || {});
|
||||
const addFavor = (e: Event) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
dashboardStore.favoriteMap[props.name] =
|
||||
!dashboardStore?.favoriteMap?.[props.name];
|
||||
window.localStorage.setItem(
|
||||
'favoriteMap',
|
||||
JSON.stringify(dashboardStore.favoriteMap)
|
||||
);
|
||||
dashboardStore.favoriteMap[props.name] = !dashboardStore?.favoriteMap?.[props.name];
|
||||
window.localStorage.setItem('favoriteMap', JSON.stringify(dashboardStore.favoriteMap));
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
@ -40,8 +36,7 @@ const addFavor = (e: Event) => {
|
||||
class="pl-4 text-xl"
|
||||
:class="{
|
||||
'text-warning': dashboardStore?.favoriteMap?.[props.name],
|
||||
'text-gray-300 dark:text-gray-500':
|
||||
!dashboardStore?.favoriteMap?.[props.name],
|
||||
'text-gray-300 dark:text-gray-500': !dashboardStore?.favoriteMap?.[props.name],
|
||||
}"
|
||||
>
|
||||
<Icon icon="mdi-star" />
|
||||
|
||||
@ -7,26 +7,25 @@ const props = defineProps({
|
||||
css: { type: String },
|
||||
});
|
||||
|
||||
const s = ref(0)
|
||||
|
||||
const s = ref(0);
|
||||
</script>
|
||||
<template>
|
||||
<Countdown
|
||||
v-if="time"
|
||||
:time="time > 0 ? time : 0"
|
||||
v-slot="{ days, hours, minutes, seconds }"
|
||||
class="countdown-container justify-items-center "
|
||||
class="countdown-container justify-items-center"
|
||||
>
|
||||
<span class="text-primary font-bold " :class="css">{{ days }}</span> days
|
||||
<span class="text-primary font-bold" :class="css">{{ hours }}</span> hours
|
||||
<span class="text-primary font-bold" :class="css">{{ days }}</span> days
|
||||
<span class="text-primary font-bold" :class="css">{{ hours }}</span> hours
|
||||
<span class="text-primary font-bold" :class="css">{{ minutes }}</span> minutes
|
||||
<span class="text-primary font-bold w-40" :class="css">
|
||||
<Transition name="slide-up">
|
||||
<span v-if="seconds % 2 === 0" class="countdown">{{ seconds }}</span>
|
||||
<span v-else="seconds % 2 === 1" class="countdown">{{ seconds }}</span>
|
||||
</Transition>
|
||||
</span>
|
||||
<span class="ml-10">seconds</span>
|
||||
<span class="text-primary font-bold w-40" :class="css">
|
||||
<Transition name="slide-up">
|
||||
<span v-if="seconds % 2 === 0" class="countdown">{{ seconds }}</span>
|
||||
<span v-else="seconds % 2 === 1" class="countdown">{{ seconds }}</span>
|
||||
</Transition>
|
||||
</span>
|
||||
<span class="ml-10">seconds</span>
|
||||
</Countdown>
|
||||
</template>
|
||||
|
||||
@ -42,5 +41,4 @@ const s = ref(0)
|
||||
text-align: right;
|
||||
float: right;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@ -2,49 +2,53 @@
|
||||
import { computed, ref } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
total: { type: String },
|
||||
limit: { type: Number },
|
||||
callback: { type: Function, required: true },
|
||||
total: { type: String },
|
||||
limit: { type: Number },
|
||||
callback: { type: Function, required: true },
|
||||
});
|
||||
const current = ref(1)
|
||||
const showSize = 3
|
||||
const current = ref(1);
|
||||
const showSize = 3;
|
||||
const pages = computed(() => {
|
||||
const pages: { color: string, page: number }[] = []
|
||||
const total = Number(props.total || 0)
|
||||
if (total > 0 && props.limit && total > props.limit) {
|
||||
let page = 0
|
||||
while (true) {
|
||||
if (page * props.limit >= total) break
|
||||
page += 1
|
||||
if (total / props.limit > 10 && page > showSize && page < (total / props.limit - showSize + 1)) {
|
||||
if (!(page >= current.value - 1 && page <= current.value + 1)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
pages.push({
|
||||
color: page === current.value ? 'btn-primary' : '',
|
||||
page: page,
|
||||
})
|
||||
const pages: { color: string; page: number }[] = [];
|
||||
const total = Number(props.total || 0);
|
||||
if (total > 0 && props.limit && total > props.limit) {
|
||||
let page = 0;
|
||||
while (true) {
|
||||
if (page * props.limit >= total) break;
|
||||
page += 1;
|
||||
if (total / props.limit > 10 && page > showSize && page < total / props.limit - showSize + 1) {
|
||||
if (!(page >= current.value - 1 && page <= current.value + 1)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pages.push({
|
||||
color: page === current.value ? 'btn-primary' : '',
|
||||
page: page,
|
||||
});
|
||||
}
|
||||
return pages
|
||||
})
|
||||
}
|
||||
return pages;
|
||||
});
|
||||
|
||||
function gotoPage(pageNum: number) {
|
||||
current.value = pageNum
|
||||
props.callback(pageNum)
|
||||
current.value = pageNum;
|
||||
props.callback(pageNum);
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="my-5 text-center">
|
||||
<div v-if="total && limit" class="btn-group">
|
||||
<button v-for="{ page, color } in pages" :key="page"
|
||||
class="btn bg-gray-100 text-gray-500 hover:text-white border-none dark:bg-gray-800 dark:text-white" :class="{
|
||||
'!btn-primary': color === 'btn-primary',
|
||||
}" @click="gotoPage(page)">
|
||||
{{ page }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="my-5 text-center">
|
||||
<div v-if="total && limit" class="btn-group">
|
||||
<button
|
||||
v-for="{ page, color } in pages"
|
||||
:key="page"
|
||||
class="btn bg-gray-100 text-gray-500 hover:text-white border-none dark:bg-gray-800 dark:text-white"
|
||||
:class="{
|
||||
'!btn-primary': color === 'btn-primary',
|
||||
}"
|
||||
@click="gotoPage(page)"
|
||||
>
|
||||
{{ page }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
useBlockchain,
|
||||
useFormatter,
|
||||
useStakingStore,
|
||||
useTxDialog,
|
||||
} from '@/stores';
|
||||
import { useBlockchain, useFormatter, useStakingStore, useTxDialog } from '@/stores';
|
||||
import { select } from '@/components/dynamic/index';
|
||||
import type { PaginatedProposals } from '@/types';
|
||||
import ProposalProcess from './ProposalProcess.vue';
|
||||
@ -39,10 +34,9 @@ const voterStatusMap: Record<string, string> = {
|
||||
|
||||
const proposalInfo = ref();
|
||||
|
||||
function metaItem(metadata: string|undefined): { title: string; summary: string } {
|
||||
return metadata ? JSON.parse(metadata) : {}
|
||||
function metaItem(metadata: string | undefined): { title: string; summary: string } {
|
||||
return metadata ? JSON.parse(metadata) : {};
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="bg-white dark:bg-[#28334e] rounded text-sm">
|
||||
@ -64,21 +58,22 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
:to="`/${chain.chainName}/gov/${item?.proposal_id}`"
|
||||
class="text-main text-base mb-1 block hover:text-indigo-400 truncate"
|
||||
>
|
||||
{{ item?.content?.title || item?.title || metaItem(item?.metadata)?.title }}
|
||||
{{
|
||||
item?.content?.title ||
|
||||
item?.title ||
|
||||
metaItem(item?.metadata)?.title
|
||||
}}
|
||||
</RouterLink>
|
||||
<div
|
||||
v-if="item.content"
|
||||
class="bg-[#f6f2ff] text-[#9c6cff] dark:bg-gray-600 dark:text-gray-300 inline-block rounded-full px-2 py-[1px] text-xs mb-1"
|
||||
>
|
||||
{{ showType(item.content['@type']) }}
|
||||
{{ showType(item.content['@type']) }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="w-60">
|
||||
<ProposalProcess
|
||||
:pool="staking.pool"
|
||||
:tally="item.final_tally_result"
|
||||
></ProposalProcess>
|
||||
<ProposalProcess :pool="staking.pool" :tally="item.final_tally_result"></ProposalProcess>
|
||||
</td>
|
||||
<td class="w-36">
|
||||
<div class="pl-4">
|
||||
@ -138,19 +133,11 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
</table>
|
||||
|
||||
<div class="lg:!hidden">
|
||||
<div
|
||||
v-for="(item, index) in proposals?.proposals"
|
||||
:key="index"
|
||||
class="px-4 py-4"
|
||||
>
|
||||
<div
|
||||
class="text-main text-base mb-1 flex justify-between hover:text-indigo-400"
|
||||
>
|
||||
<RouterLink
|
||||
:to="`/${chain.chainName}/gov/${item?.proposal_id}`"
|
||||
class="flex-1 w-0 truncate mr-4"
|
||||
>{{ item?.content?.title || item?.title || metaItem(item?.metadata)?.title }}</RouterLink
|
||||
>
|
||||
<div v-for="(item, index) in proposals?.proposals" :key="index" class="px-4 py-4">
|
||||
<div class="text-main text-base mb-1 flex justify-between hover:text-indigo-400">
|
||||
<RouterLink :to="`/${chain.chainName}/gov/${item?.proposal_id}`" class="flex-1 w-0 truncate mr-4">{{
|
||||
item?.content?.title || item?.title || metaItem(item?.metadata)?.title
|
||||
}}</RouterLink>
|
||||
<label
|
||||
for="proposal-detail-modal"
|
||||
class="text-main text-base hover:text-indigo-400 cursor-pointer"
|
||||
@ -170,18 +157,13 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="truncate text-xs text-gray-500 dark:text-gray-400 flex items-center justify-end"
|
||||
>
|
||||
<div class="truncate text-xs text-gray-500 dark:text-gray-400 flex items-center justify-end">
|
||||
{{ format.toDay(item.voting_end_time, 'from') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ProposalProcess
|
||||
:pool="staking.pool"
|
||||
:tally="item.final_tally_result"
|
||||
></ProposalProcess>
|
||||
<ProposalProcess :pool="staking.pool" :tally="item.final_tally_result"></ProposalProcess>
|
||||
</div>
|
||||
|
||||
<div class="mt-4" v-if="statusMap?.[item?.status] === 'VOTING'">
|
||||
@ -225,7 +207,6 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
|
||||
<span v-else>Vote</span></label
|
||||
>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -234,17 +215,24 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<input type="checkbox" id="proposal-detail-modal" class="modal-toggle" />
|
||||
<label for="proposal-detail-modal" class="modal">
|
||||
<label class="modal-box !w-11/12 !max-w-5xl" for="">
|
||||
<label
|
||||
for="proposal-detail-modal"
|
||||
class="btn btn-sm btn-circle absolute right-2 top-2"
|
||||
>✕</label
|
||||
>
|
||||
<label for="proposal-detail-modal" class="btn btn-sm btn-circle absolute right-2 top-2">✕</label>
|
||||
<h3 class="font-bold text-lg">Description</h3>
|
||||
<p class="py-4">
|
||||
<Component
|
||||
v-if="proposalInfo?.content?.description || proposalInfo?.summary || metaItem(proposalInfo?.metadata)?.summary"
|
||||
:is="select(proposalInfo?.content?.description || proposalInfo?.summary || metaItem(proposalInfo?.metadata)?.summary, 'horizontal')"
|
||||
:value="proposalInfo?.content?.description || proposalInfo?.summary || metaItem(proposalInfo?.metadata)?.summary"
|
||||
v-if="
|
||||
proposalInfo?.content?.description || proposalInfo?.summary || metaItem(proposalInfo?.metadata)?.summary
|
||||
"
|
||||
:is="
|
||||
select(
|
||||
proposalInfo?.content?.description ||
|
||||
proposalInfo?.summary ||
|
||||
metaItem(proposalInfo?.metadata)?.summary,
|
||||
'horizontal'
|
||||
)
|
||||
"
|
||||
:value="
|
||||
proposalInfo?.content?.description || proposalInfo?.summary || metaItem(proposalInfo?.metadata)?.summary
|
||||
"
|
||||
>
|
||||
</Component>
|
||||
</p>
|
||||
|
||||
@ -15,41 +15,21 @@ const props = defineProps({
|
||||
});
|
||||
const total = computed(() => props.pool?.bonded_tokens);
|
||||
const format = useFormatter();
|
||||
const yes = computed(() =>
|
||||
format.calculatePercent(props.tally?.yes, total.value)
|
||||
);
|
||||
const no = computed(() =>
|
||||
format.calculatePercent(props.tally?.no, total.value)
|
||||
);
|
||||
const abstain = computed(() =>
|
||||
format.calculatePercent(props.tally?.abstain, total.value)
|
||||
);
|
||||
const veto = computed(() =>
|
||||
format.calculatePercent(props.tally?.no_with_veto, total.value)
|
||||
);
|
||||
const yes = computed(() => format.calculatePercent(props.tally?.yes, total.value));
|
||||
const no = computed(() => format.calculatePercent(props.tally?.no, total.value));
|
||||
const abstain = computed(() => format.calculatePercent(props.tally?.abstain, total.value));
|
||||
const veto = computed(() => format.calculatePercent(props.tally?.no_with_veto, total.value));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="progress rounded-[3px] h-6 text-xs flex items-center">
|
||||
<div
|
||||
class="h-6 bg-yes flex items-center pl-2 text-white overflow-hidden"
|
||||
:style="`width: ${yes}`"
|
||||
:title="yes"
|
||||
>
|
||||
<div class="h-6 bg-yes flex items-center pl-2 text-white overflow-hidden" :style="`width: ${yes}`" :title="yes">
|
||||
{{ yes }}
|
||||
</div>
|
||||
<div
|
||||
class="h-6 bg-no flex items-center text-white overflow-hidden"
|
||||
:style="`width: ${no}`"
|
||||
:title="no"
|
||||
>
|
||||
<div class="h-6 bg-no flex items-center text-white overflow-hidden" :style="`width: ${no}`" :title="no">
|
||||
{{ no }}
|
||||
</div>
|
||||
<div
|
||||
class="h-6 bg-[#B71C1C] flex items-center text-white overflow-hidden"
|
||||
:style="`width: ${veto};`"
|
||||
:title="veto"
|
||||
>
|
||||
<div class="h-6 bg-[#B71C1C] flex items-center text-white overflow-hidden" :style="`width: ${veto};`" :title="veto">
|
||||
{{ veto }}
|
||||
</div>
|
||||
<div
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import { useTxDialog, useBlockchain } from '@/stores';
|
||||
const store = useTxDialog();
|
||||
const chainStore = useBlockchain()
|
||||
const chainStore = useBlockchain();
|
||||
</script>
|
||||
<template>
|
||||
<ping-tx-dialog
|
||||
:type="store.type"
|
||||
:sender="store.sender"
|
||||
:endpoint="store.endpoint"
|
||||
:params='store.params'
|
||||
:params="store.params"
|
||||
:hd-path="store.hdPaths"
|
||||
:registry-name="chainStore.current?.prettyName || chainStore.chainName"
|
||||
@view="store.view"
|
||||
|
||||
@ -2,24 +2,15 @@
|
||||
import type { PropType } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
blocks: { type: Array as PropType<{height:string, color: string}[]> },
|
||||
blocks: { type: Array as PropType<{ height: string; color: string }[]> },
|
||||
});
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex gap-0.5">
|
||||
<div class="cursor-default" v-for="(item, index) in blocks" :key="index">
|
||||
<div class="tooltip"
|
||||
:data-tip="item.height"
|
||||
:class="item.color"
|
||||
style="width: 3px;"
|
||||
>
|
||||
|
||||
</div>
|
||||
<div class="tooltip" :data-tip="item.height" :class="item.color" style="width: 3px"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
<style></style>
|
||||
|
||||
@ -5,142 +5,127 @@ import { useFormatter } from '@/stores';
|
||||
import type { CommissionRate } from '@/types';
|
||||
|
||||
const props = defineProps({
|
||||
commission: { type: Object as PropType<CommissionRate> },
|
||||
commission: { type: Object as PropType<CommissionRate> },
|
||||
});
|
||||
|
||||
let rate = computed(
|
||||
() => Number(props.commission?.commission_rates.rate || 0) * 100
|
||||
);
|
||||
let change = computed(
|
||||
() => Number(props.commission?.commission_rates.max_change_rate || 0) * 100
|
||||
);
|
||||
let max = computed(
|
||||
() => Number(props.commission?.commission_rates.max_rate || 1) * 100
|
||||
);
|
||||
let rate = computed(() => Number(props.commission?.commission_rates.rate || 0) * 100);
|
||||
let change = computed(() => Number(props.commission?.commission_rates.max_change_rate || 0) * 100);
|
||||
let max = computed(() => Number(props.commission?.commission_rates.max_rate || 1) * 100);
|
||||
|
||||
const left = rate;
|
||||
const right = computed(() => max.value - rate.value);
|
||||
|
||||
const s1 = computed(() =>
|
||||
left.value > change.value ? left.value - change.value : 0
|
||||
);
|
||||
const s2 = computed(() =>
|
||||
left.value > change.value ? change.value : left.value
|
||||
);
|
||||
const s1 = computed(() => (left.value > change.value ? left.value - change.value : 0));
|
||||
const s2 = computed(() => (left.value > change.value ? change.value : left.value));
|
||||
const s3 = 2;
|
||||
const s4 = computed(() =>
|
||||
right.value > change.value ? change.value : right.value
|
||||
);
|
||||
const s5 = computed(() =>
|
||||
right.value > change.value ? right.value - change.value : 0
|
||||
);
|
||||
const s4 = computed(() => (right.value > change.value ? change.value : right.value));
|
||||
const s5 = computed(() => (right.value > change.value ? right.value - change.value : 0));
|
||||
|
||||
const series = computed(() => [s1.value, s2.value, s3, s4.value, s5.value]);
|
||||
|
||||
const format = useFormatter();
|
||||
|
||||
const chartConfig = computed(() => {
|
||||
const secondaryText = `hsl(var(--bc))`;
|
||||
const primaryText = `hsl(var(--bc))`;
|
||||
|
||||
const secondaryText = `hsl(var(--bc))`;
|
||||
const primaryText = `hsl(var(--bc))`;
|
||||
|
||||
return {
|
||||
chart: {
|
||||
width: '200px',
|
||||
sparkline: { enabled: false },
|
||||
},
|
||||
colors: ['rgba(109,120,141,0.2)', 'rgba(114,225,40,0.2)', 'rgba(114,225,40,1)', 'rgba(114,225,40,0.2)', 'rgba(109,120,141,0.2)'],
|
||||
legend: { show: false },
|
||||
tooltip: { enabled: false },
|
||||
dataLabels: { enabled: false },
|
||||
stroke: {
|
||||
width: 3,
|
||||
lineCap: 'round',
|
||||
colors: ['hsl(var(--b1))'],
|
||||
},
|
||||
labels: [
|
||||
'Available',
|
||||
'Daily Change',
|
||||
'Commission Rate',
|
||||
'Daily Change',
|
||||
'Available',
|
||||
],
|
||||
states: {
|
||||
hover: {
|
||||
filter: { type: 'none' },
|
||||
return {
|
||||
chart: {
|
||||
width: '200px',
|
||||
sparkline: { enabled: false },
|
||||
},
|
||||
colors: [
|
||||
'rgba(109,120,141,0.2)',
|
||||
'rgba(114,225,40,0.2)',
|
||||
'rgba(114,225,40,1)',
|
||||
'rgba(114,225,40,0.2)',
|
||||
'rgba(109,120,141,0.2)',
|
||||
],
|
||||
legend: { show: false },
|
||||
tooltip: { enabled: false },
|
||||
dataLabels: { enabled: false },
|
||||
stroke: {
|
||||
width: 3,
|
||||
lineCap: 'round',
|
||||
colors: ['hsl(var(--b1))'],
|
||||
},
|
||||
labels: ['Available', 'Daily Change', 'Commission Rate', 'Daily Change', 'Available'],
|
||||
states: {
|
||||
hover: {
|
||||
filter: { type: 'none' },
|
||||
},
|
||||
active: {
|
||||
filter: { type: 'none' },
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
endAngle: 130,
|
||||
startAngle: -130,
|
||||
customScale: 0.9,
|
||||
donut: {
|
||||
size: '83%',
|
||||
labels: {
|
||||
show: true,
|
||||
name: {
|
||||
offsetY: 25,
|
||||
fontSize: '1rem',
|
||||
color: secondaryText,
|
||||
},
|
||||
active: {
|
||||
filter: { type: 'none' },
|
||||
value: {
|
||||
offsetY: -15,
|
||||
fontWeight: 500,
|
||||
fontSize: '2.125rem',
|
||||
formatter: (value: unknown) => `${rate.value.toFixed(1)}%`,
|
||||
color: primaryText,
|
||||
},
|
||||
total: {
|
||||
show: true,
|
||||
label: 'Commission Rate',
|
||||
fontSize: '1rem',
|
||||
color: secondaryText,
|
||||
formatter: () => `${rate.value.toFixed(1)}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
endAngle: 130,
|
||||
startAngle: -130,
|
||||
customScale: 0.9,
|
||||
donut: {
|
||||
size: '83%',
|
||||
labels: {
|
||||
show: true,
|
||||
name: {
|
||||
offsetY: 25,
|
||||
fontSize: '1rem',
|
||||
color: secondaryText,
|
||||
},
|
||||
value: {
|
||||
offsetY: -15,
|
||||
fontWeight: 500,
|
||||
fontSize: '2.125rem',
|
||||
formatter: (value: unknown) => `${rate.value.toFixed(1)}%`,
|
||||
color: primaryText,
|
||||
},
|
||||
total: {
|
||||
show: true,
|
||||
label: 'Commission Rate',
|
||||
fontSize: '1rem',
|
||||
color: secondaryText,
|
||||
formatter: () => `${rate.value.toFixed(1)}%`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1709,
|
||||
options: {
|
||||
chart: { height: 237 },
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1709,
|
||||
options: {
|
||||
chart: { height: 237 },
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="bg-base-100 rounded shadow p-4">
|
||||
<div class="text-lg text-main font-semibold mb-1">Commission Rate</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ `Updated at ${format.toDay(props.commission?.update_time, 'short')}` }}
|
||||
</div>
|
||||
<div class="w-80 m-auto">
|
||||
<ApexCharts type="donut" :options="chartConfig" :series="series" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center justify-center flex-wrap gap-x-3">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-success w-[6px] h-[6px] rounded-full"></div>
|
||||
<span class="text-caption">Rate:{{ rate.toFixed(0) }}%</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-success w-[6px] h-[6px] rounded-full opacity-60"></div>
|
||||
<span class="text-caption">24h: ±{{ change }}%</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-secondary w-[6px] h-[6px] rounded-full"></div>
|
||||
<span class="text-caption">Max:{{ max }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-base-100 rounded shadow p-4">
|
||||
<div class="text-lg text-main font-semibold mb-1">Commission Rate</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ `Updated at ${format.toDay(props.commission?.update_time, 'short')}` }}
|
||||
</div>
|
||||
<div class="w-80 m-auto">
|
||||
<ApexCharts type="donut" :options="chartConfig" :series="series" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-center justify-center flex-wrap gap-x-3">
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-success w-[6px] h-[6px] rounded-full"></div>
|
||||
<span class="text-caption">Rate:{{ rate.toFixed(0) }}%</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-success w-[6px] h-[6px] rounded-full opacity-60"></div>
|
||||
<span class="text-caption">24h: ±{{ change }}%</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-x-2">
|
||||
<div class="bg-secondary w-[6px] h-[6px] rounded-full"></div>
|
||||
<span class="text-caption">Max:{{ max }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { get, post } from "@/libs/http"
|
||||
import { useBaseStore, useTxDialog } from "@/stores";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import TextElement from "@/components/dynamic/TextElement.vue";
|
||||
import { get, post } from '@/libs/http';
|
||||
import { useBaseStore, useTxDialog } from '@/stores';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import TextElement from '@/components/dynamic/TextElement.vue';
|
||||
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
|
||||
import { codeToHtml } from 'shiki'
|
||||
import { useWasmStore } from "@/modules/[chain]/cosmwasm/WasmStore";
|
||||
import { toBase64 } from "@cosmjs/encoding";
|
||||
import { codeToHtml } from 'shiki';
|
||||
import { useWasmStore } from '@/modules/[chain]/cosmwasm/WasmStore';
|
||||
import { toBase64 } from '@cosmjs/encoding';
|
||||
|
||||
import { JsonViewer } from "vue3-json-viewer"
|
||||
import { CosmjsOfflineSigner } from "@leapwallet/cosmos-snap-provider";
|
||||
import { JsonViewer } from 'vue3-json-viewer';
|
||||
import { CosmjsOfflineSigner } from '@leapwallet/cosmos-snap-provider';
|
||||
|
||||
interface Verification {
|
||||
chainId?: string;
|
||||
@ -29,13 +29,13 @@ interface SourceCode<T> {
|
||||
interface Argument {
|
||||
format?: string;
|
||||
type: string;
|
||||
properties: Record<string, Argument>
|
||||
properties: Record<string, Argument>;
|
||||
}
|
||||
|
||||
interface Method {
|
||||
type: string;
|
||||
required: string[];
|
||||
properties: Record<string, Argument>
|
||||
properties: Record<string, Argument>;
|
||||
additionalProperties: boolean;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ const props = defineProps({
|
||||
contract: { type: String },
|
||||
});
|
||||
|
||||
const baseurl = "https://prod.compiler.welldonestudio.io"
|
||||
const baseurl = 'https://prod.compiler.welldonestudio.io';
|
||||
|
||||
const verification = ref<Verification>({});
|
||||
const sourceCode = ref<SourceCode<string>[]>([]);
|
||||
@ -59,206 +59,244 @@ const baseStore = useBaseStore();
|
||||
const dialog = useTxDialog();
|
||||
const result = ref<Record<string, any>>({});
|
||||
|
||||
|
||||
function fetchVerification() {
|
||||
const url = `${baseurl}/deploy-histories/${chain_id.value}?contract=${props.contract}`
|
||||
get(url).then((x) => {
|
||||
console.log("verification:", x)
|
||||
verification.value = x
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
const url = `${baseurl}/deploy-histories/${chain_id.value}?contract=${props.contract}`;
|
||||
get(url)
|
||||
.then((x) => {
|
||||
console.log('verification:', x);
|
||||
verification.value = x;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
function fetchSchema() {
|
||||
const base = useBaseStore()
|
||||
const chainId = base.latest?.block?.header?.chain_id || "neutron-1"
|
||||
const url = `${baseurl}/schemas/${chainId}?contract=${props.contract}`
|
||||
get(url).then(async (x) => {
|
||||
console.log("schema:", x)
|
||||
schemas.value = x.sourceCodes
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
|
||||
function fetchSourceCode() {
|
||||
const base = useBaseStore()
|
||||
const chainId = base.latest?.block?.header?.chain_id || "neutron-1"
|
||||
const url = `${baseurl}/source-codes/${chainId}?contract=${props.contract}`
|
||||
const theme = baseStore.theme === 'dark' ? 'material-theme' : 'github-light'
|
||||
get(url).then(async (x) => {
|
||||
console.log("source codes:", x)
|
||||
for (let i = 0; i < x.sourceCodes.length; i++) {
|
||||
const sc = x.sourceCodes[i];
|
||||
sc.sourceCode = await codeToHtml(sc.sourceCode, {lang: sc.path.endsWith('.toml')?'toml':'rust', theme})
|
||||
}
|
||||
sourceCode.value = x.sourceCodes
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
const base = useBaseStore();
|
||||
const chainId = base.latest?.block?.header?.chain_id || 'neutron-1';
|
||||
const url = `${baseurl}/schemas/${chainId}?contract=${props.contract}`;
|
||||
get(url)
|
||||
.then(async (x) => {
|
||||
console.log('schema:', x);
|
||||
schemas.value = x.sourceCodes;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
const base = useBaseStore()
|
||||
const chain_id = ref("")
|
||||
function fetchSourceCode() {
|
||||
const base = useBaseStore();
|
||||
const chainId = base.latest?.block?.header?.chain_id || 'neutron-1';
|
||||
const url = `${baseurl}/source-codes/${chainId}?contract=${props.contract}`;
|
||||
const theme = baseStore.theme === 'dark' ? 'material-theme' : 'github-light';
|
||||
get(url)
|
||||
.then(async (x) => {
|
||||
console.log('source codes:', x);
|
||||
for (let i = 0; i < x.sourceCodes.length; i++) {
|
||||
const sc = x.sourceCodes[i];
|
||||
sc.sourceCode = await codeToHtml(sc.sourceCode, { lang: sc.path.endsWith('.toml') ? 'toml' : 'rust', theme });
|
||||
}
|
||||
sourceCode.value = x.sourceCodes;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
const base = useBaseStore();
|
||||
const chain_id = ref('');
|
||||
base.$subscribe((m, s) => {
|
||||
if (chain_id.value !== s.latest?.block?.header?.chain_id) {
|
||||
chain_id.value = s.latest?.block?.header?.chain_id || "unknown"
|
||||
chain_id.value = s.latest?.block?.header?.chain_id || 'unknown';
|
||||
fetchVerification();
|
||||
fetchSchema();
|
||||
fetchSourceCode();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function verify() {
|
||||
const base = useBaseStore();
|
||||
const id = base.latest?.block?.header?.chain_id || 'unknown';
|
||||
const data = { contractAddress: props.contract, chainId: id };
|
||||
|
||||
const base = useBaseStore()
|
||||
const id = base.latest?.block?.header?.chain_id || "unknown"
|
||||
const data = {"contractAddress": props.contract, "chainId": id}
|
||||
|
||||
post(`${baseurl}/verification/neutron`, data).then((x)=> {
|
||||
if(x.result) {
|
||||
verification.value = x.result
|
||||
fetchSchema();
|
||||
fetchSourceCode();
|
||||
}
|
||||
})
|
||||
post(`${baseurl}/verification/neutron`, data).then((x) => {
|
||||
if (x.result) {
|
||||
verification.value = x.result;
|
||||
fetchSchema();
|
||||
fetchSourceCode();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const tab = ref('verification')
|
||||
const tab = ref('verification');
|
||||
function selectTab(tabName: string) {
|
||||
tab.value = tabName
|
||||
tab.value = tabName;
|
||||
}
|
||||
|
||||
const executions = computed(() => {
|
||||
return schemas.value
|
||||
.filter(x => x.path.indexOf('execute_msg')>-1 || x.path.indexOf('query_msg')>-1)
|
||||
.map(x => JSON.parse(x.sourceCode||"{}") as Schema)
|
||||
.filter((x) => x.path.indexOf('execute_msg') > -1 || x.path.indexOf('query_msg') > -1)
|
||||
.map((x) => JSON.parse(x.sourceCode || '{}') as Schema);
|
||||
// if(raw && raw.sourceCode) {
|
||||
// return JSON.parse(raw.sourceCode) as Schema
|
||||
// }
|
||||
// }
|
||||
// return {} as Schema
|
||||
})
|
||||
});
|
||||
|
||||
const queries = computed(() => {
|
||||
let raw = schemas.value.find(x => x.path.indexOf('query_msg')>-1)
|
||||
if(raw && raw.sourceCode) {
|
||||
return JSON.parse(raw.sourceCode) as Schema
|
||||
}
|
||||
return {} as Schema
|
||||
})
|
||||
let raw = schemas.value.find((x) => x.path.indexOf('query_msg') > -1);
|
||||
if (raw && raw.sourceCode) {
|
||||
return JSON.parse(raw.sourceCode) as Schema;
|
||||
}
|
||||
return {} as Schema;
|
||||
});
|
||||
|
||||
function callFunction(title: string, method: string, arg: Argument) {
|
||||
if(!props.contract) return
|
||||
if (!props.contract) return;
|
||||
|
||||
// console.log("callFunction", title, method, arg)
|
||||
|
||||
let args = {} as Record<string, any>
|
||||
if(arg.properties) Object.keys(arg.properties).forEach(k => {
|
||||
const input = document.querySelector(`input[name="${method}-${k}"]`) as HTMLInputElement
|
||||
if (input) {
|
||||
args[k] = input.value
|
||||
}
|
||||
})
|
||||
let args = {} as Record<string, any>;
|
||||
if (arg.properties)
|
||||
Object.keys(arg.properties).forEach((k) => {
|
||||
const input = document.querySelector(`input[name="${method}-${k}"]`) as HTMLInputElement;
|
||||
if (input) {
|
||||
args[k] = input.value;
|
||||
}
|
||||
});
|
||||
|
||||
//console.log("args", arg.properties, JSON.stringify(args))
|
||||
|
||||
if(title === 'ExecuteMsg') {
|
||||
let execution = {} as Record<string, any>
|
||||
execution[method] = args
|
||||
console.log("execution", execution)
|
||||
dialog.open('wasm_execute_contract', { contract: props.contract, execution})
|
||||
if (title === 'ExecuteMsg') {
|
||||
let execution = {} as Record<string, any>;
|
||||
execution[method] = args;
|
||||
console.log('execution', execution);
|
||||
dialog.open('wasm_execute_contract', { contract: props.contract, execution });
|
||||
} else {
|
||||
// QueryMsg
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractSmartQuery(props.contract, `{"${method}": ${JSON.stringify(args)}}`)
|
||||
.getWasmContractSmartQuery(
|
||||
props.contract,
|
||||
`{"${method}": ${JSON.stringify(args)}}`
|
||||
)
|
||||
.then((x) => {
|
||||
result.value[`${title}-${method}`] = x;
|
||||
result.value[`${title}-${method}`] = x;
|
||||
})
|
||||
.catch((err) => {
|
||||
result.value[`${title}-${method}`] = err;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div role="tablist" class="tabs tabs-boxed">
|
||||
<a role="tab" class="tab tooltip tooltip-right tooltip-success" data-tip="Powered By WELLDONE Studio">
|
||||
<div class="w-8 rounded">
|
||||
<img src="../assets/images/welldone-logo.svg" alt="Powered By WELLDONE Studio"/>
|
||||
</div>
|
||||
</a>
|
||||
<a role="tab" class="tab" :class="{'tab-active': tab==='verification'}" @click="selectTab('verification')">Verification</a>
|
||||
<a role="tab" class="tab" :class="{'tab-active': tab==='executions'}" @click="selectTab('executions')">Functions</a>
|
||||
<a role="tab" class="tab" :class="{'tab-active': tab==='source_code'}" @click="selectTab('source_code')">Source Code</a>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div role="tablist" class="tabs tabs-boxed">
|
||||
<a role="tab" class="tab tooltip tooltip-right tooltip-success" data-tip="Powered By WELLDONE Studio">
|
||||
<div class="w-8 rounded">
|
||||
<img src="../assets/images/welldone-logo.svg" alt="Powered By WELLDONE Studio" />
|
||||
</div>
|
||||
<div class="">
|
||||
<div v-if="tab === 'verification'"><DynamicComponent :value="verification"/></div>
|
||||
<div v-if="tab === 'executions'" class="">
|
||||
<div v-for="{title, oneOf} in executions" class="join join-vertical w-full mt-2">
|
||||
<div v-if="oneOf" v-for="m in oneOf">
|
||||
<div v-for="(props, method) in m.properties" class="collapse collapse-arrow join-item border border-base-300">
|
||||
<input type="radio" name="my-accordion-1" :checked="false"/>
|
||||
<div class="collapse-title font-medium">
|
||||
{{title}}::{{ method }}
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div v-for="(p, name) in props.properties" class="form-control pb-2">
|
||||
<label class="label">
|
||||
<span class="label-text">{{ name }}</span>
|
||||
<span></span>
|
||||
</label>
|
||||
<input :name="`${method}-${name}`" type="text" :placeholder="p.format" class="input input-sm border border-gray-300 dark:border-gray-600 w-full" />
|
||||
</div>
|
||||
<div>
|
||||
<label v-if="title==='ExecuteMsg'" for="wasm_execute_contract" class="btn btn-sm" @click="callFunction(title, method, props)">{{ method }}</label>
|
||||
<label v-else class="btn btn-sm" @click="callFunction(title, method, props)">{{ method }}</label>
|
||||
</div>
|
||||
<div v-if="result[`${title}-${method}`]" class="mt-2">
|
||||
<JsonViewer :value="result[`${title}-${method}`]" :theme="baseStore.theme||'dark'" style="background: transparent;" copyable boxed sort :expand-depth="5"/>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a role="tab" class="tab" :class="{ 'tab-active': tab === 'verification' }" @click="selectTab('verification')"
|
||||
>Verification</a
|
||||
>
|
||||
<a role="tab" class="tab" :class="{ 'tab-active': tab === 'executions' }" @click="selectTab('executions')"
|
||||
>Functions</a
|
||||
>
|
||||
<a role="tab" class="tab" :class="{ 'tab-active': tab === 'source_code' }" @click="selectTab('source_code')"
|
||||
>Source Code</a
|
||||
>
|
||||
</div>
|
||||
<div class="">
|
||||
<div v-if="tab === 'verification'"><DynamicComponent :value="verification" /></div>
|
||||
<div v-if="tab === 'executions'" class="">
|
||||
<div v-for="{ title, oneOf } in executions" class="join join-vertical w-full mt-2">
|
||||
<div v-if="oneOf" v-for="m in oneOf">
|
||||
<div
|
||||
v-for="(props, method) in m.properties"
|
||||
class="collapse collapse-arrow join-item border border-base-300"
|
||||
>
|
||||
<input type="radio" name="my-accordion-1" :checked="false" />
|
||||
<div class="collapse-title font-medium">{{ title }}::{{ method }}</div>
|
||||
<div class="collapse-content">
|
||||
<div v-for="(p, name) in props.properties" class="form-control pb-2">
|
||||
<label class="label">
|
||||
<span class="label-text">{{ name }}</span>
|
||||
<span></span>
|
||||
</label>
|
||||
<input
|
||||
:name="`${method}-${name}`"
|
||||
type="text"
|
||||
:placeholder="p.format"
|
||||
class="input input-sm border border-gray-300 dark:border-gray-600 w-full"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
v-if="title === 'ExecuteMsg'"
|
||||
for="wasm_execute_contract"
|
||||
class="btn btn-sm"
|
||||
@click="callFunction(title, method, props)"
|
||||
>{{ method }}</label
|
||||
>
|
||||
<label v-else class="btn btn-sm" @click="callFunction(title, method, props)">{{ method }}</label>
|
||||
</div>
|
||||
<div v-if="result[`${title}-${method}`]" class="mt-2">
|
||||
<JsonViewer
|
||||
:value="result[`${title}-${method}`]"
|
||||
:theme="baseStore.theme || 'dark'"
|
||||
style="background: transparent"
|
||||
copyable
|
||||
boxed
|
||||
sort
|
||||
:expand-depth="5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="tab === 'source_code'" class="mt-2 join join-vertical w-full">
|
||||
<div v-for="sc in sourceCode.filter(x => !x.isDirectory)" class="collapse collapse-arrow join-item border border-base-300">
|
||||
<input type="radio" name="sourceCodeAccordion" :checked="false"/>
|
||||
<div class="collapse-title font-medium">{{sc.path}}</div>
|
||||
<div class="collapse-content overflow-auto" v-html="sc.sourceCode"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="tab === 'verification'" class="text-center">
|
||||
<div v-if="Object.keys(verification).length == 0" >
|
||||
Haven't found verification
|
||||
</div>
|
||||
<button class="btn btn-primary mt-5" @click="verify" v-show="tab === 'verification'" :disabled="verification.error !== undefined">verify</button>
|
||||
</div>
|
||||
|
||||
<!-- alert-info -->
|
||||
</div>
|
||||
<div v-if="tab === 'source_code'" class="mt-2 join join-vertical w-full">
|
||||
<div
|
||||
class="text-[#00cfe8] bg-[rgba(0,207,232,0.12)] rounded shadow mt-4 alert-info"
|
||||
v-for="sc in sourceCode.filter((x) => !x.isDirectory)"
|
||||
class="collapse collapse-arrow join-item border border-base-300"
|
||||
>
|
||||
<div
|
||||
class="drop-shadow-md px-4 pt-2 pb-2"
|
||||
style="box-shadow: rgba(0, 207, 232, 0.4) 0px 6px 15px -7px"
|
||||
>
|
||||
<h2 class="text-base font-semibold">{{ $t('consensus.tips') }}</h2>
|
||||
</div>
|
||||
<div class="px-4 py-4">
|
||||
<ul style="list-style-type: disc" class="pl-8">
|
||||
<li>
|
||||
{{ $t('cosmwasm.tips_description_1') }}
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://docs.welldonestudio.io/code/verification-api/" target="_blank">Link to Verification API Manual</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<input type="radio" name="sourceCodeAccordion" :checked="false" />
|
||||
<div class="collapse-title font-medium">{{ sc.path }}</div>
|
||||
<div class="collapse-content overflow-auto" v-html="sc.sourceCode"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-show="tab === 'verification'" class="text-center">
|
||||
<div v-if="Object.keys(verification).length == 0">Haven't found verification</div>
|
||||
<button
|
||||
class="btn btn-primary mt-5"
|
||||
@click="verify"
|
||||
v-show="tab === 'verification'"
|
||||
:disabled="verification.error !== undefined"
|
||||
>
|
||||
verify
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- alert-info -->
|
||||
<div class="text-[#00cfe8] bg-[rgba(0,207,232,0.12)] rounded shadow mt-4 alert-info">
|
||||
<div class="drop-shadow-md px-4 pt-2 pb-2" style="box-shadow: rgba(0, 207, 232, 0.4) 0px 6px 15px -7px">
|
||||
<h2 class="text-base font-semibold">{{ $t('consensus.tips') }}</h2>
|
||||
</div>
|
||||
<div class="px-4 py-4">
|
||||
<ul style="list-style-type: disc" class="pl-8">
|
||||
<li>
|
||||
{{ $t('cosmwasm.tips_description_1') }}
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://docs.welldonestudio.io/code/verification-api/" target="_blank"
|
||||
>Link to Verification API Manual</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,30 +1,17 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { getClient, getUnit } from './ad';
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
const route = useRoute();
|
||||
|
||||
const props = defineProps({
|
||||
id: { type: String, required: true},
|
||||
unit: { type: String, required: true},
|
||||
width: { type: String },
|
||||
height: { type: String },
|
||||
});
|
||||
|
||||
const show = ref(false)
|
||||
onMounted(() => {
|
||||
const adClient = getClient();
|
||||
const adUnitId = getUnit(props.unit);
|
||||
show.value = adClient !== undefined && adUnitId !== undefined;
|
||||
|
||||
if(show.value) {
|
||||
adClient.showBannerAd({
|
||||
adUnitId,
|
||||
containerId: props.id,
|
||||
});
|
||||
const aa = computed(() => {
|
||||
const hostname = location.hostname;
|
||||
if (hostname === 'testnet.ping.pub') {
|
||||
return '2396360';
|
||||
} else if (hostname === 'ping.pub') {
|
||||
return '2395639';
|
||||
} else {
|
||||
return '2396360';
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div v-show="show" :id="id" :unit="unit" class="grid justify-items-center overflow-auto pt-4">
|
||||
</div>
|
||||
</template>
|
||||
<template></template>
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
import { PersonaAdSDK } from '@personaxyz/ad-sdk';
|
||||
|
||||
interface ADConfig {
|
||||
apiKey: string;
|
||||
environment: string;
|
||||
}
|
||||
|
||||
export const confs: Record<string, ADConfig> = {
|
||||
"localhost": {
|
||||
apiKey: 'XXXX_api_key_staging_XXXX', // An actual API key is generated once you register an app with us.
|
||||
environment: 'staging', // use value 'production' when going live
|
||||
},
|
||||
"ping.pub": {
|
||||
apiKey: 'persona-pub-0x6ca028de83d9bc438bb3fd7f9620f36b',
|
||||
environment: 'production',
|
||||
},
|
||||
"testnet.ping.pub": {
|
||||
apiKey: 'persona-pub-0x14e9ba8ca5a658ba409fc0059ebc3711',
|
||||
environment: 'production',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const UNITS: Record<string, Record<string, string>> = {
|
||||
"localhost": {
|
||||
"banner": "3a094192-4c7b-4761-a50c-bd9b6a67e987",
|
||||
"banner_mobile": "e6b82a11-6a94-46c0-a9d2-cf730159a5e6",
|
||||
"popup": "e6b82a11-6a94-46c0-a9d2-cf730159a5e6"
|
||||
},
|
||||
"ping.pub": {
|
||||
"banner": "6883877a-ccae-4a08-b457-7e30b3465a8c",
|
||||
"banner_mobile": "a95c6b55-5e2a-49eb-8993-a488513b2d10",
|
||||
},
|
||||
"testnet.ping.pub": {
|
||||
"banner": "1644951b-5022-4544-8a85-11aef8a8f645",
|
||||
"banner_mobile": "81e0527f-475a-42a4-bb9a-ed9967c5d06f",
|
||||
"popup": "bd77a47c-30fc-4592-9d37-616d4f66964d",
|
||||
"popup_mobile": "bd77a47c-30fc-4592-9d37-616d4f66964d"
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
export function getClient() {
|
||||
const conf = confs[location.hostname]
|
||||
if(conf) {
|
||||
const sdk = new PersonaAdSDK(conf)
|
||||
return sdk.getClient()
|
||||
}
|
||||
}
|
||||
|
||||
export function getUnit(ad: string): string | undefined {
|
||||
const ads = UNITS[location.hostname]
|
||||
if(ads) {
|
||||
return window.innerWidth > 600 ? ads[ad] : ads[`${ad}_mobile`]
|
||||
}
|
||||
}
|
||||
@ -15,16 +15,5 @@ const expenseRationChartConfig = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ApexCharts
|
||||
type="donut"
|
||||
height="410"
|
||||
:options="expenseRationChartConfig"
|
||||
:series="series"
|
||||
/>
|
||||
<ApexCharts type="donut" height="410" :options="expenseRationChartConfig" :series="series" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'DonetChart',
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -8,51 +8,44 @@ import { useBaseStore } from '@/stores';
|
||||
const store = useIndexModule();
|
||||
const baseStore = useBaseStore();
|
||||
const chartConfig = computed(() => {
|
||||
const theme = baseStore.theme;
|
||||
const labels = store.marketData.prices.map((item: any) => item[0]);
|
||||
return getMarketPriceChartConfig(theme, labels);
|
||||
const theme = baseStore.theme;
|
||||
const labels = store.marketData.prices.map((item: any) => item[0]);
|
||||
return getMarketPriceChartConfig(theme, labels);
|
||||
});
|
||||
const kind = ref('price');
|
||||
const series = computed(() => {
|
||||
return [
|
||||
{
|
||||
name: 'Price',
|
||||
data:
|
||||
kind.value === 'price'
|
||||
? store.marketData.prices.map((item: any) => item[1])
|
||||
: store.marketData.total_volumes.map(
|
||||
(item: any) => item[1]
|
||||
),
|
||||
},
|
||||
];
|
||||
return [
|
||||
{
|
||||
name: kind.value === 'price' ? 'Price' : 'Volume',
|
||||
data:
|
||||
kind.value === 'price'
|
||||
? store.marketData.prices.map((item: any) => item[1])
|
||||
: store.marketData.total_volumes.map((item: any) => item[1]),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
function changeChart(type: string) {
|
||||
kind.value = type;
|
||||
kind.value = type;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tabs tabs-boxed bg-transparent justify-end">
|
||||
<a
|
||||
class="tab text-xs mr-2 text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': kind === 'price' }"
|
||||
@click="changeChart('price')"
|
||||
>
|
||||
Price
|
||||
</a>
|
||||
<a
|
||||
class="tab text-xs text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': kind === 'volume' }"
|
||||
@click="changeChart('volume')"
|
||||
>
|
||||
Volume
|
||||
</a>
|
||||
</div>
|
||||
<ApexCharts
|
||||
type="area"
|
||||
height="230"
|
||||
:options="chartConfig"
|
||||
:series="series"
|
||||
/>
|
||||
<div class="tabs tabs-boxed bg-transparent justify-end">
|
||||
<a
|
||||
class="tab text-xs mr-2 text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': kind === 'price' }"
|
||||
@click="changeChart('price')"
|
||||
>
|
||||
Price
|
||||
</a>
|
||||
<a
|
||||
class="tab text-xs text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': kind === 'volume' }"
|
||||
@click="changeChart('volume')"
|
||||
>
|
||||
Volume
|
||||
</a>
|
||||
</div>
|
||||
<ApexCharts type="area" height="230" :options="chartConfig" :series="series" />
|
||||
</template>
|
||||
|
||||
@ -6,39 +6,44 @@ import { useBaseStore } from '@/stores';
|
||||
const baseStore = useBaseStore();
|
||||
|
||||
const options = computed(() => {
|
||||
return {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 150
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
// borderRadius: 4,
|
||||
horizontal: false,
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
colors: ['#5A67D8'],
|
||||
xaxis: {
|
||||
labels: {
|
||||
show: false,
|
||||
rotate: -45
|
||||
},
|
||||
show: false,
|
||||
categories: baseStore.recents.map((x) => x.block.header.height).concat(Array(50-baseStore.recents.length).fill('')),
|
||||
}
|
||||
};
|
||||
return {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 150,
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
// borderRadius: 4,
|
||||
horizontal: false,
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
colors: ['#5A67D8'],
|
||||
xaxis: {
|
||||
labels: {
|
||||
show: false,
|
||||
rotate: -45,
|
||||
},
|
||||
show: false,
|
||||
categories: baseStore.recents
|
||||
.slice(0, 50)
|
||||
.map((x) => x.block.header.height)
|
||||
.concat(Array(Math.max(0, 50)).fill('')),
|
||||
},
|
||||
};
|
||||
});
|
||||
const series = computed(() => {
|
||||
return [{
|
||||
name: 'Txs',
|
||||
data: baseStore.recents?.map((x) => x.block.data.txs.length) || []
|
||||
}]
|
||||
return [
|
||||
{
|
||||
name: 'Txs',
|
||||
data: baseStore.recents?.map((x) => x.block.data.txs.length) || [],
|
||||
},
|
||||
];
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ApexCharts type="bar" height="150" :options="options" :series="series" />
|
||||
<ApexCharts type="bar" height="150" :options="options" :series="series" />
|
||||
</template>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useBlockchain } from '@/stores';
|
||||
import numeral from 'numeral';
|
||||
|
||||
const chainStore = useBlockchain()
|
||||
const chainStore = useBlockchain();
|
||||
|
||||
const themeColors = (theme: string) => {
|
||||
if (theme === 'light') {
|
||||
@ -183,12 +183,8 @@ export const colorVariables = (theme: string) => {
|
||||
};
|
||||
};
|
||||
/// Price Chart config
|
||||
export const getMarketPriceChartConfig = (
|
||||
theme: string,
|
||||
categories: string[]
|
||||
) => {
|
||||
const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } =
|
||||
colorVariables(theme);
|
||||
export const getMarketPriceChartConfig = (theme: string, categories: string[]) => {
|
||||
const { themeSecondaryTextColor, themeBorderColor, themeDisabledTextColor } = colorVariables(theme);
|
||||
|
||||
return {
|
||||
chart: {
|
||||
@ -260,16 +256,56 @@ export const getMarketPriceChartConfig = (
|
||||
};
|
||||
|
||||
// const donutColors = Array.from({length: 19}, () => (`#${Math.floor(Math.random()*16777215+100000).toString(16)}`))
|
||||
const donutColors = ["#bbe81a", "#ff5f0b", "#43ebef", "#1999e5", "#230b2c", "#628be8", "#aa5343", "#c9fa89", "#e88ea8", "#72e4a2", "#38cd87", "#515e13", "#7bf8f5", "#83dd6e", "#e8b203", "#7d11d5", "#3e4927", "#f303e2", "#249493", "#50e5e6", "#11deb2", "#a2f9c7", "#2a7bdc", "#47383a", "#226da4", "#966319", "#1bdf99", "#f3ab0c", "#961f50", "#832efd", "#875287", "#4bebe7", "#1d3d2e", "#9caea4", "#2772f5", "#938bf1", "#6228a5", "#24fea5", "#c9bbc8", "#e27225", "#54bd9f", "#babb2d", "#bcf591", "#803b36", "#124f03"]
|
||||
const donutColors = [
|
||||
'#bbe81a',
|
||||
'#ff5f0b',
|
||||
'#43ebef',
|
||||
'#1999e5',
|
||||
'#230b2c',
|
||||
'#628be8',
|
||||
'#aa5343',
|
||||
'#c9fa89',
|
||||
'#e88ea8',
|
||||
'#72e4a2',
|
||||
'#38cd87',
|
||||
'#515e13',
|
||||
'#7bf8f5',
|
||||
'#83dd6e',
|
||||
'#e8b203',
|
||||
'#7d11d5',
|
||||
'#3e4927',
|
||||
'#f303e2',
|
||||
'#249493',
|
||||
'#50e5e6',
|
||||
'#11deb2',
|
||||
'#a2f9c7',
|
||||
'#2a7bdc',
|
||||
'#47383a',
|
||||
'#226da4',
|
||||
'#966319',
|
||||
'#1bdf99',
|
||||
'#f3ab0c',
|
||||
'#961f50',
|
||||
'#832efd',
|
||||
'#875287',
|
||||
'#4bebe7',
|
||||
'#1d3d2e',
|
||||
'#9caea4',
|
||||
'#2772f5',
|
||||
'#938bf1',
|
||||
'#6228a5',
|
||||
'#24fea5',
|
||||
'#c9bbc8',
|
||||
'#e27225',
|
||||
'#54bd9f',
|
||||
'#babb2d',
|
||||
'#bcf591',
|
||||
'#803b36',
|
||||
'#124f03',
|
||||
];
|
||||
|
||||
|
||||
export const getDonutChartConfig = (
|
||||
theme: string,
|
||||
labels: string[]
|
||||
) => {
|
||||
|
||||
const { themeSecondaryTextColor, themePrimaryTextColor } =
|
||||
colorVariables(theme);
|
||||
export const getDonutChartConfig = (theme: string, labels: string[]) => {
|
||||
const { themeSecondaryTextColor, themePrimaryTextColor } = colorVariables(theme);
|
||||
|
||||
return {
|
||||
stroke: { width: 0 },
|
||||
@ -359,4 +395,3 @@ export const getDonutChartConfig = (
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ const props = defineProps({
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="(item,index) of props.value" :key="index">
|
||||
<div v-for="(item, index) of props.value" :key="index">
|
||||
{{ toBase64(item) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -3,7 +3,7 @@ import ArrayBytesElement from './ArrayBytesElement.vue';
|
||||
import ArrayObjectElement from './ArrayObjectElement.vue';
|
||||
import TextElement from './TextElement.vue';
|
||||
import ArrayCoinElement from './ArrayCoinElement.vue';
|
||||
import ArrayStringElement from './ArrayStringElement.vue'
|
||||
import ArrayStringElement from './ArrayStringElement.vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: { type: Array<Object> },
|
||||
|
||||
@ -18,15 +18,11 @@ const header = computed(() => {
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="overflow-auto max-h-96 ">
|
||||
<div class="overflow-auto max-h-96">
|
||||
<table class="table table-xs table-compact table-pin-rows w-full">
|
||||
<thead v-if="thead">
|
||||
<tr>
|
||||
<th
|
||||
v-for="(item, index) in header"
|
||||
:key="index"
|
||||
class="text-left capitalize"
|
||||
>
|
||||
<th v-for="(item, index) in header" :key="index" class="text-left capitalize">
|
||||
{{ item.replace(/_/g, ' ') }}
|
||||
</th>
|
||||
</tr>
|
||||
|
||||
@ -8,18 +8,12 @@ const props = defineProps(['value']);
|
||||
<table class="table table-compact w-full text-sm">
|
||||
<tbody>
|
||||
<tr v-for="(v, k) of value">
|
||||
<td
|
||||
class="capitalize whitespace-break-spaces min-w-max"
|
||||
>
|
||||
<td class="capitalize whitespace-break-spaces min-w-max">
|
||||
{{ String(k).replaceAll('_', ' ') }}
|
||||
</td>
|
||||
<td class="w-4/5">
|
||||
<div class="overflow-hidden w-auto whitespace-normal" >
|
||||
<Component
|
||||
v-if="v"
|
||||
:is="select(v, 'horizontal')"
|
||||
:value="v"
|
||||
></Component>
|
||||
<div class="overflow-hidden w-auto whitespace-normal">
|
||||
<Component v-if="v" :is="select(v, 'horizontal')" :value="v"></Component>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -3,90 +3,115 @@ import { isBech32Address } from '@/libs/utils';
|
||||
import { useBlockchain, useFormatter } from '@/stores';
|
||||
import MdEditor from 'md-editor-v3';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import nameMatcha from '@leapwallet/name-matcha'
|
||||
|
||||
import { fromBase64, toHex } from '@cosmjs/encoding';
|
||||
|
||||
const chainStore = useBlockchain()
|
||||
import { registry as nameMatcha } from '@leapwallet/name-matcha';
|
||||
|
||||
const chainStore = useBlockchain();
|
||||
const props = defineProps(['value']);
|
||||
const format = useFormatter();
|
||||
function isMD() {
|
||||
if (
|
||||
props.value &&
|
||||
(String(props.value).indexOf('\n') > -1 || String(props.value).indexOf('\\n') > -1)
|
||||
) {
|
||||
if (props.value && (String(props.value).indexOf('\n') > -1 || String(props.value).indexOf('\\n') > -1)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isAddress() {
|
||||
return isBech32Address(props.value) && String(props.value).indexOf('valoper1') === -1
|
||||
return isBech32Address(props.value) && String(props.value).indexOf('valoper1') === -1;
|
||||
}
|
||||
|
||||
const text = computed(() => {
|
||||
if(!props.value) return ""
|
||||
const v = String(props.value)
|
||||
switch(true) {
|
||||
case v.length === 28 && v.endsWith("="): {
|
||||
return format.validator(v) || v
|
||||
if (!props.value) return '';
|
||||
const v = String(props.value);
|
||||
switch (true) {
|
||||
case v.length === 28 && v.endsWith('='): {
|
||||
return format.validator(v) || v;
|
||||
}
|
||||
// 2023-06-12T03:09:38.253756368Z
|
||||
case v.search(/^[1-9]\d{3}-\d{1,2}-\d{1,2}T\d{1,2}:\d{2}:\d{2}[.\d]*Z$/g) > -1: {
|
||||
return new Date(v).toLocaleString(navigator.language)
|
||||
return new Date(v).toLocaleString(navigator.language);
|
||||
}
|
||||
case toHexOutput.value:
|
||||
return toHex(fromBase64(v)).toUpperCase()
|
||||
return toHex(fromBase64(v)).toUpperCase();
|
||||
}
|
||||
return v
|
||||
})
|
||||
return v;
|
||||
});
|
||||
|
||||
const names = ref([] as {name?: string | null, provider?: string}[])
|
||||
const names = ref([] as { name?: any; provider?: string }[]);
|
||||
|
||||
onMounted(() => {
|
||||
if(isAddress()) nameMatcha.lookupAll(props.value).then(re => {
|
||||
names.value = Object.keys(re).map(key => ({name: re[key], provider: key})).filter( x => x.name)
|
||||
})
|
||||
})
|
||||
const toHexOutput = ref(false)
|
||||
if (isAddress())
|
||||
nameMatcha.lookupAll(props.value).then((re) => {
|
||||
names.value = Object.keys(re)
|
||||
.map((key) => ({ name: re[key], provider: key }))
|
||||
.filter((x) => x.name);
|
||||
});
|
||||
});
|
||||
const toHexOutput = ref(false);
|
||||
const isConvertable = computed(() => {
|
||||
return String(props.value).endsWith('=') && props.value.length !== 28
|
||||
})
|
||||
|
||||
return String(props.value).endsWith('=') && props.value.length !== 28;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<MdEditor
|
||||
v-if="isMD()"
|
||||
:model-value="format.multiLine(value)"
|
||||
previewOnly
|
||||
class="md-editor-recover"
|
||||
></MdEditor>
|
||||
<MdEditor v-if="isMD()" :model-value="format.multiLine(value)" previewOnly class="md-editor-recover"></MdEditor>
|
||||
<span v-else-if="isAddress()" class="flex">
|
||||
<RouterLink :to="`/${chainStore.chainName}/account/${text}`">{{ text }}</RouterLink>
|
||||
<div v-for="{name, provider} in names">
|
||||
<span class="text-xs truncate relative py-1 px-2 p2-4 w-fit ml-2 rounded text-success tooltip" :data-tip="provider" :title="provider">
|
||||
<div v-for="{ name, provider } in names">
|
||||
<span
|
||||
class="text-xs truncate relative py-1 px-2 p2-4 w-fit ml-2 rounded text-success tooltip"
|
||||
:data-tip="provider"
|
||||
:title="provider"
|
||||
>
|
||||
<span class="inset-x-0 inset-y-0 opacity-10 absolute bg-success"></span>
|
||||
<button>{{ name }}</button>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<span v-else class="flex"><span class="break-words max-w-5xl">{{ text }}</span>
|
||||
</span>
|
||||
<span v-else class="flex"
|
||||
><span class="break-words max-w-5xl">{{ text }}</span>
|
||||
<span v-if="isConvertable" @click="toHexOutput = !toHexOutput" class="ml-2 cursor-pointer">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.md-editor-recover {
|
||||
.h1,h1 {
|
||||
.h1,
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
.h2,h2 {
|
||||
.h2,
|
||||
h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
|
||||
.h1,
|
||||
.h2,
|
||||
.h3,
|
||||
.h4,
|
||||
.h5,
|
||||
.h6,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 500;
|
||||
@ -101,10 +126,15 @@ const isConvertable = computed(() => {
|
||||
margin-inline-end: 0px;
|
||||
padding-inline-start: 40px;
|
||||
}
|
||||
dl, ol, ul {
|
||||
dl,
|
||||
ol,
|
||||
ul {
|
||||
margin-top: 0;
|
||||
}
|
||||
address, dl, ol, ul {
|
||||
address,
|
||||
dl,
|
||||
ol,
|
||||
ul {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
p {
|
||||
@ -112,11 +142,21 @@ const isConvertable = computed(() => {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
a {
|
||||
color: #666cff !important;
|
||||
color: #666cff !important;
|
||||
}
|
||||
.h1 > a, .h2> a, .h3> a, .h4> a, .h5> a, .h6> a, h1> a, h2> a, h3> a, h4> a, h5> a, h6> a {
|
||||
.h1 > a,
|
||||
.h2 > a,
|
||||
.h3 > a,
|
||||
.h4 > a,
|
||||
.h5 > a,
|
||||
.h6 > a,
|
||||
h1 > a,
|
||||
h2 > a,
|
||||
h3 > a,
|
||||
h4 > a,
|
||||
h5 > a,
|
||||
h6 > a {
|
||||
color: inherit !important;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -1,10 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
|
||||
const props = defineProps(['value']);
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class="overflow-auto">
|
||||
{{ value["amount"] }} {{ value["denom"] }}
|
||||
</div>
|
||||
<div class="overflow-auto">{{ value['amount'] }} {{ value['denom'] }}</div>
|
||||
</template>
|
||||
|
||||
@ -9,10 +9,23 @@ const props = defineProps({
|
||||
});
|
||||
|
||||
const txs = computed(() => {
|
||||
return props.value?.map((x) => ({
|
||||
hash: hashTx(fromBase64(x)),
|
||||
tx: decodeTxRaw(fromBase64(x)),
|
||||
})) || []
|
||||
return (
|
||||
props.value?.map((x) => {
|
||||
const tx_bytes = fromBase64(x);
|
||||
let tx = null;
|
||||
let injected = 'Standard';
|
||||
try {
|
||||
tx = decodeTxRaw(fromBase64(x));
|
||||
} catch (e) {
|
||||
injected = 'Injected';
|
||||
}
|
||||
return {
|
||||
hash: hashTx(tx_bytes),
|
||||
tx,
|
||||
injected,
|
||||
};
|
||||
}) || []
|
||||
);
|
||||
});
|
||||
|
||||
const format = useFormatter();
|
||||
@ -23,26 +36,29 @@ const chain = useBlockchain();
|
||||
<table class="table w-full" density="compact" v-if="txs.length > 0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="position: relative; z-index: 2;">Hash</th>
|
||||
<th>Type</th>
|
||||
<th style="position: relative; z-index: 2">Hash</th>
|
||||
<th>Msgs</th>
|
||||
<th>Memo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm">
|
||||
<tr v-for="item in txs">
|
||||
<td>{{ item.injected }}</td>
|
||||
<td>
|
||||
<RouterLink :to="`/${chain.chainName}/tx/${item.hash}`" class="text-primary dark:invert">{{
|
||||
<span v-if="item.injected === 'Injected'">{{ item.hash }}</span>
|
||||
<RouterLink v-else :to="`/${chain.chainName}/tx/${item.hash}`" class="text-primary dark:invert">{{
|
||||
item.hash
|
||||
}}</RouterLink>
|
||||
</td>
|
||||
<td>
|
||||
{{
|
||||
format.messages(
|
||||
item.tx.body.messages.map((x) => ({ '@type': x.typeUrl }))
|
||||
)
|
||||
}}
|
||||
<span v-if="item.tx">
|
||||
{{ format.messages(item.tx.body.messages.map((x) => ({ '@type': x.typeUrl }))) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="item.tx">{{ item.tx.body.memo }}</span>
|
||||
</td>
|
||||
<td>{{ item.tx.body.memo }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@ -13,7 +13,5 @@ function change() {
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<span
|
||||
>{{ text }} <VIcon size="12" icon="mdi-cached" @click="change()"
|
||||
/></span>
|
||||
<span>{{ text }} <VIcon size="12" icon="mdi-cached" @click="change()" /></span>
|
||||
</template>
|
||||
|
||||
@ -31,7 +31,9 @@ function selectObject(v: Object, direct?: string) {
|
||||
return UInt8Array;
|
||||
case Array.isArray(v):
|
||||
return ArrayElement;
|
||||
case v && Object.keys(v).includes('amount') && Object.keys(v).includes('denom'): {
|
||||
case v &&
|
||||
Object.keys(v).includes('amount') &&
|
||||
Object.keys(v).includes('denom'): {
|
||||
return TokenElement;
|
||||
}
|
||||
case direct === 'horizontal':
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
|
||||
/>
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="17"
|
||||
fill="currentColor"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
|
||||
<path
|
||||
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
|
||||
/>
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="18"
|
||||
height="20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
|
||||
/>
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="currentColor"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
|
||||
<path
|
||||
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
|
||||
/>
|
||||
|
||||
@ -1 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" tag="i" class="v-icon notranslate v-theme--light v-icon--size-default iconify iconify--mdi" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M16.36 14c.08-.66.14-1.32.14-2c0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2m-5.15 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 0 1-4.33 3.56M14.34 14H9.66c-.1-.66-.16-1.32-.16-2c0-.68.06-1.35.16-2h4.68c.09.65.16 1.32.16 2c0 .68-.07 1.34-.16 2M12 19.96c-.83-1.2-1.5-2.53-1.91-3.96h3.82c-.41 1.43-1.08 2.76-1.91 3.96M8 8H5.08A7.923 7.923 0 0 1 9.4 4.44C8.8 5.55 8.35 6.75 8 8m-2.92 8H8c.35 1.25.8 2.45 1.4 3.56A8.008 8.008 0 0 1 5.08 16m-.82-2C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2c0 .68.06 1.34.14 2M12 4.03c.83 1.2 1.5 2.54 1.91 3.97h-3.82c.41-1.43 1.08-2.77 1.91-3.97M18.92 8h-2.95a15.65 15.65 0 0 0-1.38-3.56c1.84.63 3.37 1.9 4.33 3.56M12 2C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2Z"></path></svg>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
tag="i"
|
||||
class="v-icon notranslate v-theme--light v-icon--size-default iconify iconify--mdi"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
><path fill="currentColor" d="M16.36 14c.08-.66.14-1.32.14-2c0-.68-.06-1.34-.14-2h3.38c.16.64.26 1.31.26 2s-.1 1.36-.26 2m-5.15 5.56c.6-1.11 1.06-2.31 1.38-3.56h2.95a8.03 8.03 0 0 1-4.33 3.56M14.34 14H9.66c-.1-.66-.16-1.32-.16-2c0-.68.06-1.35.16-2h4.68c.09.65.16 1.32.16 2c0 .68-.07 1.34-.16 2M12 19.96c-.83-1.2-1.5-2.53-1.91-3.96h3.82c-.41 1.43-1.08 2.76-1.91 3.96M8 8H5.08A7.923 7.923 0 0 1 9.4 4.44C8.8 5.55 8.35 6.75 8 8m-2.92 8H8c.35 1.25.8 2.45 1.4 3.56A8.008 8.008 0 0 1 5.08 16m-.82-2C4.1 13.36 4 12.69 4 12s.1-1.36.26-2h3.38c-.08.66-.14 1.32-.14 2c0 .68.06 1.34.14 2M12 4.03c.83 1.2 1.5 2.54 1.91 3.97h-3.82c.41-1.43 1.08-2.77 1.91-3.97M18.92 8h-2.95a15.65 15.65 0 0 0-1.38-3.56c1.84.63 3.37 1.9 4.33 3.56M12 2C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2Z"></path></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1,12 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, resolveComponent, h } from 'vue'
|
||||
import { defineComponent, resolveComponent, h } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const routerView = resolveComponent('router-view');
|
||||
|
||||
return () =>
|
||||
h('div', { class: 'layout-wrapper layout-blank' }, h(routerView));
|
||||
return () => h('div', { class: 'layout-wrapper layout-blank' }, h(routerView));
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useBlockchain, useBaseStore, type Endpoint } from '@/stores';
|
||||
import { useBlockchain, useBaseStore } from '@/stores';
|
||||
import type { Endpoint } from '@/types/chaindata';
|
||||
import { useRouter } from 'vue-router';
|
||||
const chainStore = useBlockchain();
|
||||
const baseStore = useBaseStore();
|
||||
@ -17,45 +18,33 @@ function changeEndpoint(item: Endpoint) {
|
||||
<div class="p-1 relative mr-3 cursor-pointer">
|
||||
<img v-lazy="chainStore.logo" class="w-9 h-9 rounded-full" />
|
||||
<div
|
||||
class="w-2 h-2 rounded-full absolute right-0 bottom-0 shadow" :class="{
|
||||
class="w-2 h-2 rounded-full absolute right-0 bottom-0 shadow"
|
||||
:class="{
|
||||
'bg-success': baseStore.connected,
|
||||
'bg-error': !baseStore.connected
|
||||
'bg-error': !baseStore.connected,
|
||||
}"
|
||||
></div>
|
||||
</div>
|
||||
<div class="flex-1 w-0">
|
||||
<div
|
||||
:key="
|
||||
baseStore.latest?.block?.header?.height ||
|
||||
chainStore.chainName ||
|
||||
''
|
||||
"
|
||||
:key="baseStore.latest?.block?.header?.height || chainStore.chainName || ''"
|
||||
class="capitalize whitespace-nowrap text-base font-semibold text-gray-600 dark:text-gray-200 hidden md:!block"
|
||||
>
|
||||
{{
|
||||
{{
|
||||
baseStore.latest?.block?.header?.height
|
||||
? `#${baseStore.latest.block.header.height}`
|
||||
: chainStore.chainName || ''
|
||||
}} <span class="text-error">{{ baseStore.connected ? '' : 'disconnected' }}</span>
|
||||
: chainStore.chainName || ''
|
||||
}}
|
||||
<span class="text-error">{{ baseStore.connected ? '' : 'disconnected' }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap hidden md:!block"
|
||||
>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap hidden md:!block">
|
||||
{{ chainStore.connErr || chainStore.endpoint.address }}
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<div
|
||||
tabindex="0"
|
||||
class="dropdown-content -left-6 w-80 menu shadow bg-base-200 rounded-box overflow-auto"
|
||||
>
|
||||
<div tabindex="0" class="dropdown-content -left-6 w-80 menu shadow bg-base-200 rounded-box overflow-auto">
|
||||
<!-- rest -->
|
||||
<div
|
||||
class="px-4 py-2 text-sm text-gray-400"
|
||||
v-if="chainStore.current?.endpoints?.rest"
|
||||
>
|
||||
Rest Endpoint
|
||||
</div>
|
||||
<div class="px-4 py-2 text-sm text-gray-400" v-if="chainStore.current?.endpoints?.rest">Rest Endpoint</div>
|
||||
<div
|
||||
v-for="(item, index) in chainStore.current?.endpoints?.rest"
|
||||
class="px-4 py-2 w-full hover:bg-gray-100 dark:hover:bg-[#384059] cursor-pointer"
|
||||
@ -82,14 +71,18 @@ function changeEndpoint(item: Endpoint) {
|
||||
<div class="px-4 py-2 text-sm text-gray-400">Information</div>
|
||||
<div class="w-full">
|
||||
<div class="py-2 px-4">
|
||||
Chain Id: {{ baseStore.latest.block?.header.chain_id && baseStore.connected
|
||||
? baseStore.latest.block.header.chain_id
|
||||
: 'N/A' }}
|
||||
Chain Id:
|
||||
{{
|
||||
baseStore.latest.block?.header.chain_id && baseStore.connected
|
||||
? baseStore.latest.block.header.chain_id
|
||||
: 'N/A'
|
||||
}}
|
||||
</div>
|
||||
<div class="py-2 px-4">
|
||||
Height: {{ baseStore.latest.block?.header.height && baseStore.connected
|
||||
? baseStore.latest.block.header.height
|
||||
: '0' }}
|
||||
Height:
|
||||
{{
|
||||
baseStore.latest.block?.header.height && baseStore.connected ? baseStore.latest.block.header.height : '0'
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- bottom-->
|
||||
|
||||
@ -9,13 +9,20 @@ import NavbarSearch from '@/layouts/components/NavbarSearch.vue';
|
||||
import ChainProfile from '@/layouts/components/ChainProfile.vue';
|
||||
import Sponsors from '@/layouts/components/Sponsors.vue';
|
||||
|
||||
import { NetworkType, useDashboard } from '@/stores/useDashboard';
|
||||
import { useDashboard } from '@/stores/useDashboard';
|
||||
import { NetworkType } from '@/types/chaindata';
|
||||
import { useBaseStore, useBlockchain } from '@/stores';
|
||||
|
||||
import NavBarI18n from './NavBarI18n.vue';
|
||||
import NavBarWallet from './NavBarWallet.vue';
|
||||
import type { NavGroup, NavLink, NavSectionTitle, VerticalNavItems } from '../types';
|
||||
import type {
|
||||
NavGroup,
|
||||
NavLink,
|
||||
NavSectionTitle,
|
||||
VerticalNavItems,
|
||||
} from '../types';
|
||||
import dayjs from 'dayjs';
|
||||
import AdBanner from '@/components/ad/AdBanner.vue';
|
||||
|
||||
const dashboard = useDashboard();
|
||||
dashboard.initial();
|
||||
@ -24,10 +31,10 @@ blockchain.randomSetupEndpoint();
|
||||
const baseStore = useBaseStore();
|
||||
|
||||
const current = ref(''); // the current chain
|
||||
const temp = ref('')
|
||||
const temp = ref('');
|
||||
blockchain.$subscribe((m, s) => {
|
||||
if(current.value ===s.chainName && temp.value != s.endpoint.address) {
|
||||
temp.value = s.endpoint.address
|
||||
if (current.value === s.chainName && temp.value != s.endpoint.address) {
|
||||
temp.value = s.endpoint.address;
|
||||
blockchain.initial();
|
||||
}
|
||||
if (current.value != s.chainName) {
|
||||
@ -47,29 +54,33 @@ const changeOpen = (index: Number) => {
|
||||
const showDiscord = window.location.host.search('ping.pub') > -1;
|
||||
|
||||
function isNavGroup(nav: VerticalNavItems | any): nav is NavGroup {
|
||||
return (<NavGroup>nav).children !== undefined;
|
||||
return (<NavGroup>nav).children !== undefined;
|
||||
}
|
||||
function isNavLink(nav: VerticalNavItems | any): nav is NavLink {
|
||||
return (<NavLink>nav).to !== undefined;
|
||||
return (<NavLink>nav).to !== undefined;
|
||||
}
|
||||
function isNavTitle(nav: VerticalNavItems | any): nav is NavSectionTitle {
|
||||
return (<NavSectionTitle>nav).heading !== undefined;
|
||||
return (<NavSectionTitle>nav).heading !== undefined;
|
||||
}
|
||||
function selected(route: any, nav: NavLink) {
|
||||
const b = route.path === nav.to?.path || route.path.startsWith(nav.to?.path) && nav.title.indexOf('dashboard') === -1
|
||||
return b
|
||||
const b =
|
||||
route.path === nav.to?.path || (route.path.startsWith(nav.to?.path) && nav.title.indexOf('dashboard') === -1);
|
||||
return b;
|
||||
}
|
||||
const blocktime = computed(() => {
|
||||
return dayjs(baseStore.latest?.block?.header?.time)
|
||||
return dayjs(baseStore.latest?.block?.header?.time);
|
||||
});
|
||||
|
||||
const behind = computed(() => {
|
||||
const current = dayjs().subtract(10, 'minute')
|
||||
return blocktime.value.isBefore(current)
|
||||
const current = dayjs().subtract(10, 'minute');
|
||||
return blocktime.value.isBefore(current);
|
||||
});
|
||||
|
||||
dayjs()
|
||||
dayjs();
|
||||
|
||||
const show_ad = computed(() => {
|
||||
return location.hostname.indexOf('ping.pub') > -1;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -83,7 +94,7 @@ dayjs()
|
||||
<RouterLink to="/" class="flex items-center">
|
||||
<img class="w-10 h-10" src="../../assets/logo.svg" />
|
||||
<h1 class="flex-1 ml-3 text-2xl font-semibold dark:text-white">
|
||||
Ping.pub
|
||||
Zenith Explorer
|
||||
</h1>
|
||||
</RouterLink>
|
||||
<div
|
||||
@ -93,27 +104,18 @@ dayjs()
|
||||
<Icon icon="mdi-close" class="text-2xl" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(item, index) of blockchain.computedChainMenu"
|
||||
:key="index"
|
||||
class="px-2"
|
||||
>
|
||||
<div v-for="(item, index) of blockchain.computedChainMenu" :key="index" class="px-2">
|
||||
<div
|
||||
v-if="isNavGroup(item)"
|
||||
:tabindex="index"
|
||||
class="collapse"
|
||||
:class="{
|
||||
'collapse-arrow':index > 0 && item?.children?.length > 0,
|
||||
'collapse-arrow': index > 0 && item?.children?.length > 0,
|
||||
'collapse-open': index === 0 && sidebarOpen,
|
||||
'collapse-close': index === 0 && !sidebarOpen,
|
||||
}"
|
||||
>
|
||||
<input
|
||||
v-if="index > 0"
|
||||
type="checkbox"
|
||||
class="cursor-pointer !h-10 block"
|
||||
@click="changeOpen(index)"
|
||||
/>
|
||||
<input v-if="index > 0" type="checkbox" class="cursor-pointer !h-10 block" @click="changeOpen(index)" />
|
||||
<div
|
||||
class="collapse-title !py-0 px-4 flex items-center cursor-pointer hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
@ -126,14 +128,8 @@ dayjs()
|
||||
'text-blue-500': item?.title !== 'Favorite',
|
||||
}"
|
||||
/>
|
||||
<img
|
||||
v-if="item?.icon?.image"
|
||||
:src="item?.icon?.image"
|
||||
class="w-6 h-6 rounded-full mr-3"
|
||||
/>
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-700 dark:text-gray-200 whitespace-nowrap"
|
||||
>
|
||||
<img v-if="item?.icon?.image" :src="item?.icon?.image" class="w-6 h-6 rounded-full mr-3" />
|
||||
<div class="text-base capitalize flex-1 text-gray-700 dark:text-gray-200 whitespace-nowrap">
|
||||
{{ item?.title }}
|
||||
</div>
|
||||
<div
|
||||
@ -144,7 +140,7 @@ dayjs()
|
||||
{{ item?.badgeContent }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div class="collapse-content">
|
||||
<div v-for="(el, key) of item?.children" class="menu bg-base-100 w-full !p-0">
|
||||
<RouterLink
|
||||
v-if="isNavLink(el)"
|
||||
@ -160,17 +156,16 @@ dayjs()
|
||||
icon="mdi:chevron-right"
|
||||
class="mr-2 ml-3"
|
||||
:class="{
|
||||
'text-white':
|
||||
$route.path === el?.to?.path &&
|
||||
item?.title !== 'Favorite',
|
||||
'text-white': $route.path === el?.to?.path && item?.title !== 'Favorite',
|
||||
}"
|
||||
/>
|
||||
<img
|
||||
v-if="el?.icon?.image"
|
||||
:src="el?.icon?.image"
|
||||
class="w-6 h-6 rounded-full mr-3 ml-4 " :class="{
|
||||
'border border-gray-300 bg-white': selected($route, el),
|
||||
}"
|
||||
class="w-6 h-6 rounded-full mr-3 ml-4"
|
||||
:class="{
|
||||
'border border-gray-300 bg-white': selected($route, el),
|
||||
}"
|
||||
/>
|
||||
<div
|
||||
class="text-base capitalize text-gray-500 dark:text-gray-300"
|
||||
@ -182,24 +177,17 @@ dayjs()
|
||||
</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div v-if="index === 0 && dashboard.networkType === NetworkType.Testnet" class="menu bg-base-100 w-full !p-0">
|
||||
<RouterLink
|
||||
class="hover:bg-gray-100 dark:hover:bg-[#373f59] rounded cursor-pointer px-3 py-2 flex items-center"
|
||||
:to="`/${blockchain.chainName}/faucet`">
|
||||
<Icon
|
||||
icon="mdi:chevron-right"
|
||||
class="mr-2 ml-3"
|
||||
></Icon>
|
||||
<div
|
||||
class="text-base capitalize text-gray-500 dark:text-gray-300"
|
||||
>
|
||||
Faucet
|
||||
</div>
|
||||
<div
|
||||
class="badge badge-sm text-white border-none badge-error ml-auto"
|
||||
>
|
||||
New
|
||||
</div>
|
||||
<div
|
||||
v-if="index === 0 && dashboard.networkType === NetworkType.Testnet"
|
||||
class="menu bg-base-100 w-full !p-0"
|
||||
>
|
||||
<RouterLink
|
||||
class="hover:bg-gray-100 dark:hover:bg-[#373f59] rounded cursor-pointer px-3 py-2 flex items-center"
|
||||
:to="`/${blockchain.chainName}/faucet`"
|
||||
>
|
||||
<Icon icon="mdi:chevron-right" class="mr-2 ml-3"></Icon>
|
||||
<div class="text-base capitalize text-gray-500 dark:text-gray-300">Faucet</div>
|
||||
<div class="badge badge-sm text-white border-none badge-error ml-auto">New</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
@ -225,14 +213,12 @@ dayjs()
|
||||
:src="item?.icon?.image"
|
||||
class="w-6 h-6 rounded-full mr-3 border border-blue-100"
|
||||
/>
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-700 dark:text-gray-200 whitespace-nowrap"
|
||||
>
|
||||
<div class="text-base capitalize flex-1 text-gray-700 dark:text-gray-200 whitespace-nowrap">
|
||||
{{ item?.title }}
|
||||
</div>
|
||||
<div
|
||||
v-if="item?.badgeContent"
|
||||
class="badge badge-sm text-white border-none"
|
||||
class="badge badge-sm text-white border-none"
|
||||
:class="item?.badgeClass"
|
||||
>
|
||||
{{ item?.badgeContent }}
|
||||
@ -246,35 +232,31 @@ dayjs()
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-2">
|
||||
<div class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">
|
||||
Tools
|
||||
</div>
|
||||
<RouterLink to="/wallet/suggest"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
<div class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">
|
||||
Tools
|
||||
</div>
|
||||
<RouterLink to="/wallet/suggest"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<Icon icon="mdi:frequently-asked-questions" class="text-xl mr-2" />
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200"
|
||||
>
|
||||
<Icon icon="mdi:frequently-asked-questions" class="text-xl mr-2" />
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200"
|
||||
>
|
||||
Wallet Helper
|
||||
</div>
|
||||
</RouterLink>
|
||||
<div class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">
|
||||
Wallet Helper
|
||||
</div>
|
||||
</RouterLink>
|
||||
<!-- <div class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">
|
||||
{{ $t('module.sponsors') }}
|
||||
</div>
|
||||
<Sponsors />
|
||||
<div class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">{{ $t('module.links') }}</div>
|
||||
<Sponsors /> -->
|
||||
<!-- <div class="px-4 text-sm pt-2 text-gray-400 pb-2 uppercase">{{ $t('module.links') }}</div>
|
||||
<a
|
||||
href="https://twitter.com/ping_pub"
|
||||
href="https://x.com/laconicnetwork"
|
||||
target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<Icon icon="mdi:twitter" class="text-xl mr-2" />
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200"
|
||||
>
|
||||
Twitter
|
||||
</div>
|
||||
<div class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200">Twitter</div>
|
||||
</a>
|
||||
<a
|
||||
v-if="showDiscord"
|
||||
@ -288,19 +270,7 @@ dayjs()
|
||||
>
|
||||
Discord
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/ping-pub/explorer/discussions"
|
||||
target="_blank"
|
||||
class="py-2 px-4 flex items-center rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<Icon icon="mdi:frequently-asked-questions" class="text-xl mr-2" />
|
||||
<div
|
||||
class="text-base capitalize flex-1 text-gray-600 dark:text-gray-200"
|
||||
>
|
||||
FAQ
|
||||
</div>
|
||||
</a>
|
||||
</a> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="xl:!ml-64 px-3 pt-4">
|
||||
@ -327,20 +297,35 @@ dayjs()
|
||||
</div>
|
||||
|
||||
<!-- 👉 Pages -->
|
||||
<div style="min-height: calc(100vh - 180px);">
|
||||
<div v-if="behind" class="alert alert-error mb-4">
|
||||
<div class="flex gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
class="stroke-current flex-shrink-0 w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
||||
</svg>
|
||||
<span>{{ $t('pages.out_of_sync') }} {{ blocktime.format() }} ({{ blocktime.fromNow() }})</span>
|
||||
</div>
|
||||
<div style="min-height: calc(100vh - 180px)">
|
||||
<div v-if="behind" class="alert alert-error mb-4">
|
||||
<div class="flex gap-2">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
class="stroke-current flex-shrink-0 w-6 h-6"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
></path>
|
||||
</svg>
|
||||
<span
|
||||
>{{ $t('pages.out_of_sync') }} {{ blocktime.format() }} ({{
|
||||
blocktime.fromNow()
|
||||
}})</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<RouterView v-slot="{ Component }">
|
||||
<Transition mode="out-in">
|
||||
<Component :is="Component" />
|
||||
<div>
|
||||
<AdBanner v-if="show_ad" />
|
||||
<Component :is="Component" />
|
||||
</div>
|
||||
</Transition>
|
||||
</RouterView>
|
||||
</div>
|
||||
|
||||
@ -4,65 +4,68 @@ import { Icon } from '@iconify/vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const i18nLangs: Array<{ label: string; i18nLang: string }> = [
|
||||
{
|
||||
label: 'English',
|
||||
i18nLang: 'en',
|
||||
},
|
||||
{
|
||||
label: '中文',
|
||||
i18nLang: 'cn',
|
||||
},
|
||||
{
|
||||
label: 'Indonesian',
|
||||
i18nLang: 'id',
|
||||
},
|
||||
{
|
||||
label: 'English',
|
||||
i18nLang: 'en',
|
||||
},
|
||||
{
|
||||
label: '中文',
|
||||
i18nLang: 'zh',
|
||||
},
|
||||
{
|
||||
label: 'Indonesian',
|
||||
i18nLang: 'id',
|
||||
},
|
||||
{
|
||||
label: '日本語',
|
||||
i18nLang: 'ja',
|
||||
},
|
||||
{
|
||||
label: '한국인',
|
||||
i18nLang: 'ko',
|
||||
},
|
||||
{
|
||||
label: 'Deutsch',
|
||||
i18nLang: 'de',
|
||||
},
|
||||
{
|
||||
label: 'Español',
|
||||
i18nLang: 'es',
|
||||
},
|
||||
];
|
||||
|
||||
let locale = ref(useI18n({ useScope: 'global' }).locale);
|
||||
watch(locale, (val) => {
|
||||
document.documentElement.setAttribute('lang', val as string);
|
||||
document.documentElement.setAttribute('lang', val as string);
|
||||
});
|
||||
|
||||
let currentLang = ref(localStorage.getItem('lang') || 'en');
|
||||
|
||||
watch(currentLang, (val: string) => {
|
||||
document.documentElement.setAttribute('lang', val as string);
|
||||
document.documentElement.setAttribute('lang', val as string);
|
||||
});
|
||||
|
||||
const handleLangChange = (lang: string) => {
|
||||
locale.value = lang;
|
||||
currentLang.value = lang;
|
||||
localStorage.setItem('lang', lang);
|
||||
locale.value = lang;
|
||||
currentLang.value = lang;
|
||||
localStorage.setItem('lang', lang);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="dropdown"
|
||||
:class="
|
||||
currentLang === 'ar'
|
||||
? 'dropdown-right'
|
||||
: 'dropdown-bottom dropdown-end'
|
||||
"
|
||||
>
|
||||
<label tabindex="0" class="btn btn-ghost btn-circle btn-sm mx-1">
|
||||
<Icon
|
||||
icon="mdi-translate"
|
||||
class="text-2xl text-gray-500 dark:text-gray-400"
|
||||
/>
|
||||
</label>
|
||||
<ul
|
||||
tabindex="0"
|
||||
class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-40"
|
||||
<div class="dropdown" :class="currentLang === 'ar' ? 'dropdown-right' : 'dropdown-bottom dropdown-end'">
|
||||
<label tabindex="0" class="btn btn-ghost btn-circle btn-sm mx-1">
|
||||
<Icon icon="mdi-translate" class="text-2xl text-gray-500 dark:text-gray-400" />
|
||||
</label>
|
||||
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-40">
|
||||
<li v-for="lang in i18nLangs" :key="lang.i18nLang">
|
||||
<a
|
||||
class="hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
:class="{ 'text-primary': currentLang === lang.i18nLang }"
|
||||
@click="handleLangChange(lang.i18nLang)"
|
||||
>{{ lang.label }}</a
|
||||
>
|
||||
<li v-for="lang in i18nLangs" :key="lang.i18nLang">
|
||||
<a
|
||||
class="hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
:class="{ 'text-primary': currentLang === lang.i18nLang }"
|
||||
@click="handleLangChange(lang.i18nLang)"
|
||||
>{{ lang.label }}</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useBaseStore, useBlockchain, useWalletStore } from '@/stores';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { ref, computed } from 'vue';
|
||||
@ -39,44 +39,66 @@ const params = computed(() => {
|
||||
if (chainStore.chainName == 'side') {
|
||||
return JSON.stringify({
|
||||
wallet: ['okex', 'unisat'],
|
||||
});
|
||||
});
|
||||
}
|
||||
return "";
|
||||
return '';
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="dropdown dropdown-hover dropdown-end">
|
||||
<label tabindex="0" class="btn btn-sm btn-primary m-1 lowercase truncate !inline-flex text-xs md:!text-sm">
|
||||
<label
|
||||
tabindex="0"
|
||||
class="btn btn-sm btn-primary m-1 lowercase truncate !inline-flex text-xs md:!text-sm"
|
||||
>
|
||||
<Icon icon="mdi:wallet" />
|
||||
<span class="ml-1 hidden md:block">
|
||||
{{ walletStore.shortAddress || 'Wallet' }}</span>
|
||||
<span class="ml-1 hidden md:block"> {{ walletStore.shortAddress || 'Wallet' }}</span>
|
||||
</label>
|
||||
<div tabindex="0" class="dropdown-content menu shadow p-2 bg-base-100 rounded w-52 md:!w-64 overflow-auto">
|
||||
<label v-if="!walletStore?.currentAddress" for="PingConnectWallet" class="btn btn-sm btn-primary">
|
||||
<div
|
||||
tabindex="0"
|
||||
class="dropdown-content menu shadow p-2 bg-base-100 rounded w-52 md:!w-64 overflow-auto"
|
||||
>
|
||||
<label
|
||||
v-if="!walletStore?.currentAddress"
|
||||
for="PingConnectWallet"
|
||||
class="btn btn-sm btn-primary"
|
||||
>
|
||||
<Icon icon="mdi:wallet" /><span class="ml-1 block">Connect Wallet</span>
|
||||
</label>
|
||||
<div class="px-2 mb-1 text-gray-500 dark:text-gray-400 font-semibold">
|
||||
{{ walletStore.connectedWallet?.wallet }}
|
||||
</div>
|
||||
<div>
|
||||
<a v-if="walletStore.currentAddress"
|
||||
<a
|
||||
v-if="walletStore.currentAddress"
|
||||
class="block py-2 px-2 hover:bg-gray-100 dark:hover:bg-[#353f5a] rounded cursor-pointer"
|
||||
style="overflow-wrap: anywhere" @click="copyAdress(walletStore.currentAddress)">
|
||||
style="overflow-wrap: anywhere"
|
||||
@click="copyAdress(walletStore.currentAddress)"
|
||||
>
|
||||
{{ walletStore.currentAddress }}
|
||||
</a>
|
||||
<div class="divider mt-1 mb-1"></div>
|
||||
<RouterLink to="/wallet/accounts">
|
||||
<div class="block py-2 px-2 hover:!bg-gray-100 rounded cursor-pointer">Accounts</div>
|
||||
<div
|
||||
class="block py-2 px-2 hover:!bg-gray-100 rounded cursor-pointer"
|
||||
>
|
||||
Accounts
|
||||
</div>
|
||||
</RouterLink>
|
||||
<RouterLink to="/wallet/portfolio">
|
||||
<div class="block py-2 px-2 hover:!bg-gray-100 rounded cursor-pointer">Portfolio</div>
|
||||
<div
|
||||
class="block py-2 px-2 hover:!bg-gray-100 rounded cursor-pointer"
|
||||
>
|
||||
Portfolio
|
||||
</div>
|
||||
</RouterLink>
|
||||
<div v-if="walletStore.currentAddress" class="divider mt-1 mb-1"></div>
|
||||
<a v-if="walletStore.currentAddress"
|
||||
<a
|
||||
v-if="walletStore.currentAddress"
|
||||
class="block py-2 px-2 hover:bg-gray-100 dark:hover:bg-[#353f5a] rounded cursor-pointer"
|
||||
@click="walletStore.disconnect()">Disconnect</a>
|
||||
@click="walletStore.disconnect()"
|
||||
>Disconnect</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="toast" v-show="showCopyToast === 1">
|
||||
@ -95,9 +117,14 @@ const params = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
<Teleport to="body">
|
||||
<ping-connect-wallet :chain-id="baseStore.currentChainId || 'cosmoshub-4'" :hd-path="chainStore.defaultHDPath"
|
||||
:addr-prefix="chainStore.current?.bech32Prefix || 'cosmos'" @connect="walletStateChange"
|
||||
@keplr-config="walletStore.suggestChain()" :params="params" />
|
||||
<ping-connect-wallet
|
||||
:chain-id="baseStore.currentChainId || 'cosmoshub-4'"
|
||||
:hd-path="chainStore.defaultHDPath"
|
||||
:addr-prefix="chainStore.current?.bech32Prefix || 'cosmos'"
|
||||
@connect="walletStateChange"
|
||||
@keplr-config="walletStore.suggestChain()"
|
||||
:params="params"
|
||||
/>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,37 +1,26 @@
|
||||
<template>
|
||||
<!-- footer -->
|
||||
<footer
|
||||
class="flex items-center h-12 mt-5 text-sm bg-gray-100 dark:bg-[#171d30] py-2 z-10 w-full"
|
||||
>
|
||||
<footer class="flex items-center h-12 mt-5 text-sm bg-gray-100 dark:bg-[#171d30] py-2 z-10 w-full">
|
||||
<div class="flex flex-1">
|
||||
©
|
||||
{{ new Date().getFullYear() }}
|
||||
Made With
|
||||
<img src="../../assets/images/heart.svg" />
|
||||
By
|
||||
<a
|
||||
class="link link-primary no-underline"
|
||||
href="https://ping.pub"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
{{ new Date().getFullYear() }} Made With <img src="../../assets/images/heart.svg" /> By
|
||||
<a class="link link-primary no-underline" href="https://ping.pub" target="_blank" rel="noopener noreferrer"
|
||||
>Ping.pub</a
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="hidden md:!block"
|
||||
>
|
||||
<div class="hidden md:!block">
|
||||
<a
|
||||
class="link link-primary no-underline mr-4"
|
||||
href="https://github.com/ping-pub/explorer/blob/master/LICENSE"
|
||||
href="https://git.vdb.to/cerc-io/cosmos-explorer/src/branch/master/LICENSE"
|
||||
target="noopener noreferrer"
|
||||
>License</a
|
||||
>
|
||||
<a
|
||||
class="link link-primary no-underline"
|
||||
href="https://github.com/ping-pub/explorer"
|
||||
href="https://git.vdb.to/cerc-io/cosmos-explorer"
|
||||
target="noopener noreferrer"
|
||||
>Github</a
|
||||
>Source</a
|
||||
>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@ -61,14 +61,8 @@ function confirm() {
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<button
|
||||
class="btn btn-ghost btn-circle btn-sm mx-1"
|
||||
@click="openSearchModal"
|
||||
>
|
||||
<Icon
|
||||
icon="mdi:magnify"
|
||||
class="text-2xl text-gray-500 dark:text-gray-400"
|
||||
/>
|
||||
<button class="btn btn-ghost btn-circle btn-sm mx-1" @click="openSearchModal">
|
||||
<Icon icon="mdi:magnify" class="text-2xl text-gray-500 dark:text-gray-400" />
|
||||
</button>
|
||||
|
||||
<!-- modal -->
|
||||
@ -77,29 +71,15 @@ function confirm() {
|
||||
class="cursor-pointer modal !pointer-events-auto !opacity-100 !visible"
|
||||
@click="closeSearchModal"
|
||||
>
|
||||
<div
|
||||
class="relative modal-box cursor-default"
|
||||
@click="(event) => preventClick(event)"
|
||||
>
|
||||
<div class="relative modal-box cursor-default" @click="(event) => preventClick(event)">
|
||||
<!-- header -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div
|
||||
class="text-lg font-bold flex flex-col md:!flex-row justify-between items-baseline"
|
||||
>
|
||||
<div class="text-lg font-bold flex flex-col md:!flex-row justify-between items-baseline">
|
||||
<span class="mr-2">Search</span>
|
||||
<span class="capitalize text-sm md:!text-base"
|
||||
>Height/Transaction/Account Address</span
|
||||
>
|
||||
<span class="capitalize text-sm md:!text-base">Height/Transaction/Account Address</span>
|
||||
</div>
|
||||
<label
|
||||
htmlFor="modal-pool-modal"
|
||||
class="cursor-pointer"
|
||||
@click="closeSearchModal"
|
||||
>
|
||||
<Icon
|
||||
icon="zondicons:close-outline"
|
||||
class="text-2xl text-gray-500 dark:text-gray-400"
|
||||
/>
|
||||
<label htmlFor="modal-pool-modal" class="cursor-pointer" @click="closeSearchModal">
|
||||
<Icon icon="zondicons:close-outline" class="text-2xl text-gray-500 dark:text-gray-400" />
|
||||
</label>
|
||||
</div>
|
||||
<!-- body -->
|
||||
@ -110,19 +90,14 @@ function confirm() {
|
||||
v-model="searchQuery"
|
||||
placeholder="Height/Transaction/Account Address"
|
||||
/>
|
||||
<div
|
||||
class="mt-2 text-right text-sm text-error"
|
||||
v-show="errorMessage"
|
||||
>
|
||||
<div class="mt-2 text-right text-sm text-error" v-show="errorMessage">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- foot -->
|
||||
<div class="mt-6">
|
||||
<button class="w-full btn btn-primary" @click="confirm">
|
||||
Confirm
|
||||
</button>
|
||||
<button class="w-full btn btn-primary" @click="confirm">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,45 +4,42 @@ import { onMounted, computed } from 'vue';
|
||||
import { useBaseStore } from '@/stores';
|
||||
|
||||
const themeMap: Record<string, string> = {
|
||||
system: 'mdi-laptop',
|
||||
light: 'mdi-weather-sunny',
|
||||
dark: 'mdi-weather-night',
|
||||
system: 'mdi-laptop',
|
||||
light: 'mdi-weather-sunny',
|
||||
dark: 'mdi-weather-night',
|
||||
};
|
||||
const baseStore = useBaseStore();
|
||||
const theme = computed(() => {
|
||||
return baseStore.theme;
|
||||
return baseStore.theme;
|
||||
});
|
||||
|
||||
const changeMode = (val?: 'dark' | 'light') => {
|
||||
let value: 'dark' | 'light' = 'dark';
|
||||
const currentValue: 'dark' | 'light' = val || theme.value;
|
||||
if (currentValue === 'dark') {
|
||||
value = 'light';
|
||||
}
|
||||
if (value === 'light') {
|
||||
document.documentElement.classList.add('light');
|
||||
document.documentElement.classList.remove('dark');
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
document.documentElement.classList.remove('light');
|
||||
}
|
||||
document.documentElement.setAttribute('data-theme', value);
|
||||
window.localStorage.setItem('theme', value);
|
||||
baseStore.theme = value;
|
||||
let value: 'dark' | 'light' = 'dark';
|
||||
const currentValue: 'dark' | 'light' = val || theme.value;
|
||||
if (currentValue === 'dark') {
|
||||
value = 'light';
|
||||
}
|
||||
if (value === 'light') {
|
||||
document.documentElement.classList.add('light');
|
||||
document.documentElement.classList.remove('dark');
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
document.documentElement.classList.remove('light');
|
||||
}
|
||||
document.documentElement.setAttribute('data-theme', value);
|
||||
window.localStorage.setItem('theme', value);
|
||||
baseStore.theme = value;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
changeMode(theme.value === 'light' ? 'dark' : 'light');
|
||||
changeMode(theme.value === 'light' ? 'dark' : 'light');
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tooltip tooltip-bottom delay-1000">
|
||||
<button
|
||||
class="btn btn-ghost btn-circle btn-sm mx-1"
|
||||
@click="changeMode()"
|
||||
>
|
||||
<Icon :icon="themeMap?.[theme]" class="text-2xl text-gray-500 dark:text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="tooltip tooltip-bottom delay-1000">
|
||||
<button class="btn btn-ghost btn-circle btn-sm mx-1" @click="changeMode()">
|
||||
<Icon :icon="themeMap?.[theme]" class="text-2xl text-gray-500 dark:text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,25 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<a href="https://cosmos.network" target="_blank"
|
||||
<a href="https://www.laconic.com" target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<img src="https://ping.pub/logos/cosmos.svg" class="w-6 h-6 rounded-full mr-3" />
|
||||
<svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.05 12.623A15.378 15.378 0 0 0 8.57 1.714C8.573 1.136 8.54.564 8.477 0H0v16.287c0 1.974.752 3.949 2.258 5.454A7.69 7.69 0 0 0 7.714 24L24 24v-8.477a15.636 15.636 0 0 0-1.715-.095c-4.258 0-8.115 1.73-10.908 4.523-2.032 1.981-5.291 1.982-7.299-.026-2.006-2.006-2.007-5.266-.029-7.302Zm18.192-10.86a6.004 6.004 0 0 0-8.485 0 6.003 6.003 0 0 0 0 8.484 6.003 6.003 0 0 0 8.485 0 6.002 6.002 0 0 0 0-8.485Z" fill="var(--color-white)"></path>
|
||||
</svg>
|
||||
<div class="text-sm capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
Cosmos Hub
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://osmosis.zone" target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<img src="https://ping.pub/logos/osmosis.jpg" class="w-6 h-6 rounded-full mr-3" />
|
||||
<div class="text-sm capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
Osmosis
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://celestia.org" target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]">
|
||||
<img src="https://ping.pub/logos/celestia.png" class="w-6 h-6 rounded-full mr-3" />
|
||||
<div class="text-sm capitalize flex-1 text-gray-600 dark:text-gray-200">
|
||||
Celestia
|
||||
Zenith Network
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
78
src/layouts/types.d.ts
vendored
78
src/layouts/types.d.ts
vendored
@ -1,59 +1,59 @@
|
||||
// 👉 Vertical nav section title
|
||||
export interface NavSectionTitle extends Partial<AclProperties> {
|
||||
heading: string
|
||||
heading: string;
|
||||
}
|
||||
|
||||
// 👉 Vertical nav link
|
||||
declare type ATagTargetAttrValues = '_blank' | '_self' | '_parent' | '_top' | 'framename'
|
||||
declare type ATagTargetAttrValues = '_blank' | '_self' | '_parent' | '_top' | 'framename';
|
||||
declare type ATagRelAttrValues =
|
||||
| 'alternate'
|
||||
| 'author'
|
||||
| 'bookmark'
|
||||
| 'external'
|
||||
| 'help'
|
||||
| 'license'
|
||||
| 'next'
|
||||
| 'nofollow'
|
||||
| 'noopener'
|
||||
| 'noreferrer'
|
||||
| 'prev'
|
||||
| 'search'
|
||||
| 'tag'
|
||||
| 'alternate'
|
||||
| 'author'
|
||||
| 'bookmark'
|
||||
| 'external'
|
||||
| 'help'
|
||||
| 'license'
|
||||
| 'next'
|
||||
| 'nofollow'
|
||||
| 'noopener'
|
||||
| 'noreferrer'
|
||||
| 'prev'
|
||||
| 'search'
|
||||
| 'tag';
|
||||
|
||||
export interface NavLinkProps {
|
||||
to?: RouteLocationRaw | string | null
|
||||
href?: string
|
||||
target?: ATagTargetAttrValues
|
||||
rel?: ATagRelAttrValues
|
||||
i18n?: boolean
|
||||
to?: RouteLocationRaw | string | null;
|
||||
href?: string;
|
||||
target?: ATagTargetAttrValues;
|
||||
rel?: ATagRelAttrValues;
|
||||
i18n?: boolean;
|
||||
}
|
||||
|
||||
export interface Icon {
|
||||
icon?: string,
|
||||
image?: string,
|
||||
size: string,
|
||||
icon?: string;
|
||||
image?: string;
|
||||
size: string;
|
||||
}
|
||||
|
||||
export interface NavLink extends NavLinkProps {
|
||||
title: string
|
||||
icon?: Icon
|
||||
badgeContent?: string | number
|
||||
badgeClass?: string
|
||||
disable?: boolean
|
||||
order?: number
|
||||
title: string;
|
||||
icon?: Icon;
|
||||
badgeContent?: string | number;
|
||||
badgeClass?: string;
|
||||
disable?: boolean;
|
||||
order?: number;
|
||||
}
|
||||
|
||||
// 👉 Vertical nav group
|
||||
export interface NavGroup {
|
||||
title: string
|
||||
icon?: Icon
|
||||
badgeContent?: string | number
|
||||
badgeClass?: string
|
||||
children: (NavLink | NavGroup)[]
|
||||
disable?: boolean
|
||||
order?: number
|
||||
i18n?: boolean
|
||||
title: string;
|
||||
icon?: Icon;
|
||||
badgeContent?: string | number;
|
||||
badgeClass?: string;
|
||||
children: (NavLink | NavGroup)[];
|
||||
disable?: boolean;
|
||||
order?: number;
|
||||
i18n?: boolean;
|
||||
}
|
||||
|
||||
export declare type VerticalNavItems = (NavLink | NavGroup | NavSectionTitle)[]
|
||||
export declare type HorizontalNavItems = (NavLink | NavGroup)[]
|
||||
export declare type VerticalNavItems = (NavLink | NavGroup | NavSectionTitle)[];
|
||||
export declare type HorizontalNavItems = (NavLink | NavGroup)[];
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
fromBase64,
|
||||
fromBech32,
|
||||
toBech32,
|
||||
toHex,
|
||||
} from '@cosmjs/encoding';
|
||||
import { fromBase64, fromBech32, toBase64, toBech32, toHex } from '@cosmjs/encoding';
|
||||
import { Ripemd160, sha256 } from '@cosmjs/crypto';
|
||||
|
||||
export function decodeAddress(address: string) {
|
||||
@ -24,10 +19,7 @@ export function operatorAddressToAccount(operAddress?: string) {
|
||||
return toBech32(prefix.replace('valoper', ''), data);
|
||||
}
|
||||
|
||||
export function consensusPubkeyToHexAddress(consensusPubkey?: {
|
||||
'@type': string;
|
||||
key: string;
|
||||
}) {
|
||||
export function consensusPubkeyToHexAddress(consensusPubkey?: { '@type': string; key: string }) {
|
||||
if (!consensusPubkey) return '';
|
||||
let raw = '';
|
||||
if (consensusPubkey['@type'] === '/cosmos.crypto.ed25519.PubKey') {
|
||||
@ -42,10 +34,24 @@ export function consensusPubkeyToHexAddress(consensusPubkey?: {
|
||||
return raw;
|
||||
}
|
||||
|
||||
export function pubKeyToValcons(
|
||||
consensusPubkey: { '@type': string; key: string },
|
||||
prefix: string
|
||||
) {
|
||||
// not work as expected, will fix later or remove
|
||||
export function consumerKeyToBase64Address(consumerKey?: Record<string, string>) {
|
||||
if (!consumerKey) return '';
|
||||
let raw = '';
|
||||
if (consumerKey.ed25519) {
|
||||
const pubkey = fromBase64(consumerKey.ed25519);
|
||||
if (pubkey) return toBase64(sha256(pubkey)).slice(0, 40);
|
||||
}
|
||||
|
||||
if (consumerKey.secp256k1) {
|
||||
const pubkey = fromBase64(consumerKey.secp256k1);
|
||||
if (pubkey)
|
||||
return toBase64(new Ripemd160().update(sha256(pubkey)).digest());
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
export function pubKeyToValcons(consensusPubkey: { '@type': string; key: string }, prefix: string) {
|
||||
if (consensusPubkey && consensusPubkey.key) {
|
||||
const pubkey = fromBase64(consensusPubkey.key);
|
||||
if (pubkey) {
|
||||
@ -57,7 +63,7 @@ export function pubKeyToValcons(
|
||||
}
|
||||
|
||||
export function valconsToBase64(address: string) {
|
||||
if (address) return toHex(fromBech32(address).data).toUpperCase();
|
||||
if (address) return toBase64(fromBech32(address).data);
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
0
src/libs/api/customization/README.md
Normal file
0
src/libs/api/customization/README.md
Normal file
71
src/libs/api/customization/atomone.ts
Normal file
71
src/libs/api/customization/atomone.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
// import dayjs from 'dayjs'
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
import type { GovProposal, PaginatedProposals } from '@/types';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'name'; // name or version
|
||||
// Blockchain Name
|
||||
export const name = 'atomone';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0];
|
||||
//p.proposal_id = p.id
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// osmosis custom request
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
// mint_inflation: {
|
||||
// url: `https://public-osmosis-api.numia.xyz/apr?start_date=${new Date(new Date().getTime() - 186400*1000).toISOString().split('T')[0]}&end_date=${new Date().toISOString().split('T')[0]}`,
|
||||
// adapter: async (data: any) => {
|
||||
// const [first] = data
|
||||
// return {inflation: String(Number(first?.apr|| "0")/100.0)}
|
||||
// }
|
||||
// },
|
||||
gov_params_voting: { url: '/atomone/gov/v1beta1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/atomone/gov/v1beta1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/atomone/gov/v1beta1/params/deposit', adapter },
|
||||
gov_proposals: {
|
||||
url: '/atomone/gov/v1beta1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/atomone/gov/v1beta1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
65
src/libs/api/customization/evmos.ts
Normal file
65
src/libs/api/customization/evmos.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import type { RequestRegistry } from '../registry';
|
||||
import { adapter } from '../registry';
|
||||
import type { GovProposal, PaginatedProposals } from '@/types';
|
||||
// which registry is store
|
||||
export const store = 'name'; // name or version
|
||||
// Blockchain Name
|
||||
export const name = 'evmos';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
mint_inflation: {
|
||||
url: '/evmos/inflation/v1/inflation_rate',
|
||||
adapter: async (data: any) => ({ inflation: (Number(data.inflation_rate || 0) / 100).toFixed(2) }),
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
@ -1,24 +1,24 @@
|
||||
import type { RequestRegistry } from '@/libs/registry'
|
||||
import type { GovProposal, PaginatedProposals } from '@/types'
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
import type { GovProposal, PaginatedProposals } from '@/types';
|
||||
import { CosmosRestClient } from '@/libs/client';
|
||||
import { useBlockchain } from '@/stores';
|
||||
import { adapter } from '@/libs/registry'
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
|
||||
// Blockchain Name
|
||||
export const name = 'nolus';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
p.proposal_id = p.id
|
||||
if (p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
};
|
||||
}
|
||||
return p
|
||||
return p;
|
||||
}
|
||||
|
||||
// nolus custom request
|
||||
@ -27,31 +27,32 @@ export const requests: Partial<RequestRegistry> = {
|
||||
url: '/nolus/mint/v1beta1/annual_inflation',
|
||||
adapter: async (data: any): Promise<any> => {
|
||||
try {
|
||||
const client = CosmosRestClient.newDefault(useBlockchain().endpoint.address)
|
||||
const staking = await client.getStakingPool()
|
||||
const client = CosmosRestClient.newDefault(useBlockchain().endpoint.address);
|
||||
const staking = await client.getStakingPool();
|
||||
const inflation = Number(data.annual_inflation) / Number(staking.pool.bonded_tokens) || 0;
|
||||
return { inflation: inflation.toString() };
|
||||
} catch (error) {
|
||||
console.log("Error in adapter:", error);
|
||||
return { inflation: "0" };
|
||||
console.log('Error in adapter:', error);
|
||||
return { inflation: '0' };
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p))
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
@ -61,5 +62,5 @@ export const requests: Partial<RequestRegistry> = {
|
||||
gov_proposals_tally: { url: '/cosmos/gov/v1/proposals/{proposal_id}/tally', adapter },
|
||||
gov_proposals_votes: { url: '/cosmos/gov/v1/proposals/{proposal_id}/votes', adapter },
|
||||
gov_proposals_votes_voter: { url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}', adapter },
|
||||
bank_supply_by_denom: { url: "/cosmos/bank/v1beta1/supply/by_denom?denom={denom}", adapter }
|
||||
bank_supply_by_denom: { url: '/cosmos/bank/v1beta1/supply/by_denom?denom={denom}', adapter },
|
||||
};
|
||||
73
src/libs/api/customization/osmosis.ts
Normal file
73
src/libs/api/customization/osmosis.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
// import dayjs from 'dayjs'
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
import type { GovProposal, PaginatedProposals } from '@/types';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'name'; // name or version
|
||||
// Blockchain Name
|
||||
export const name = 'osmosis';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// osmosis custom request
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
mint_inflation: {
|
||||
url: `https://public-osmosis-api.numia.xyz/apr?start_date=${
|
||||
new Date(new Date().getTime() - 186400 * 1000).toISOString().split('T')[0]
|
||||
}&end_date=${new Date().toISOString().split('T')[0]}`,
|
||||
adapter: async (data: any) => {
|
||||
const [first] = data;
|
||||
return { inflation: String(Number(first?.apr || '0') / 100.0) };
|
||||
},
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
70
src/libs/api/customization/v0.46.0.ts
Normal file
70
src/libs/api/customization/v0.46.0.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
import type {
|
||||
GovParams,
|
||||
GovProposal,
|
||||
GovVote,
|
||||
PaginatedProposalDeposit,
|
||||
PaginatedProposalVotes,
|
||||
PaginatedProposals,
|
||||
Tally,
|
||||
} from '@/types/';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'version'; // name or version
|
||||
// Cosmos SDK version
|
||||
export const name = 'v0.46.7';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
71
src/libs/api/customization/v0.50.0.ts
Normal file
71
src/libs/api/customization/v0.50.0.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
import type {
|
||||
GovParams,
|
||||
GovProposal,
|
||||
GovVote,
|
||||
PaginatedProposalDeposit,
|
||||
PaginatedProposalVotes,
|
||||
PaginatedProposals,
|
||||
Tally,
|
||||
} from '@/types/';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'version'; // name or version
|
||||
// Cosmos SDK version
|
||||
export const name = 'v0.50.0';
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
bank_supply_by_denom: { url: '/cosmos/bank/v1beta1/supply/by_denom?denom={denom}', adapter },
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
100
src/libs/api/customization/xion.ts
Normal file
100
src/libs/api/customization/xion.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import type { RequestRegistry } from '@/libs/api/registry';
|
||||
import { adapter } from '@/libs/api/registry';
|
||||
import { CosmosRestClient } from '@/libs/client';
|
||||
import { useBlockchain } from '@/stores';
|
||||
import type { GovProposal, PaginatedProposals } from '@/types/';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'name'; // name or version
|
||||
// Blockchain Name
|
||||
export const name = 'xion';
|
||||
|
||||
export function proposalAdapter(p: any): GovProposal {
|
||||
if (p) {
|
||||
if (p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0];
|
||||
p.proposal_id = p.id;
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
};
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// xion custom request
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
bank_supply_by_denom: {
|
||||
url: '/cosmos/bank/v1beta1/supply/by_denom?denom={denom}',
|
||||
adapter,
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: {
|
||||
url: '/cosmos/gov/v1/proposals',
|
||||
adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p: any) => proposalAdapter(p));
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination,
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{ proposal: GovProposal }> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal),
|
||||
};
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
mint_inflation: {
|
||||
url: '/cosmos/mint/v1beta1/inflation',
|
||||
adapter: async (data: any): Promise<{ inflation: string }> => {
|
||||
try {
|
||||
const client = CosmosRestClient.newDefault(useBlockchain().endpoint.address);
|
||||
|
||||
// Get distribution params to fetch community tax
|
||||
const { params } = await client.getDistributionParams().catch((e) => {
|
||||
console.error('[Xion Adapter] Failed to fetch distribution params:', {
|
||||
error: e instanceof Error ? e.message : e,
|
||||
endpoint: '/distribution/params',
|
||||
});
|
||||
return { params: { community_tax: '0' } };
|
||||
});
|
||||
|
||||
const communityTax = params.community_tax;
|
||||
|
||||
// apr calcuation is inflation * (1 - communityTax)
|
||||
const adjustedInflation = parseFloat(data.inflation) * (1 - parseFloat(communityTax));
|
||||
|
||||
return { inflation: adjustedInflation.toString() };
|
||||
} catch (e) {
|
||||
console.error('[Xion Adapter] Error calculating inflation:', {
|
||||
error: e instanceof Error ? e.message : e,
|
||||
timestamp: new Date().toISOString(),
|
||||
endpoint: useBlockchain().endpoint.address,
|
||||
});
|
||||
return { inflation: '0' };
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -1,7 +1,4 @@
|
||||
import {
|
||||
type RequestRegistry,
|
||||
adapter,
|
||||
} from './registry';
|
||||
import { type RequestRegistry, adapter } from './registry';
|
||||
|
||||
export const DEFAULT: RequestRegistry = {
|
||||
auth_params: { url: '/cosmos/auth/v1beta1/params', adapter },
|
||||
@ -10,7 +7,10 @@ export const DEFAULT: RequestRegistry = {
|
||||
url: '/cosmos/auth/v1beta1/accounts/{address}',
|
||||
adapter,
|
||||
},
|
||||
params: { url: '/cosmos/params/v1beta1/params?subspace={subspace}&key={key}', adapter },
|
||||
params: {
|
||||
url: '/cosmos/params/v1beta1/params?subspace={subspace}&key={key}',
|
||||
adapter,
|
||||
},
|
||||
bank_params: { url: '/cosmos/bank/v1beta1/params', adapter },
|
||||
bank_balances_address: {
|
||||
url: '/cosmos/bank/v1beta1/balances/{address}',
|
||||
@ -195,4 +195,37 @@ export const DEFAULT: RequestRegistry = {
|
||||
url: '/interchain_security/ccv/provider/validator_consumer_addr?provider_address={provider_address}&chain_id={chain_id}',
|
||||
adapter,
|
||||
},
|
||||
interchain_security_provider_opted_in_validators: {
|
||||
url: '/interchain_security/ccv/provider/opted_in_validators/{chain_id}',
|
||||
adapter,
|
||||
},
|
||||
interchain_security_consumer_validators: {
|
||||
url: '/interchain_security/ccv/provider/consumer_validators/{chain_id}',
|
||||
adapter,
|
||||
},
|
||||
|
||||
group_groups: {
|
||||
url: '/cosmos/group/v1/groups',
|
||||
adapter,
|
||||
},
|
||||
group_groups_by_admin: {
|
||||
url: '/cosmos/group/v1/groups_by_admin/{admin}',
|
||||
adapter,
|
||||
},
|
||||
group_groups_by_member: {
|
||||
url: '/cosmos/group/v1/groups_by_member/{address}',
|
||||
adapter,
|
||||
},
|
||||
group_proposal: {
|
||||
url: '/cosmos/group/v1/proposal/{proposal_id}',
|
||||
adapter,
|
||||
},
|
||||
group_proposal_tally: {
|
||||
url: '/cosmos/group/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
group_proposals_by_group_policy: {
|
||||
url: '/cosmos/group/v1/proposals_by_group_policy/{address}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
@ -5,22 +5,19 @@ import type {
|
||||
Coin,
|
||||
ConnectionWithProof,
|
||||
DenomTrace,
|
||||
Group,
|
||||
GroupProposal,
|
||||
GroupTallyResult,
|
||||
NodeInfo,
|
||||
PaginabledAccounts,
|
||||
PaginatedGroupProposals,
|
||||
PaginatedGroups,
|
||||
PaginatedIBCChannels,
|
||||
PaginatedIBCConnections,
|
||||
PaginatedTendermintValidator,
|
||||
} from '@/types';
|
||||
import type {
|
||||
BankParams,
|
||||
PaginatedBalances,
|
||||
PaginatedDenomMetadata,
|
||||
PaginatedSupply,
|
||||
} from '@/types/bank';
|
||||
import type {
|
||||
DistributionParams,
|
||||
PaginatedSlashes,
|
||||
} from '@/types/distribution';
|
||||
import type { BankParams, PaginatedBalances, PaginatedDenomMetadata, PaginatedSupply } from '@/types/bank';
|
||||
import type { DistributionParams, PaginatedSlashes } from '@/types/distribution';
|
||||
import type {
|
||||
GovParams,
|
||||
GovProposal,
|
||||
@ -42,7 +39,7 @@ import type {
|
||||
Validator,
|
||||
} from '@/types/staking';
|
||||
import type { PaginatedTxs, Tx, TxResponse } from '@/types';
|
||||
import semver from 'semver'
|
||||
import semver from 'semver';
|
||||
export interface Request<T> {
|
||||
url: string;
|
||||
adapter: (source: any) => Promise<T>;
|
||||
@ -75,10 +72,10 @@ export interface RequestRegistry extends AbstractRegistry {
|
||||
distribution_community_pool: Request<{ pool: Coin[] }>;
|
||||
distribution_delegator_rewards: Request<{
|
||||
rewards: {
|
||||
validator_address: string,
|
||||
reward: Coin[]
|
||||
}[],
|
||||
total: Coin[]
|
||||
validator_address: string;
|
||||
reward: Coin[];
|
||||
}[];
|
||||
total: Coin[];
|
||||
}>;
|
||||
|
||||
mint_inflation: Request<{ inflation: string }>;
|
||||
@ -90,7 +87,7 @@ export interface RequestRegistry extends AbstractRegistry {
|
||||
}>;
|
||||
mint_annual_provisions: Request<{ annual_provisions: string }>;
|
||||
|
||||
slashing_params: Request<{params: SlashingParam}>;
|
||||
slashing_params: Request<{ params: SlashingParam }>;
|
||||
slashing_signing_info: Request<PaginatedSigningInfo>;
|
||||
|
||||
gov_params_voting: Request<GovParams>;
|
||||
@ -124,7 +121,14 @@ export interface RequestRegistry extends AbstractRegistry {
|
||||
base_tendermint_validatorsets_latest: Request<PaginatedTendermintValidator>;
|
||||
base_tendermint_validatorsets_height: Request<PaginatedTendermintValidator>;
|
||||
|
||||
params: Request<{param: any}>;
|
||||
params: Request<{ param: any }>;
|
||||
|
||||
group_groups: Request<PaginatedGroups>;
|
||||
group_groups_by_admin: Request<PaginatedGroups>;
|
||||
group_groups_by_member: Request<PaginatedGroups>;
|
||||
group_proposal: Request<{ proposal: GroupProposal }>;
|
||||
group_proposal_tally: Request<{ tally: GroupTallyResult }>;
|
||||
group_proposals_by_group_policy: Request<PaginatedGroupProposals>;
|
||||
|
||||
tx_txs: Request<PaginatedTxs>;
|
||||
tx_txs_block: Request<Tx>;
|
||||
@ -151,7 +155,11 @@ export interface RequestRegistry extends AbstractRegistry {
|
||||
ibc_core_connection_connections: Request<PaginatedIBCConnections>;
|
||||
ibc_core_connection_connections_connection_id: Request<ConnectionWithProof>;
|
||||
ibc_core_connection_connections_connection_id_client_state: Request<ClientStateWithProof>;
|
||||
interchain_security_ccv_provider_validator_consumer_addr: Request<{consumer_address: string}>
|
||||
interchain_security_ccv_provider_validator_consumer_addr: Request<{ consumer_address: string }>;
|
||||
interchain_security_provider_opted_in_validators: Request<{ validators_provider_addresses: string[] }>;
|
||||
interchain_security_consumer_validators: Request<{
|
||||
validators: { provider_address: string; consumer_key: { ed25519: string }; power: string }[];
|
||||
}>;
|
||||
}
|
||||
|
||||
export function adapter<T>(source: any): Promise<T> {
|
||||
@ -162,10 +170,7 @@ export interface ApiProfileRegistry {
|
||||
[key: string]: RequestRegistry;
|
||||
}
|
||||
|
||||
export function withCustomRequest<T extends RequestRegistry>(
|
||||
target: T,
|
||||
source?: Partial<T>
|
||||
): T {
|
||||
export function withCustomRequest<T extends RequestRegistry>(target: T, source?: Partial<T>): T {
|
||||
return source ? Object.assign({}, target, source) : target;
|
||||
}
|
||||
|
||||
@ -175,15 +180,13 @@ export const VERSION_REGISTRY: ApiProfileRegistry = {};
|
||||
export const NAME_REGISTRY: ApiProfileRegistry = {};
|
||||
|
||||
export function registryVersionProfile(version: string, requests: RequestRegistry) {
|
||||
VERSION_REGISTRY[version] = requests
|
||||
VERSION_REGISTRY[version] = requests;
|
||||
}
|
||||
|
||||
export function registryChainProfile(version: string, requests: RequestRegistry) {
|
||||
NAME_REGISTRY[version] = requests
|
||||
NAME_REGISTRY[version] = requests;
|
||||
}
|
||||
export function findApiProfileByChain(
|
||||
name: string,
|
||||
): RequestRegistry {
|
||||
export function findApiProfileByChain(name: string): RequestRegistry {
|
||||
const url = NAME_REGISTRY[name];
|
||||
// if (!url) {
|
||||
// throw new Error(`Unsupported version or name: ${name}`);
|
||||
@ -191,15 +194,12 @@ export function findApiProfileByChain(
|
||||
return url;
|
||||
}
|
||||
|
||||
export function findApiProfileBySDKVersion(
|
||||
version: string,
|
||||
): RequestRegistry | undefined {
|
||||
export function findApiProfileBySDKVersion(version: string): RequestRegistry | undefined {
|
||||
let closestVersion: string | null = null;
|
||||
|
||||
const chain_version = version.match(/(\d+\.\d+\.?\d*)/g) || [''];
|
||||
for (const k in VERSION_REGISTRY) {
|
||||
const key = k.replace('v', "")
|
||||
// console.log(semver.gt(key, version), semver.gte(version, key), key, version)
|
||||
if (semver.lte(key, version)) {
|
||||
const key = k.replace('v', '');
|
||||
if (semver.lte(key, chain_version[0])) {
|
||||
if (!closestVersion || semver.gt(key, closestVersion)) {
|
||||
closestVersion = k;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { fetchData } from '@/libs';
|
||||
import { fetchData, get } from '@/libs';
|
||||
import { DEFAULT } from '@/libs';
|
||||
import {
|
||||
adapter,
|
||||
@ -10,62 +10,73 @@ import {
|
||||
registryChainProfile,
|
||||
registryVersionProfile,
|
||||
withCustomRequest,
|
||||
} from './registry';
|
||||
import { PageRequest,type Coin } from '@/types';
|
||||
} from './api/registry';
|
||||
import { PageRequest, type Coin } from '@/types';
|
||||
import semver from 'semver';
|
||||
|
||||
export class BaseRestClient<R extends AbstractRegistry> {
|
||||
version: string;
|
||||
endpoint: string;
|
||||
registry: R;
|
||||
constructor(endpoint: string, registry: R) {
|
||||
constructor(endpoint: string, registry: R, version?: string) {
|
||||
this.endpoint = endpoint;
|
||||
this.registry = registry;
|
||||
this.version = version || 'v0.40';
|
||||
}
|
||||
async request<T>(request: Request<T>, args: Record<string, any>, query = '', adapter?: (source: any) => Promise<T> ) {
|
||||
let url = `${request.url.startsWith("http")?'':this.endpoint}${request.url}${query}`;
|
||||
async request<T>(request: Request<T>, args: Record<string, any>, query = '', adapter?: (source: any) => Promise<T>) {
|
||||
let url = `${request.url.startsWith('http') ? '' : this.endpoint}${request.url}${query}`;
|
||||
Object.keys(args).forEach((k) => {
|
||||
url = url.replace(`{${k}}`, args[k] || '');
|
||||
});
|
||||
return fetchData<T>(url, adapter||request.adapter);
|
||||
return fetchData<T>(url, adapter || request.adapter);
|
||||
}
|
||||
async get<T>(request: Request<T>, args: Record<string, any>, query = '') {
|
||||
let url = `${request.url.startsWith('http') ? '' : this.endpoint}${request.url}${query}`;
|
||||
Object.keys(args).forEach((k) => {
|
||||
url = url.replace(`{${k}}`, args[k] || '');
|
||||
});
|
||||
return get(url);
|
||||
}
|
||||
}
|
||||
|
||||
// dynamic all custom request implementations
|
||||
function registeCustomRequest() {
|
||||
const extensions: Record<string, any> = import.meta.glob('./clients/*.ts', { eager: true });
|
||||
Object.values(extensions).forEach(m => {
|
||||
if(m.store === 'version') {
|
||||
registryVersionProfile(m.name, withCustomRequest(DEFAULT, m.requests))
|
||||
const extensions: Record<string, any> = import.meta.glob('./api/customization/*.ts', { eager: true });
|
||||
Object.values(extensions).forEach((m) => {
|
||||
if (m.store === 'version') {
|
||||
registryVersionProfile(m.name, withCustomRequest(DEFAULT, m.requests));
|
||||
} else {
|
||||
registryChainProfile(m.name, withCustomRequest(DEFAULT, m.requests));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
registeCustomRequest()
|
||||
|
||||
registeCustomRequest();
|
||||
|
||||
export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
static newDefault(endpoint: string) {
|
||||
return new CosmosRestClient(endpoint, DEFAULT)
|
||||
return new CosmosRestClient(endpoint, DEFAULT);
|
||||
}
|
||||
|
||||
static newStrategy(endpoint: string, chain: any) {
|
||||
|
||||
let req
|
||||
if(chain) {
|
||||
// sdk version of current chain
|
||||
const ver = localStorage.getItem(`sdk_version_${chain.chainName}`) || chain.versions?.cosmosSdk;
|
||||
let profile;
|
||||
if (chain) {
|
||||
// find by name first
|
||||
req = findApiProfileByChain(chain.chainName)
|
||||
profile = findApiProfileByChain(chain.chainName);
|
||||
// if not found. try sdk version
|
||||
if(!req && chain.versions?.cosmosSdk) {
|
||||
req = findApiProfileBySDKVersion(localStorage.getItem(`sdk_version_${chain.chainName}`) || chain.versions?.cosmosSdk)
|
||||
if (!profile && chain.versions?.cosmosSdk) {
|
||||
profile = findApiProfileBySDKVersion(ver);
|
||||
}
|
||||
}
|
||||
return new CosmosRestClient(endpoint, req || DEFAULT)
|
||||
return new CosmosRestClient(endpoint, profile || DEFAULT, ver);
|
||||
}
|
||||
|
||||
// Auth Module
|
||||
async getAuthAccounts(page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
const query =`?${page.toQueryString()}`;
|
||||
if (!page) page = new PageRequest();
|
||||
const query = `?${page.toQueryString()}`;
|
||||
return this.request(this.registry.auth_accounts, {}, query);
|
||||
}
|
||||
async getAuthAccount(address: string) {
|
||||
@ -81,20 +92,23 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
async getBankDenomMetadata() {
|
||||
return this.request(this.registry.bank_denoms_metadata, {});
|
||||
}
|
||||
async getBankSupply(page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
const query =`?${page.toQueryString()}`;
|
||||
async getBankSupply(page?: PageRequest) {
|
||||
if (!page) page = new PageRequest();
|
||||
const query = `?${page.toQueryString()}`;
|
||||
return this.request(this.registry.bank_supply, {}, query);
|
||||
}
|
||||
async getBankSupplyByDenom(denom: string) {
|
||||
let supply;
|
||||
try{
|
||||
supply = await this.request(this.registry.bank_supply_by_denom, { denom });
|
||||
} catch(err) {
|
||||
try {
|
||||
supply = await this.request(this.registry.bank_supply_by_denom, { denom });
|
||||
} catch (err) {
|
||||
// will move this to sdk version profile later
|
||||
supply = await this.request({url: "/cosmos/bank/v1beta1/supply/by_denom?denom={denom}", adapter } as Request<{ amount: Coin }>, { denom });
|
||||
supply = await this.request(
|
||||
{ url: '/cosmos/bank/v1beta1/supply/by_denom?denom={denom}', adapter } as Request<{ amount: Coin }>,
|
||||
{ denom }
|
||||
);
|
||||
}
|
||||
return supply
|
||||
return supply;
|
||||
}
|
||||
// Distribution Module
|
||||
async getDistributionParams() {
|
||||
@ -114,10 +128,7 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
});
|
||||
}
|
||||
async getDistributionValidatorOutstandingRewards(validator_address: string) {
|
||||
return this.request(
|
||||
this.registry.distribution_validator_outstanding_rewards,
|
||||
{ validator_address }
|
||||
);
|
||||
return this.request(this.registry.distribution_validator_outstanding_rewards, { validator_address });
|
||||
}
|
||||
async getDistributionValidatorSlashes(validator_address: string) {
|
||||
return this.request(this.registry.distribution_validator_slashes, {
|
||||
@ -134,8 +145,8 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
}
|
||||
// Gov
|
||||
async getParams(subspace: string, key: string) {
|
||||
console.log(this.registry.params, subspace, key)
|
||||
return this.request(this.registry.params, {subspace, key});
|
||||
console.log(this.registry.params, subspace, key);
|
||||
return this.request(this.registry.params, { subspace, key });
|
||||
}
|
||||
async getGovParamsVoting() {
|
||||
return this.request(this.registry.gov_params_voting, {});
|
||||
@ -147,9 +158,9 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
return this.request(this.registry.gov_params_tally, {});
|
||||
}
|
||||
async getGovProposals(status: string, page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
page.reverse = true
|
||||
const query =`?proposal_status={status}&${page.toQueryString()}`;
|
||||
if (!page) page = new PageRequest();
|
||||
page.reverse = true;
|
||||
const query = `?proposal_status={status}&${page.toQueryString()}`;
|
||||
return this.request(this.registry.gov_proposals, { status }, query);
|
||||
}
|
||||
async getGovProposal(proposal_id: string) {
|
||||
@ -162,20 +173,20 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
}
|
||||
async getGovProposalTally(proposal_id: string) {
|
||||
return this.request(this.registry.gov_proposals_tally, { proposal_id }, undefined, (source: any) => {
|
||||
return Promise.resolve({ tally: {
|
||||
yes: source.tally.yes || source.tally.yes_count,
|
||||
abstain: source.tally.abstain || source.tally.abstain_count,
|
||||
no: source.tally.no || source.tally.no_count,
|
||||
no_with_veto: source.tally.no_with_veto || source.tally.no_with_veto_count,
|
||||
return Promise.resolve({
|
||||
tally: {
|
||||
yes: source.tally.yes || source.tally.yes_count,
|
||||
abstain: source.tally.abstain || source.tally.abstain_count,
|
||||
no: source.tally.no || source.tally.no_count,
|
||||
no_with_veto: source.tally.no_with_veto || source.tally.no_with_veto_count,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
async getGovProposalVotes(proposal_id: string, page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
page.reverse = true
|
||||
const query =`?proposal_status={status}&${page.toQueryString()}`;
|
||||
if (!page) page = new PageRequest();
|
||||
page.reverse = true;
|
||||
const query = `?proposal_status={status}&${page.toQueryString()}`;
|
||||
return this.request(this.registry.gov_proposals_votes, { proposal_id }, query);
|
||||
}
|
||||
async getGovProposalVotesVoter(proposal_id: string, voter: string) {
|
||||
@ -218,34 +229,29 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
});
|
||||
}
|
||||
async getStakingValidatorsDelegations(validator_addr: string, page?: PageRequest) {
|
||||
if(!page) {
|
||||
page = new PageRequest()
|
||||
if (!page) {
|
||||
page = new PageRequest();
|
||||
// page.reverse = true
|
||||
page.count_total = true
|
||||
page.offset = 0
|
||||
}
|
||||
const query =`?${page.toQueryString()}`;
|
||||
return this.request(this.registry.staking_validators_delegations, {
|
||||
page.count_total = true;
|
||||
page.offset = 0;
|
||||
}
|
||||
const query = `?${page.toQueryString()}`;
|
||||
return this.request(
|
||||
this.registry.staking_validators_delegations,
|
||||
{
|
||||
validator_addr,
|
||||
},
|
||||
query
|
||||
);
|
||||
}
|
||||
async getStakingValidatorsDelegationsDelegator(validator_addr: string, delegator_addr: string) {
|
||||
return this.request(this.registry.staking_validators_delegations_delegator, { validator_addr, delegator_addr });
|
||||
}
|
||||
async getStakingValidatorsDelegationsUnbonding(validator_addr: string, delegator_addr: string) {
|
||||
return this.request(this.registry.staking_validators_delegations_unbonding_delegations, {
|
||||
validator_addr,
|
||||
}, query);
|
||||
}
|
||||
async getStakingValidatorsDelegationsDelegator(
|
||||
validator_addr: string,
|
||||
delegator_addr: string
|
||||
) {
|
||||
return this.request(
|
||||
this.registry.staking_validators_delegations_delegator,
|
||||
{ validator_addr, delegator_addr }
|
||||
);
|
||||
}
|
||||
async getStakingValidatorsDelegationsUnbonding(
|
||||
validator_addr: string,
|
||||
delegator_addr: string
|
||||
) {
|
||||
return this.request(
|
||||
this.registry.staking_validators_delegations_unbonding_delegations,
|
||||
{ validator_addr, delegator_addr }
|
||||
);
|
||||
delegator_addr,
|
||||
});
|
||||
}
|
||||
|
||||
//tendermint
|
||||
@ -262,19 +268,29 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
return this.request(this.registry.base_tendermint_node_info, {});
|
||||
}
|
||||
async getBaseValidatorsetAt(height: string | number, offset: number) {
|
||||
const query = `?pagination.limit=100&pagination.offset=${offset}`
|
||||
return this.request(this.registry.base_tendermint_validatorsets_height, {
|
||||
height,
|
||||
}, query);
|
||||
const query = `?pagination.limit=100&pagination.offset=${offset}`;
|
||||
return this.request(
|
||||
this.registry.base_tendermint_validatorsets_height,
|
||||
{
|
||||
height,
|
||||
},
|
||||
query
|
||||
);
|
||||
}
|
||||
async getBaseValidatorsetLatest(offset: number) {
|
||||
const query = `?pagination.limit=100&pagination.offset=${offset}`
|
||||
const query = `?pagination.limit=100&pagination.offset=${offset}`;
|
||||
return this.request(this.registry.base_tendermint_validatorsets_latest, {}, query);
|
||||
}
|
||||
// tx
|
||||
async getTxsBySender(sender: string, page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
const query = `?order_by=2&events=message.sender='${sender}'&pagination.limit=${page.limit}&pagination.offset=${page.offset||0}`;
|
||||
if (!page) page = new PageRequest();
|
||||
|
||||
let query = `?events=message.sender='${sender}'&pagination.limit=${page.limit}&pagination.offset=${
|
||||
page.offset || 0
|
||||
}`;
|
||||
if (semver.gte(this.version.replaceAll('v', ''), '0.50.0')) {
|
||||
query = `?query=message.sender='${sender}'&pagination.limit=${page.limit}&pagination.offset=${page.offset || 0}`;
|
||||
}
|
||||
return this.request(this.registry.tx_txs, {}, query);
|
||||
}
|
||||
// query ibc sending msgs
|
||||
@ -282,8 +298,13 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
// query ibc receiving msgs
|
||||
// ?&pagination.reverse=true&events=recv_packet.packet_dst_channel='${channel}'&events=recv_packet.packet_dst_port='${port}'
|
||||
async getTxs(query: string, params: any, page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
return this.request(this.registry.tx_txs, params, `${query}&${page.toQueryString()}`);
|
||||
if (!page) page = new PageRequest();
|
||||
if (semver.gte(this.version.replaceAll('v', ''), '0.50.0')) {
|
||||
let query_edit = query.replaceAll('events=', 'query=');
|
||||
return this.request(this.registry.tx_txs, params, `${query_edit}&${page.toQueryString()}`);
|
||||
} else {
|
||||
return this.request(this.registry.tx_txs, params, `${query}&${page.toQueryString()}`);
|
||||
}
|
||||
}
|
||||
async getTxsAt(height: string | number) {
|
||||
return this.request(this.registry.tx_txs_block, { height });
|
||||
@ -310,21 +331,15 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
});
|
||||
}
|
||||
async getIBCConnections(page?: PageRequest) {
|
||||
if(!page) page = new PageRequest()
|
||||
const query =`?${page.toQueryString()}`;
|
||||
if (!page) page = new PageRequest();
|
||||
const query = `?${page.toQueryString()}`;
|
||||
return this.request(this.registry.ibc_core_connection_connections, {}, query);
|
||||
}
|
||||
async getIBCConnectionsById(connection_id: string) {
|
||||
return this.request(
|
||||
this.registry.ibc_core_connection_connections_connection_id,
|
||||
{ connection_id }
|
||||
);
|
||||
return this.request(this.registry.ibc_core_connection_connections_connection_id, { connection_id });
|
||||
}
|
||||
async getIBCConnectionsClientState(connection_id: string) {
|
||||
return this.request(
|
||||
this.registry.ibc_core_connection_connections_connection_id_client_state,
|
||||
{ connection_id }
|
||||
);
|
||||
return this.request(this.registry.ibc_core_connection_connections_connection_id_client_state, { connection_id });
|
||||
}
|
||||
async getIBCConnectionsChannels(connection_id: string) {
|
||||
return this.request(this.registry.ibc_core_channel_connections_channels, {
|
||||
@ -335,10 +350,7 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
return this.request(this.registry.ibc_core_channel_channels, {});
|
||||
}
|
||||
async getIBCChannelAcknowledgements(channel_id: string, port_id: string) {
|
||||
return this.request(
|
||||
this.registry.ibc_core_channel_channels_acknowledgements,
|
||||
{ channel_id, port_id }
|
||||
);
|
||||
return this.request(this.registry.ibc_core_channel_channels_acknowledgements, { channel_id, port_id });
|
||||
}
|
||||
async getIBCChannelNextSequence(channel_id: string, port_id: string) {
|
||||
return this.request(this.registry.ibc_core_channel_channels_next_sequence, {
|
||||
@ -347,6 +359,15 @@ export class CosmosRestClient extends BaseRestClient<RequestRegistry> {
|
||||
});
|
||||
}
|
||||
async getInterchainSecurityValidatorRotatedKey(chain_id: string, provider_address: string) {
|
||||
return this.request(this.registry.interchain_security_ccv_provider_validator_consumer_addr, {chain_id, provider_address});
|
||||
return this.request(this.registry.interchain_security_ccv_provider_validator_consumer_addr, {
|
||||
chain_id,
|
||||
provider_address,
|
||||
});
|
||||
}
|
||||
async getInterchainSecurityProviderOptedInValidators(chain_id: string) {
|
||||
return this.request(this.registry.interchain_security_provider_opted_in_validators, { chain_id });
|
||||
}
|
||||
async getInterchainSecurityConsumerValidators(chain_id: string) {
|
||||
return this.request(this.registry.interchain_security_consumer_validators, { chain_id });
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
import type{ RequestRegistry } from '@/libs/registry'
|
||||
import { adapter } from '@/libs/registry'
|
||||
import type { GovProposal, PaginatedProposals } from '@/types'
|
||||
// which registry is store
|
||||
export const store = 'name' // name or version
|
||||
// Blockchain Name
|
||||
export const name = 'evmos'
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if(p) {
|
||||
if(p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
p.proposal_id = p.id
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
mint_inflation: { url: '/evmos/inflation/v1/inflation_rate', adapter: async (data: any) => ({inflation: (Number(data.inflation_rate || 0)/ 100 ).toFixed(2)}) },
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: { url: '/cosmos/gov/v1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p:any) => proposalAdapter(p))
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{proposal: GovProposal}> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
import type{ RequestRegistry } from '@/libs/registry'
|
||||
// import dayjs from 'dayjs'
|
||||
import { adapter } from '@/libs/registry'
|
||||
import type { GovProposal, PaginatedProposals } from '@/types'
|
||||
|
||||
// which registry is store
|
||||
export const store = 'name' // name or version
|
||||
// Blockchain Name
|
||||
export const name = 'osmosis'
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if(p) {
|
||||
if(p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
p.proposal_id = p.id
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// osmosis custom request
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
mint_inflation: {
|
||||
url: `https://public-osmosis-api.numia.xyz/apr?start_date=${new Date(new Date().getTime() - 186400*1000).toISOString().split('T')[0]}&end_date=${new Date().toISOString().split('T')[0]}`,
|
||||
adapter: async (data: any) => {
|
||||
const [first] = data
|
||||
return {inflation: String(Number(first?.apr|| "0")/100.0)}
|
||||
}
|
||||
},
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: { url: '/cosmos/gov/v1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p:any) => proposalAdapter(p))
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{proposal: GovProposal}> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
}
|
||||
@ -1,67 +0,0 @@
|
||||
import type { RequestRegistry } from '@/libs/registry'
|
||||
import { adapter } from '@/libs/registry'
|
||||
import type {
|
||||
GovParams,
|
||||
GovProposal,
|
||||
GovVote,
|
||||
PaginatedProposalDeposit,
|
||||
PaginatedProposalVotes,
|
||||
PaginatedProposals,
|
||||
Tally,
|
||||
} from '@/types/';
|
||||
|
||||
// which registry is store
|
||||
export const store = 'version' // name or version
|
||||
// Blockchain Name
|
||||
export const name = 'v0.46.7'
|
||||
|
||||
function proposalAdapter(p: any): GovProposal {
|
||||
if(p) {
|
||||
if(p.messages && p.messages.length >= 1) p.content = p.messages[0].content || p.messages[0]
|
||||
p.proposal_id = p.id
|
||||
p.final_tally_result = {
|
||||
yes: p.final_tally_result?.yes_count,
|
||||
no: p.final_tally_result?.no_count,
|
||||
no_with_veto: p.final_tally_result?.no_with_veto_count,
|
||||
abstain: p.final_tally_result?.abstain_count,
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
export const requests: Partial<RequestRegistry> = {
|
||||
gov_params_voting: { url: '/cosmos/gov/v1/params/voting', adapter },
|
||||
gov_params_tally: { url: '/cosmos/gov/v1/params/tallying', adapter },
|
||||
gov_params_deposit: { url: '/cosmos/gov/v1/params/deposit', adapter },
|
||||
gov_proposals: { url: '/cosmos/gov/v1/proposals', adapter: async (source: any): Promise<PaginatedProposals> => {
|
||||
const proposals = source.proposals.map((p:any) => proposalAdapter(p))
|
||||
return {
|
||||
proposals,
|
||||
pagination: source.pagination
|
||||
}
|
||||
}},
|
||||
gov_proposals_proposal_id: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}',
|
||||
adapter: async (source: any): Promise<{proposal: GovProposal}> => {
|
||||
return {
|
||||
proposal: proposalAdapter(source.proposal)
|
||||
}
|
||||
},
|
||||
},
|
||||
gov_proposals_deposits: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/deposits',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_tally: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/tally',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes',
|
||||
adapter,
|
||||
},
|
||||
gov_proposals_votes_voter: {
|
||||
url: '/cosmos/gov/v1/proposals/{proposal_id}/votes/{voter}',
|
||||
adapter,
|
||||
},
|
||||
}
|
||||
@ -1,12 +1,9 @@
|
||||
import fetch from 'cross-fetch';
|
||||
|
||||
export async function fetchData<T>(
|
||||
url: string,
|
||||
adapter: (source: any) => Promise<T>
|
||||
): Promise<T> {
|
||||
export async function fetchData<T>(url: string, adapter: (source: any) => Promise<T>): Promise<T> {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error: ${response.status}`);
|
||||
throw new Error(`HTTP error: ${response.status}, ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
return adapter(data);
|
||||
@ -29,11 +26,11 @@ try {
|
||||
}
|
||||
// */
|
||||
export async function get(url: string) {
|
||||
return (await fetch(url, {referrerPolicy: 'origin-when-cross-origin'})).json();
|
||||
return (await fetch(url, { referrerPolicy: 'origin-when-cross-origin' })).json();
|
||||
}
|
||||
|
||||
export async function getB(url: string) {
|
||||
return (await fetch(url, {referrerPolicy: 'origin-when-cross-origin'})).arrayBuffer();
|
||||
return (await fetch(url, { referrerPolicy: 'origin-when-cross-origin' })).arrayBuffer();
|
||||
}
|
||||
|
||||
export async function post(url: string, data: any) {
|
||||
|
||||
@ -30,26 +30,7 @@ export function uint8ArrayToString(arr: Uint8Array) {
|
||||
return str;
|
||||
}
|
||||
|
||||
const COUNT_ABBRS = [
|
||||
'',
|
||||
'K',
|
||||
'M',
|
||||
'B',
|
||||
't',
|
||||
'q',
|
||||
's',
|
||||
'S',
|
||||
'o',
|
||||
'n',
|
||||
'd',
|
||||
'U',
|
||||
'D',
|
||||
'T',
|
||||
'Qt',
|
||||
'Qd',
|
||||
'Sd',
|
||||
'St',
|
||||
];
|
||||
const COUNT_ABBRS = ['', 'K', 'M', 'B', 't', 'q', 's', 'S', 'o', 'n', 'd', 'U', 'D', 'T', 'Qt', 'Qd', 'Sd', 'St'];
|
||||
|
||||
export function formatNumber(count: number, withAbbr = false, decimals = 2) {
|
||||
const i = count === 0 ? count : Math.floor(Math.log(count) / Math.log(1000));
|
||||
@ -60,24 +41,15 @@ export function formatNumber(count: number, withAbbr = false, decimals = 2) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export function formatTokenAmount(
|
||||
assets: any,
|
||||
tokenAmount: any,
|
||||
decimals = 2,
|
||||
tokenDenom = 'uatom',
|
||||
format = true
|
||||
) {
|
||||
const denom = typeof tokenDenom === 'string'
|
||||
? tokenDenom
|
||||
// @ts-ignore
|
||||
: tokenDenom?.denom_trace?.base_denom;
|
||||
export function formatTokenAmount(assets: any, tokenAmount: any, decimals = 2, tokenDenom = 'uatom', format = true) {
|
||||
const denom =
|
||||
typeof tokenDenom === 'string'
|
||||
? tokenDenom
|
||||
: // @ts-ignore
|
||||
tokenDenom?.denom_trace?.base_denom;
|
||||
let amount = 0;
|
||||
const asset = assets.find((a: any) => a.base === denom);
|
||||
let exp = asset
|
||||
? asset.exponent
|
||||
: String(denom).startsWith('gravity')
|
||||
? 18
|
||||
: 6;
|
||||
let exp = asset ? asset.exponent : String(denom).startsWith('gravity') ? 18 : 6;
|
||||
const config = Object.values(getLocalChains());
|
||||
|
||||
amount = Number(Number(tokenAmount)) / 10 ** exp;
|
||||
@ -120,24 +92,24 @@ export function isHexAddress(v: any) {
|
||||
}
|
||||
|
||||
export function isBech32Address(v?: string) {
|
||||
if(!v) return ""
|
||||
const pattern = /^[a-z\d]+1[a-z\d]{38}$/g
|
||||
return String(v).search(pattern) > -1
|
||||
if (!v) return '';
|
||||
const pattern = /^[a-z\d]+1[a-z\d]{38}$/g;
|
||||
return String(v).search(pattern) > -1;
|
||||
}
|
||||
|
||||
export function formatSeconds(value?: string) {
|
||||
if(!value) return ''
|
||||
const duration = Number(value.replace(/s/, ''))
|
||||
if(duration > 24*60*60) {
|
||||
return `${(duration / ( 24 * 60 * 60)).toFixed()} days`
|
||||
if (!value) return '';
|
||||
const duration = Number(value.replace(/s/, ''));
|
||||
if (duration > 24 * 60 * 60) {
|
||||
return `${(duration / (24 * 60 * 60)).toFixed()} days`;
|
||||
}
|
||||
if(duration > 60*60) {
|
||||
return `${(duration / (60 * 60)).toFixed()} hours`
|
||||
}
|
||||
if(duration > 60) {
|
||||
return `${duration / 60} mins`
|
||||
if (duration > 60 * 60) {
|
||||
return `${(duration / (60 * 60)).toFixed()} hours`;
|
||||
}
|
||||
return value
|
||||
if (duration > 60) {
|
||||
return `${duration / 60} mins`;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
export function hexToRgb(hex: string) {
|
||||
|
||||
10
src/main.ts
10
src/main.ts
@ -7,7 +7,7 @@ import { createPinia } from 'pinia';
|
||||
import LazyLoad from 'lazy-load-vue3';
|
||||
|
||||
import router from './router';
|
||||
import { useBaseStore } from './stores/useBaseStore';
|
||||
import { useBaseStore } from '@/stores';
|
||||
|
||||
// Create vue app
|
||||
const app = createApp(App);
|
||||
@ -19,6 +19,12 @@ app.use(LazyLoad, { component: true });
|
||||
// Mount vue app
|
||||
app.mount('#app');
|
||||
|
||||
// fetch new block(s) on this interval, specified in miliseconds
|
||||
// note that the design of the block fetching code is such that it
|
||||
// will MISS BLOCKS if this interval is longer than the block time
|
||||
// TODO: make this configurable
|
||||
const blockFetchInterval = 2 * 1000;
|
||||
|
||||
// fetch latest block every 6s
|
||||
const blockStore = useBaseStore();
|
||||
const requestCounter = ref(0);
|
||||
@ -28,4 +34,4 @@ setInterval(() => {
|
||||
// max allowed request
|
||||
blockStore.fetchLatest().finally(() => (requestCounter.value -= 1));
|
||||
}
|
||||
}, 6000);
|
||||
}, blockFetchInterval);
|
||||
|
||||
@ -1,23 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
useBlockchain,
|
||||
useFormatter,
|
||||
useStakingStore,
|
||||
useTxDialog,
|
||||
} from '@/stores';
|
||||
import { useBlockchain, useFormatter, useStakingStore, useTxDialog } from '@/stores';
|
||||
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
|
||||
import DonutChart from '@/components/charts/DonutChart.vue';
|
||||
import { computed, ref } from '@vue/reactivity';
|
||||
import { onMounted } from 'vue';
|
||||
import { Icon } from '@iconify/vue';
|
||||
|
||||
import type {
|
||||
AuthAccount,
|
||||
Delegation,
|
||||
TxResponse,
|
||||
DelegatorRewards,
|
||||
UnbondingResponses,
|
||||
} from '@/types';
|
||||
import type { AuthAccount, Delegation, TxResponse, DelegatorRewards, UnbondingResponses } from '@/types';
|
||||
import type { Coin } from '@cosmjs/amino';
|
||||
import Countdown from '@/components/Countdown.vue';
|
||||
import { fromBase64 } from '@cosmjs/encoding';
|
||||
@ -81,13 +70,12 @@ const totalValue = computed(() => {
|
||||
});
|
||||
unbonding.value?.forEach((x) => {
|
||||
x.entries?.forEach((y) => {
|
||||
value += format.tokenValueNumber({amount: y.balance, denom: stakingStore.params.bond_denom});
|
||||
value += format.tokenValueNumber({ amount: y.balance, denom: stakingStore.params.bond_denom });
|
||||
});
|
||||
});
|
||||
return format.formatNumber(value, '0,0.00');
|
||||
});
|
||||
|
||||
|
||||
function loadAccount(address: string) {
|
||||
blockchain.rpc.getAuthAccount(address).then((x) => {
|
||||
account.value = x.account;
|
||||
@ -113,7 +101,7 @@ function loadAccount(address: string) {
|
||||
});
|
||||
});
|
||||
|
||||
const receivedQuery = `?&pagination.reverse=true&events=coin_received.receiver='${address}'&pagination.limit=5`;
|
||||
const receivedQuery = `?&pagination.reverse=true&events=coin_received.receiver='${address}'&pagination.limit=5`;
|
||||
blockchain.rpc.getTxs(receivedQuery, {}).then((x) => {
|
||||
recentReceived.value = x.tx_responses;
|
||||
});
|
||||
@ -123,11 +111,12 @@ function updateEvent() {
|
||||
loadAccount(props.address);
|
||||
}
|
||||
|
||||
function mapAmount(events:{type: string, attributes: {key: string, value: string}[]}[]) {
|
||||
if(!events) return []
|
||||
return events.find(x => x.type==='coin_received')?.attributes
|
||||
.filter(x => x.key === 'YW1vdW50'|| x.key === `amount`)
|
||||
.map(x => x.key==='amount'? x.value : String.fromCharCode(...fromBase64(x.value)))
|
||||
function mapAmount(events: { type: string; attributes: { key: string; value: string }[] }[]) {
|
||||
if (!events) return [];
|
||||
return events
|
||||
.find((x) => x.type === 'coin_received')
|
||||
?.attributes.filter((x) => x.key === 'YW1vdW50' || x.key === `amount`)
|
||||
.map((x) => (x.key === 'amount' ? x.value : String.fromCharCode(...fromBase64(x.value))));
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
@ -137,17 +126,9 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
<div class="flex items-center">
|
||||
<!-- img -->
|
||||
<div class="inline-flex relative w-11 h-11 rounded-md">
|
||||
<div
|
||||
class="w-11 h-11 absolute rounded-md opacity-10 bg-primary"
|
||||
></div>
|
||||
<div
|
||||
class="w-full inline-flex items-center align-middle flex-none justify-center"
|
||||
>
|
||||
<Icon
|
||||
icon="mdi-qrcode"
|
||||
class="text-primary"
|
||||
style="width: 27px; height: 27px"
|
||||
/>
|
||||
<div class="w-11 h-11 absolute rounded-md opacity-10 bg-primary"></div>
|
||||
<div class="w-full inline-flex items-center align-middle flex-none justify-center">
|
||||
<Icon icon="mdi-qrcode" class="text-primary" style="width: 27px; height: 27px" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- content -->
|
||||
@ -164,48 +145,37 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
<h2 class="card-title mb-4">{{ $t('account.assets') }}</h2>
|
||||
<!-- button -->
|
||||
<div class="flex justify-end mb-4 pr-5">
|
||||
<label
|
||||
for="send"
|
||||
class="btn btn-primary btn-sm mr-2"
|
||||
@click="dialog.open('send', {}, updateEvent)"
|
||||
>{{ $t('account.btn_send') }}</label
|
||||
>
|
||||
<label
|
||||
for="transfer"
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="
|
||||
dialog.open(
|
||||
'transfer',
|
||||
{
|
||||
chain_name: blockchain.current?.prettyName,
|
||||
},
|
||||
updateEvent
|
||||
)
|
||||
"
|
||||
>{{ $t('account.btn_transfer') }}</label
|
||||
>
|
||||
</div>
|
||||
<label for="send" class="btn btn-primary btn-sm mr-2" @click="dialog.open('send', {}, updateEvent)">{{
|
||||
$t('account.btn_send')
|
||||
}}</label>
|
||||
<label
|
||||
for="transfer"
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="
|
||||
dialog.open(
|
||||
'transfer',
|
||||
{
|
||||
chain_name: blockchain.current?.prettyName,
|
||||
},
|
||||
updateEvent
|
||||
)
|
||||
"
|
||||
>{{ $t('account.btn_transfer') }}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid md:!grid-cols-3">
|
||||
<div class="md:!col-span-1">
|
||||
<DonutChart :series="totalAmountByCategory" :labels="labels" />
|
||||
</div>
|
||||
<div class="mt-4 md:!col-span-2 md:!mt-0 md:!ml-4">
|
||||
<div class="mt-4 md:!col-span-2 md:!mt-0 md:!ml-4">
|
||||
<!-- list-->
|
||||
<div class="">
|
||||
<!--balances -->
|
||||
<div
|
||||
class="flex items-center px-4 mb-2"
|
||||
v-for="(balanceItem, index) in balances"
|
||||
:key="index"
|
||||
>
|
||||
<div
|
||||
class="w-9 h-9 rounded overflow-hidden flex items-center justify-center relative mr-4"
|
||||
>
|
||||
<div class="flex items-center px-4 mb-2" v-for="(balanceItem, index) in balances" :key="index">
|
||||
<div class="w-9 h-9 rounded overflow-hidden flex items-center justify-center relative mr-4">
|
||||
<Icon icon="mdi-account-cash" class="text-info" size="20" />
|
||||
<div
|
||||
class="absolute top-0 bottom-0 left-0 right-0 bg-info opacity-20"
|
||||
></div>
|
||||
<div class="absolute top-0 bottom-0 left-0 right-0 bg-info opacity-20"></div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="text-sm font-semibold">
|
||||
@ -215,97 +185,55 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
{{ format.calculatePercent(balanceItem.amount, totalAmount) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-xs truncate relative py-1 px-3 rounded-full w-fit text-primary dark:invert mr-2"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert text-sm"
|
||||
></span>
|
||||
${{ format.tokenValue(balanceItem) }}
|
||||
<div class="text-xs truncate relative py-1 px-3 rounded-full w-fit text-primary dark:invert mr-2">
|
||||
<span class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert text-sm"></span>
|
||||
${{ format.tokenValue(balanceItem) }}
|
||||
</div>
|
||||
</div>
|
||||
<!--delegations -->
|
||||
<div
|
||||
class="flex items-center px-4 mb-2"
|
||||
v-for="(delegationItem, index) in delegations"
|
||||
:key="index"
|
||||
>
|
||||
<div
|
||||
class="w-9 h-9 rounded overflow-hidden flex items-center justify-center relative mr-4"
|
||||
>
|
||||
<div class="flex items-center px-4 mb-2" v-for="(delegationItem, index) in delegations" :key="index">
|
||||
<div class="w-9 h-9 rounded overflow-hidden flex items-center justify-center relative mr-4">
|
||||
<Icon icon="mdi-user-clock" class="text-warning" size="20" />
|
||||
<div
|
||||
class="absolute top-0 bottom-0 left-0 right-0 bg-warning opacity-20"
|
||||
></div>
|
||||
<div class="absolute top-0 bottom-0 left-0 right-0 bg-warning opacity-20"></div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="text-sm font-semibold">
|
||||
{{ format.formatToken(delegationItem?.balance) }}
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
{{
|
||||
format.calculatePercent(
|
||||
delegationItem?.balance?.amount,
|
||||
totalAmount
|
||||
)
|
||||
}}
|
||||
{{ format.calculatePercent(delegationItem?.balance?.amount, totalAmount) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-xs truncate relative py-1 px-3 rounded-full w-fit text-primary dark:invert mr-2"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert text-sm"
|
||||
></span>
|
||||
${{ format.tokenValue(delegationItem?.balance) }}
|
||||
<div class="text-xs truncate relative py-1 px-3 rounded-full w-fit text-primary dark:invert mr-2">
|
||||
<span class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert text-sm"></span>
|
||||
${{ format.tokenValue(delegationItem?.balance) }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- rewards.total -->
|
||||
<div
|
||||
class="flex items-center px-4 mb-2"
|
||||
v-for="(rewardItem, index) in rewards.total"
|
||||
:key="index"
|
||||
>
|
||||
<div
|
||||
class="w-9 h-9 rounded overflow-hidden flex items-center justify-center relative mr-4"
|
||||
>
|
||||
<Icon
|
||||
icon="mdi-account-arrow-up"
|
||||
class="text-success"
|
||||
size="20"
|
||||
/>
|
||||
<div
|
||||
class="absolute top-0 bottom-0 left-0 right-0 bg-success opacity-20"
|
||||
></div>
|
||||
<div class="flex items-center px-4 mb-2" v-for="(rewardItem, index) in rewards.total" :key="index">
|
||||
<div class="w-9 h-9 rounded overflow-hidden flex items-center justify-center relative mr-4">
|
||||
<Icon icon="mdi-account-arrow-up" class="text-success" size="20" />
|
||||
<div class="absolute top-0 bottom-0 left-0 right-0 bg-success opacity-20"></div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="text-sm font-semibold">
|
||||
{{ format.formatToken(rewardItem) }}
|
||||
</div>
|
||||
<div class="text-xs">{{ format.calculatePercent(rewardItem.amount, totalAmount) }}</div>
|
||||
<div class="text-xs">
|
||||
{{ format.calculatePercent(rewardItem.amount, totalAmount) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-xs truncate relative py-1 px-3 rounded-full w-fit text-primary dark:invert mr-2"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert text-sm"
|
||||
></span>${{ format.tokenValue(rewardItem) }}
|
||||
|
||||
<div class="text-xs truncate relative py-1 px-3 rounded-full w-fit text-primary dark:invert mr-2">
|
||||
<span class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert text-sm"></span>${{
|
||||
format.tokenValue(rewardItem)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<!-- mdi-account-arrow-right -->
|
||||
<div class="flex items-center px-4">
|
||||
<div
|
||||
class="w-9 h-9 rounded overflow-hidden flex items-center justify-center relative mr-4"
|
||||
>
|
||||
<Icon
|
||||
icon="mdi-account-arrow-right"
|
||||
class="text-error"
|
||||
size="20"
|
||||
/>
|
||||
<div
|
||||
class="absolute top-0 bottom-0 left-0 right-0 bg-error opacity-20"
|
||||
></div>
|
||||
<div class="w-9 h-9 rounded overflow-hidden flex items-center justify-center relative mr-4">
|
||||
<Icon icon="mdi-account-arrow-right" class="text-error" size="20" />
|
||||
<div class="absolute top-0 bottom-0 left-0 right-0 bg-error opacity-20"></div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="text-sm font-semibold">
|
||||
@ -320,19 +248,20 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
{{ format.calculatePercent(unbondingTotal, totalAmount) }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-xs truncate relative py-1 px-3 rounded-full w-fit text-primary dark:invert mr-2"
|
||||
>
|
||||
<div class="text-xs truncate relative py-1 px-3 rounded-full w-fit text-primary dark:invert mr-2">
|
||||
<span class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary dark:invert"></span>
|
||||
${{format.tokenValue({
|
||||
amount: String(unbondingTotal),
|
||||
denom: stakingStore.params.bond_denom,
|
||||
})
|
||||
}}
|
||||
${{
|
||||
format.tokenValue({
|
||||
amount: String(unbondingTotal),
|
||||
denom: stakingStore.params.bond_denom,
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 text-lg font-semibold mr-5 pl-5 border-t pt-4 text-right">
|
||||
<div
|
||||
class="mt-4 text-lg font-semibold mr-5 pl-5 border-t pt-4 text-right"
|
||||
>
|
||||
{{ $t('account.total_value') }}: ${{ totalValue }}
|
||||
</div>
|
||||
</div>
|
||||
@ -344,18 +273,12 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
<div class="flex justify-between">
|
||||
<h2 class="card-title mb-4">{{ $t('account.delegations') }}</h2>
|
||||
<div class="flex justify-end mb-4">
|
||||
<label
|
||||
for="delegate"
|
||||
class="btn btn-primary btn-sm mr-2"
|
||||
@click="dialog.open('delegate', {}, updateEvent)"
|
||||
>{{ $t('account.btn_delegate') }}</label
|
||||
>
|
||||
<label
|
||||
for="withdraw"
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="dialog.open('withdraw', {}, updateEvent)"
|
||||
>{{ $t('account.btn_withdraw') }}</label
|
||||
>
|
||||
<label for="delegate" class="btn btn-primary btn-sm mr-2" @click="dialog.open('delegate', {}, updateEvent)">{{
|
||||
$t('account.btn_delegate')
|
||||
}}</label>
|
||||
<label for="withdraw" class="btn btn-primary btn-sm" @click="dialog.open('withdraw', {}, updateEvent)">{{
|
||||
$t('account.btn_withdraw')
|
||||
}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
@ -369,15 +292,16 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm">
|
||||
<tr v-if="delegations.length === 0"><td colspan="10"><div class="text-center">{{ $t('account.no_delegations') }}</div></td></tr>
|
||||
<tr v-if="delegations.length === 0">
|
||||
<td colspan="10">
|
||||
<div class="text-center">{{ $t('account.no_delegations') }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="(v, index) in delegations" :key="index">
|
||||
<td class="text-caption text-primary py-3">
|
||||
<RouterLink
|
||||
:to="`/${chain}/staking/${v.delegation.validator_address}`"
|
||||
>{{
|
||||
format.validatorFromBech32(v.delegation.validator_address) || v.delegation.validator_address
|
||||
}}</RouterLink
|
||||
>
|
||||
<RouterLink :to="`/${chain}/staking/${v.delegation.validator_address}`">{{
|
||||
format.validatorFromBech32(v.delegation.validator_address) || v.delegation.validator_address
|
||||
}}</RouterLink>
|
||||
</td>
|
||||
<td class="py-3">
|
||||
{{ format.formatToken(v.balance, true, '0,0.[000000]') }}
|
||||
@ -385,10 +309,7 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
<td class="py-3">
|
||||
{{
|
||||
format.formatTokens(
|
||||
rewards?.rewards?.find(
|
||||
(x) =>
|
||||
x.validator_address === v.delegation.validator_address
|
||||
)?.reward
|
||||
rewards?.rewards?.find((x) => x.validator_address === v.delegation.validator_address)?.reward
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
@ -445,10 +366,7 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
</div>
|
||||
|
||||
<!-- Unbonding Delegations -->
|
||||
<div
|
||||
class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow"
|
||||
v-if="unbonding && unbonding.length > 0"
|
||||
>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow" v-if="unbonding && unbonding.length > 0">
|
||||
<h2 class="card-title mb-4">{{ $t('account.unbonding_delegations') }}</h2>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table text-sm w-full">
|
||||
@ -461,47 +379,42 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm" v-for="(v, index) in unbonding" :key="index">
|
||||
<tr>
|
||||
<td class="text-caption text-primary py-3 bg-slate-200" colspan="10">
|
||||
<RouterLink
|
||||
:to="`/${chain}/staking/${v.validator_address}`"
|
||||
>{{
|
||||
v.validator_address
|
||||
}}</RouterLink
|
||||
>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="entry in v.entries">
|
||||
<td class="py-3">{{ entry.creation_height }}</td>
|
||||
<td class="py-3">
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: entry.initial_balance,
|
||||
denom: stakingStore.params.bond_denom,
|
||||
},
|
||||
true,
|
||||
'0,0.[00]'
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: entry.balance,
|
||||
denom: stakingStore.params.bond_denom,
|
||||
},
|
||||
true,
|
||||
'0,0.[00]'
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<Countdown :time="new Date(entry.completion_time).getTime() - new Date().getTime()" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tr>
|
||||
<td class="text-caption text-primary py-3 bg-slate-200" colspan="10">
|
||||
<RouterLink :to="`/${chain}/staking/${v.validator_address}`">{{ v.validator_address }}</RouterLink>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="entry in v.entries">
|
||||
<td class="py-3">{{ entry.creation_height }}</td>
|
||||
<td class="py-3">
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: entry.initial_balance,
|
||||
denom: stakingStore.params.bond_denom,
|
||||
},
|
||||
true,
|
||||
'0,0.[00]'
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
{{
|
||||
format.formatToken(
|
||||
{
|
||||
amount: entry.balance,
|
||||
denom: stakingStore.params.bond_denom,
|
||||
},
|
||||
true,
|
||||
'0,0.[00]'
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td class="py-3">
|
||||
<Countdown :time="new Date(entry.completion_time).getTime() - new Date().getTime()" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@ -520,15 +433,24 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm">
|
||||
<tr v-if="txs.length === 0"><td colspan="10"><div class="text-center">{{ $t('account.no_transactions') }}</div></td></tr>
|
||||
<tr v-if="txs.length === 0">
|
||||
<td colspan="10">
|
||||
<div class="text-center">{{ $t('account.no_transactions') }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="(v, index) in txs" :key="index">
|
||||
<td class="text-sm py-3">
|
||||
<RouterLink :to="`/${chain}/block/${v.height}`" class="text-primary dark:invert">{{
|
||||
v.height
|
||||
}}</RouterLink>
|
||||
<RouterLink
|
||||
:to="`/${chain}/block/${v.height}`"
|
||||
class="text-primary dark:invert"
|
||||
>{{ v.height }}</RouterLink
|
||||
>
|
||||
</td>
|
||||
<td class="truncate py-3" style="max-width: 200px">
|
||||
<RouterLink :to="`/${chain}/tx/${v.txhash}`" class="text-primary dark:invert">
|
||||
<RouterLink
|
||||
:to="`/${chain}/tx/${v.txhash}`"
|
||||
class="text-primary dark:invert"
|
||||
>
|
||||
{{ v.txhash }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
@ -536,14 +458,13 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
<div class="mr-2">
|
||||
{{ format.messages(v.tx.body.messages) }}
|
||||
</div>
|
||||
<Icon
|
||||
v-if="v.code === 0"
|
||||
icon="mdi-check"
|
||||
class="text-success text-lg"
|
||||
/>
|
||||
<Icon v-if="v.code === 0" icon="mdi-check" class="text-success text-lg" />
|
||||
<Icon v-else icon="mdi-multiply" class="text-error text-lg" />
|
||||
</td>
|
||||
<td class="py-3">{{ format.toLocaleDate(v.timestamp) }} <span class=" text-xs">({{ format.toDay(v.timestamp, 'from') }})</span> </td>
|
||||
<td class="py-3">
|
||||
{{ format.toLocaleDate(v.timestamp) }}
|
||||
<span class="text-xs">({{ format.toDay(v.timestamp, 'from') }})</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -564,30 +485,38 @@ function mapAmount(events:{type: string, attributes: {key: string, value: string
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm">
|
||||
<tr v-if="recentReceived.length === 0"><td colspan="10"><div class="text-center">{{ $t('account.no_transactions') }}</div></td></tr>
|
||||
<tr v-if="recentReceived.length === 0">
|
||||
<td colspan="10">
|
||||
<div class="text-center">{{ $t('account.no_transactions') }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-for="(v, index) in recentReceived" :key="index">
|
||||
<td class="text-sm py-3">
|
||||
<RouterLink :to="`/${chain}/block/${v.height}`" class="text-primary dark:invert">{{
|
||||
v.height
|
||||
}}</RouterLink>
|
||||
<RouterLink
|
||||
:to="`/${chain}/block/${v.height}`"
|
||||
class="text-primary dark:invert"
|
||||
>{{ v.height }}</RouterLink
|
||||
>
|
||||
</td>
|
||||
<td class="truncate py-3" style="max-width: 200px">
|
||||
<RouterLink :to="`/${chain}/tx/${v.txhash}`" class="text-primary dark:invert">
|
||||
<RouterLink
|
||||
:to="`/${chain}/tx/${v.txhash}`"
|
||||
class="text-primary dark:invert"
|
||||
>
|
||||
{{ v.txhash }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
<td class="flex items-center py-3">
|
||||
<div class="mr-2">
|
||||
{{ mapAmount(v.events)?.join(", ")}}
|
||||
{{ mapAmount(v.events)?.join(', ') }}
|
||||
</div>
|
||||
<Icon
|
||||
v-if="v.code === 0"
|
||||
icon="mdi-check"
|
||||
class="text-success text-lg"
|
||||
/>
|
||||
<Icon v-if="v.code === 0" icon="mdi-check" class="text-success text-lg" />
|
||||
<Icon v-else icon="mdi-multiply" class="text-error text-lg" />
|
||||
</td>
|
||||
<td class="py-3">{{ format.toLocaleDate(v.timestamp) }} <span class=" text-xs">({{ format.toDay(v.timestamp, 'from') }})</span> </td>
|
||||
<td class="py-3">
|
||||
{{ format.toLocaleDate(v.timestamp) }}
|
||||
<span class="text-xs">({{ format.toDay(v.timestamp, 'from') }})</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@ -6,72 +6,73 @@ import { onMounted } from 'vue';
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
const props = defineProps(['chain']);
|
||||
|
||||
const chainStore = useBlockchain()
|
||||
const chainStore = useBlockchain();
|
||||
|
||||
const accounts = ref([] as AuthAccount[])
|
||||
const pageRequest = ref(new PageRequest())
|
||||
const pageResponse = ref({} as Pagination)
|
||||
const accounts = ref([] as AuthAccount[]);
|
||||
const pageRequest = ref(new PageRequest());
|
||||
const pageResponse = ref({} as Pagination);
|
||||
|
||||
onMounted(() => {
|
||||
pageload(1)
|
||||
pageload(1);
|
||||
});
|
||||
|
||||
function pageload(p: number) {
|
||||
pageRequest.value.setPage(p)
|
||||
chainStore.rpc.getAuthAccounts(pageRequest.value).then(x => {
|
||||
accounts.value = x.accounts
|
||||
pageResponse.value = x.pagination
|
||||
pageRequest.value.setPage(p);
|
||||
chainStore.rpc.getAuthAccounts(pageRequest.value).then((x) => {
|
||||
accounts.value = x.accounts;
|
||||
pageResponse.value = x.pagination;
|
||||
});
|
||||
}
|
||||
|
||||
function showType(v: string) {
|
||||
return v.replace("/cosmos.auth.v1beta1.", "")
|
||||
return v.replace('/cosmos.auth.v1beta1.', '');
|
||||
}
|
||||
function findField(v: any, field: string) {
|
||||
if(!v || Array.isArray(v) || typeof v === 'string') return null
|
||||
const fields = Object.keys(v)
|
||||
if(fields.includes(field)) {
|
||||
return v[field]
|
||||
}
|
||||
for(let i= 0; i < fields.length; i++) {
|
||||
const re: any = findField(v[fields[i]], field)
|
||||
if(re) return re
|
||||
}
|
||||
if (!v || Array.isArray(v) || typeof v === 'string') return null;
|
||||
const fields = Object.keys(v);
|
||||
if (fields.includes(field)) {
|
||||
return v[field];
|
||||
}
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
const re: any = findField(v[fields[i]], field);
|
||||
if (re) return re;
|
||||
}
|
||||
}
|
||||
function showAddress(v: any) {
|
||||
return findField(v, 'address')
|
||||
return findField(v, 'address');
|
||||
}
|
||||
function showAccountNumber(v: any) {
|
||||
return findField(v, 'account_number')
|
||||
return findField(v, 'account_number');
|
||||
}
|
||||
function showSequence(v: any) {
|
||||
return findField(v, 'sequence')
|
||||
return findField(v, 'sequence');
|
||||
}
|
||||
function showPubkey(v: any) {
|
||||
return findField(v, 'pub_key')
|
||||
return findField(v, 'pub_key');
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div class=" overflow-x-auto">
|
||||
<table class="table table-compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{{ $t('account.type') }}</td>
|
||||
<td>{{ $t('account.address') }}</td>
|
||||
<td>{{ $t('account.acc_num') }}</td>
|
||||
<td>{{ $t('account.sequence') }}</td>
|
||||
<td>{{ $t('account.pub_key') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="acc in accounts">
|
||||
<td>{{ showType(acc['@type']) }}</td>
|
||||
<td><RouterLink :to="`/${chain}/account/${showAddress(acc)}`">{{ showAddress(acc) }}</RouterLink></td>
|
||||
<td>{{ showAccountNumber(acc) }}</td>
|
||||
<td>{{ showSequence(acc) }}</td>
|
||||
<td>{{ showPubkey(acc) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<PaginationBar :limit="pageRequest.limit" :total="pageResponse.total" :callback="pageload" />
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{{ $t('account.type') }}</td>
|
||||
<td>{{ $t('account.address') }}</td>
|
||||
<td>{{ $t('account.acc_num') }}</td>
|
||||
<td>{{ $t('account.sequence') }}</td>
|
||||
<td>{{ $t('account.pub_key') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="acc in accounts">
|
||||
<td>{{ showType(acc['@type']) }}</td>
|
||||
<td>
|
||||
<RouterLink :to="`/${chain}/account/${showAddress(acc)}`">{{ showAddress(acc) }}</RouterLink>
|
||||
</td>
|
||||
<td>{{ showAccountNumber(acc) }}</td>
|
||||
<td>{{ showSequence(acc) }}</td>
|
||||
<td>{{ showPubkey(acc) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<PaginationBar :limit="pageRequest.limit" :total="pageResponse.total" :callback="pageload" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -12,9 +12,9 @@ import Countdown from '@/components/Countdown.vue';
|
||||
const props = defineProps(['height', 'chain']);
|
||||
|
||||
const store = useBaseStore();
|
||||
const format = useFormatter()
|
||||
const current = ref({} as Block)
|
||||
const target = ref(Number(props.height || 0))
|
||||
const format = useFormatter();
|
||||
const current = ref({} as Block);
|
||||
const target = ref(Number(props.height || 0));
|
||||
|
||||
const height = computed(() => {
|
||||
return Number(current.value.block?.header?.height || props.height || 0);
|
||||
@ -22,40 +22,40 @@ const height = computed(() => {
|
||||
|
||||
const isFutureBlock = computed({
|
||||
get: () => {
|
||||
const latest = store.latest?.block?.header.height
|
||||
const isFuture = latest ? target.value > Number(latest) : true
|
||||
if (!isFuture && !current.value.block_id) store.fetchBlock(target.value).then(x => current.value = x)
|
||||
return isFuture
|
||||
const latest = store.latest?.block?.header.height;
|
||||
const isFuture = latest ? target.value > Number(latest) : true;
|
||||
if (!isFuture && !current.value.block_id) store.fetchBlock(target.value).then((x) => (current.value = x));
|
||||
return isFuture;
|
||||
},
|
||||
set: val => {
|
||||
console.log(val)
|
||||
}
|
||||
})
|
||||
set: (val) => {
|
||||
console.log(val);
|
||||
},
|
||||
});
|
||||
|
||||
const remainingBlocks = computed(() => {
|
||||
const latest = store.latest?.block?.header.height
|
||||
return latest ? Number(target.value) - Number(latest) : 0
|
||||
})
|
||||
const latest = store.latest?.block?.header.height;
|
||||
return latest ? Number(target.value) - Number(latest) : 0;
|
||||
});
|
||||
|
||||
const estimateTime = computed(() => {
|
||||
const seconds = remainingBlocks.value * Number((store.blocktime / 1000).toFixed()) * 1000
|
||||
return seconds
|
||||
})
|
||||
const seconds = Number((remainingBlocks.value * store.blocktime).toFixed(2));
|
||||
return seconds;
|
||||
});
|
||||
|
||||
const estimateDate = computed(() => {
|
||||
return new Date(new Date().getTime() + estimateTime.value)
|
||||
})
|
||||
return new Date(new Date().getTime() + estimateTime.value);
|
||||
});
|
||||
|
||||
const edit = ref(false)
|
||||
const newHeight = ref(props.height)
|
||||
const edit = ref(false);
|
||||
const newHeight = ref(props.height);
|
||||
function updateTarget() {
|
||||
target.value = Number(newHeight.value)
|
||||
console.log(target.value)
|
||||
target.value = Number(newHeight.value);
|
||||
console.log(target.value);
|
||||
}
|
||||
|
||||
onBeforeRouteUpdate(async (to, from, next) => {
|
||||
if (from.path !== to.path) {
|
||||
store.fetchBlock(String(to.params.height)).then(x => current.value = x);
|
||||
store.fetchBlock(String(to.params.height)).then((x) => (current.value = x));
|
||||
next();
|
||||
}
|
||||
});
|
||||
@ -66,29 +66,37 @@ onBeforeRouteUpdate(async (to, from, next) => {
|
||||
<div v-if="remainingBlocks > 0">
|
||||
<div class="text-primary font-bold text-lg my-10">#{{ target }}</div>
|
||||
<Countdown :time="estimateTime" css="md:!text-5xl font-sans md:mx-5" />
|
||||
<div class="my-5">{{ $t('block.estimated_time') }}: <span class="text-xl font-bold">{{ format.toLocaleDate(estimateDate) }}</span>
|
||||
<div class="my-5">
|
||||
{{ $t('block.estimated_time') }}:
|
||||
<span class="text-xl font-bold">{{ format.toLocaleDate(estimateDate) }}</span>
|
||||
</div>
|
||||
<div class="pt-10 flex justify-center">
|
||||
<table class="table w-max rounded-lg bg-base-100">
|
||||
<tbody>
|
||||
<tr class="hover cursor-pointer" @click="edit = !edit">
|
||||
<td>{{ $t('block.countdown_for_block') }}:</td>
|
||||
<td class="text-right"><span class="md:!ml-40">{{ target }}</span></td>
|
||||
<td class="text-right">
|
||||
<span class="md:!ml-40">{{ target }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="edit">
|
||||
<td colspan="2" class="text-center">
|
||||
<h3 class="text-lg font-bold">{{ $t('block.countdown_for_block_input') }}</h3>
|
||||
<p class="py-4">
|
||||
<div class="join">
|
||||
<input class="input input-bordered join-item" v-model="newHeight" type="number" />
|
||||
<button class="btn btn-primary join-item" @click="updateTarget()">{{ $t('block.btn_update') }}</button>
|
||||
<div class="py-4">
|
||||
<div class="join">
|
||||
<input class="input input-bordered join-item" v-model="newHeight" type="number" />
|
||||
<button class="btn btn-primary join-item" @click="updateTarget()">
|
||||
{{ $t('block.btn_update') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('block.current_height') }}:</td>
|
||||
<td class="text-right">#{{ store.latest?.block?.header.height }}</td>
|
||||
<td class="text-right">
|
||||
#{{ store.latest?.block?.header.height }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('block.remaining_blocks') }}:</td>
|
||||
@ -96,12 +104,13 @@ onBeforeRouteUpdate(async (to, from, next) => {
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('block.average_block_time') }}:</td>
|
||||
<td class="text-right">{{ (store.blocktime / 1000).toFixed(1) }}s</td>
|
||||
<td class="text-right">
|
||||
{{ (store.blocktime / 1000).toFixed(1) }}s
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
@ -109,12 +118,16 @@ onBeforeRouteUpdate(async (to, from, next) => {
|
||||
<h2 class="card-title flex flex-row justify-between">
|
||||
<p class="">#{{ current.block?.header?.height }}</p>
|
||||
<div class="flex" v-if="props.height">
|
||||
<RouterLink :to="`/${store.blockchain.chainName}/block/${height - 1}`"
|
||||
class="btn btn-primary btn-sm p-1 text-2xl mr-2">
|
||||
<RouterLink
|
||||
:to="`/${store.blockchain.chainName}/block/${height - 1}`"
|
||||
class="btn btn-primary btn-sm p-1 text-2xl mr-2"
|
||||
>
|
||||
<Icon icon="mdi-arrow-left" class="w-full h-full" />
|
||||
</RouterLink>
|
||||
<RouterLink :to="`/${store.blockchain.chainName}/block/${height + 1}`"
|
||||
class="btn btn-primary btn-sm p-1 text-2xl">
|
||||
<RouterLink
|
||||
:to="`/${store.blockchain.chainName}/block/${height + 1}`"
|
||||
class="btn btn-primary btn-sm p-1 text-2xl"
|
||||
>
|
||||
<Icon icon="mdi-arrow-right" class="w-full h-full" />
|
||||
</RouterLink>
|
||||
</div>
|
||||
@ -125,18 +138,25 @@ onBeforeRouteUpdate(async (to, from, next) => {
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title flex flex-row justify-between">{{ $t('block.block_header') }}</h2>
|
||||
<h2 class="card-title flex flex-row justify-between">
|
||||
{{ $t('block.block_header') }}
|
||||
</h2>
|
||||
<DynamicComponent :value="current.block?.header" />
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title flex flex-row justify-between">{{ $t('account.transactions') }}</h2>
|
||||
<h2 class="card-title flex flex-row justify-between">
|
||||
{{ $t('account.transactions') }}
|
||||
</h2>
|
||||
<TxsElement :value="current.block?.data?.txs" />
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title flex flex-row justify-between">{{ $t('block.last_commit') }}</h2>
|
||||
<h2 class="card-title flex flex-row justify-between">
|
||||
{{ $t('block.last_commit') }}
|
||||
</h2>
|
||||
<DynamicComponent :value="current.block?.last_commit" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></template>
|
||||
</template>
|
||||
|
||||
@ -1,68 +1,42 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { decodeTxRaw, type DecodedTxRaw } from '@cosmjs/proto-signing';
|
||||
import { useBlockchain } from '@/stores';
|
||||
import { hashTx } from '@/libs';
|
||||
import type { Block } from '@/types';
|
||||
import { useBaseStore, useBlockchain } from '@/stores';
|
||||
|
||||
export const useBlockModule = defineStore('blockModule', {
|
||||
state: () => {
|
||||
return {
|
||||
latest: {} as Block,
|
||||
current: {} as Block,
|
||||
recents: [] as Block[],
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
baseStore() {
|
||||
return useBaseStore();
|
||||
},
|
||||
blockchain() {
|
||||
return useBlockchain();
|
||||
},
|
||||
blocktime() {
|
||||
if (this.recents.length < 2) return 6000;
|
||||
return 6000; // todo later
|
||||
return useBaseStore().blocktime;
|
||||
},
|
||||
txsInRecents() {
|
||||
const txs = [] as { hash: string; tx: DecodedTxRaw }[];
|
||||
this.recents.forEach((x) =>
|
||||
x.block?.data?.txs.forEach((tx: Uint8Array) => {
|
||||
if (tx) {
|
||||
try {
|
||||
txs.push({
|
||||
hash: hashTx(tx),
|
||||
tx: decodeTxRaw(tx),
|
||||
});
|
||||
} catch (e) {}
|
||||
}
|
||||
})
|
||||
);
|
||||
return txs;
|
||||
return useBaseStore().txsInRecents;
|
||||
},
|
||||
latest(){
|
||||
return useBaseStore().latest;
|
||||
},
|
||||
earliest() {
|
||||
return useBaseStore().earliest;
|
||||
},
|
||||
recents() {
|
||||
return useBaseStore().recents;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
initial() {
|
||||
this.clearRecentBlocks();
|
||||
this.autoFetch();
|
||||
},
|
||||
async clearRecentBlocks() {
|
||||
this.recents = [];
|
||||
},
|
||||
autoFetch() {
|
||||
this.fetchLatest().then((x) => {
|
||||
const timer = this.autoFetch;
|
||||
this.latest = x;
|
||||
// if(this.recents.length >= 50) this.recents.pop()
|
||||
// this.recents.push(x)
|
||||
// setTimeout(timer, 6000)
|
||||
});
|
||||
return this.baseStore.clearRecentBlocks()
|
||||
},
|
||||
async fetchLatest() {
|
||||
this.latest = await this.blockchain.rpc?.getBaseBlockLatest();
|
||||
if (this.recents.length >= 50) this.recents.shift();
|
||||
this.recents.push(this.latest);
|
||||
return this.latest;
|
||||
return this.baseStore.fetchLatest()
|
||||
},
|
||||
async fetchBlock(height: string) {
|
||||
this.current = await this.blockchain.rpc?.getBaseBlockAt(height);
|
||||
return this.current;
|
||||
return this.baseStore.fetchBlock(height)
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -7,52 +7,56 @@ const props = defineProps(['chain']);
|
||||
|
||||
const tab = ref('blocks');
|
||||
|
||||
const base = useBaseStore()
|
||||
const base = useBaseStore();
|
||||
|
||||
const format = useFormatter();
|
||||
|
||||
const list = computed(() => {
|
||||
// const recents = base.recents
|
||||
// return recents.sort((a, b) => (Number(b.block.header.height) - Number(a.block.header.height)))
|
||||
return base.recents
|
||||
})
|
||||
// const recents = base.recents
|
||||
// return recents.sort((a, b) => (Number(b.block.header.height) - Number(a.block.header.height)))
|
||||
return base.recents;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === 'blocks' }"
|
||||
@click="tab = 'blocks'">{{ $t('block.recent') }}</a>
|
||||
<RouterLink class="tab text-gray-400 uppercase"
|
||||
:to="`/${chain}/block/${Number(base.latest?.block?.header.height||0) + 10000}`"
|
||||
>{{ $t('block.future') }}</RouterLink>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'blocks'">
|
||||
|
||||
<TxsInBlocksChart />
|
||||
|
||||
<div class="grid xl:!grid-cols-6 md:!grid-cols-4 grid-cols-1 gap-3">
|
||||
<RouterLink v-for="item in list"
|
||||
class="flex flex-col justify-between rounded p-4 shadow bg-base-100"
|
||||
:to="`/${chain}/block/${item.block.header.height}`">
|
||||
<div class="flex justify-between">
|
||||
<h3 class="text-md font-bold sm:!text-lg">
|
||||
{{ item.block.header.height }}
|
||||
</h3>
|
||||
<span class="rounded text-xs whitespace-nowrap font-medium text-green-600">
|
||||
{{ format.toDay(item.block?.header?.time, 'from') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between tooltip" data-tip="Block Proposor">
|
||||
<div class="mt-2 hidden text-sm sm:!block truncate">
|
||||
<span>{{ format.validator(item.block?.header?.proposer_address) }}</span>
|
||||
</div>
|
||||
<span class="text-right mt-1 whitespace-nowrap"> {{ item.block?.data?.txs.length }} txs </span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === 'blocks' }" @click="tab = 'blocks'">{{
|
||||
$t('block.recent')
|
||||
}}</a>
|
||||
<RouterLink
|
||||
class="tab text-gray-400 uppercase"
|
||||
:to="`/${chain}/block/${Number(base.latest?.block?.header.height || 0) + 10000}`"
|
||||
>{{ $t('block.future') }}</RouterLink
|
||||
>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'blocks'">
|
||||
<TxsInBlocksChart />
|
||||
|
||||
<div class="grid xl:!grid-cols-6 md:!grid-cols-4 grid-cols-1 gap-3">
|
||||
<RouterLink
|
||||
v-for="item in list"
|
||||
class="flex flex-col justify-between rounded p-4 shadow bg-base-100"
|
||||
:to="`/${chain}/block/${item.block.header.height}`"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h3 class="text-md font-bold sm:!text-lg">
|
||||
{{ item.block.header.height }}
|
||||
</h3>
|
||||
<span class="rounded text-xs whitespace-nowrap font-medium text-green-600">
|
||||
{{ format.toDay(item.block?.header?.time, 'from') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex justify-between tooltip" data-tip="Block Proposor">
|
||||
<div class="mt-2 hidden text-sm sm:!block truncate">
|
||||
<span>{{ format.validator(item.block?.header?.proposer_address) }}</span>
|
||||
</div>
|
||||
<span class="text-right mt-1 whitespace-nowrap"> {{ item.block?.data?.txs.length }} txs </span>
|
||||
</div>
|
||||
</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<route>
|
||||
|
||||
@ -1,15 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import fetch from 'cross-fetch';
|
||||
import { onMounted, ref, computed, onUnmounted } from 'vue';
|
||||
import { useBlockchain, useFormatter, useStakingStore } from '@/stores';
|
||||
import { useBlockchain, useFormatter, useStakingStore, useBaseStore } from '@/stores';
|
||||
import { consensusPubkeyToHexAddress } from '@/libs';
|
||||
|
||||
const format = useFormatter();
|
||||
const chainStore = useBlockchain();
|
||||
const stakingStore = useStakingStore();
|
||||
const rpcList = ref(
|
||||
chainStore.current?.endpoints?.rpc || [{ address: '', provider: '' }]
|
||||
);
|
||||
const baseStore = useBaseStore();
|
||||
const rpcList = ref(chainStore.current?.endpoints?.rpc || [{ address: '', provider: '' }]);
|
||||
let rpc = ref('');
|
||||
const validators = ref(stakingStore.validators);
|
||||
|
||||
@ -30,10 +29,10 @@ onMounted(async () => {
|
||||
rpc.value = rpcList.value[0].address + '/consensus_state';
|
||||
await fetchPosition();
|
||||
update();
|
||||
clearTime()
|
||||
clearTime();
|
||||
timer = setInterval(() => {
|
||||
update();
|
||||
}, 6000);
|
||||
}, Math.round(baseStore.blocktime / 2));
|
||||
});
|
||||
onUnmounted(() => {
|
||||
clearTime();
|
||||
@ -80,7 +79,7 @@ function color(i: number, txt: string) {
|
||||
}
|
||||
return txt === 'nil-Vote' ? 'gray-700' : 'success';
|
||||
}
|
||||
async function onChange () {
|
||||
async function onChange() {
|
||||
httpstatus.value = 200;
|
||||
httpStatusText.value = '';
|
||||
roundState.value = {};
|
||||
@ -89,7 +88,7 @@ async function onChange () {
|
||||
update();
|
||||
timer = setInterval(() => {
|
||||
update();
|
||||
}, 6000);
|
||||
}, Math.round(baseStore.blocktime / 2));
|
||||
}
|
||||
|
||||
async function fetchPosition() {
|
||||
@ -135,11 +134,7 @@ async function update() {
|
||||
|
||||
// find the highest onboard rate
|
||||
roundState.value?.height_vote_set?.forEach((element: any) => {
|
||||
const rates = Number(
|
||||
element.prevotes_bit_array.substring(
|
||||
element.prevotes_bit_array.length - 4
|
||||
)
|
||||
);
|
||||
const rates = Number(element.prevotes_bit_array.substring(element.prevotes_bit_array.length - 4));
|
||||
if (rates > 0) {
|
||||
rate.value = `${(rates * 100).toFixed()}%`;
|
||||
}
|
||||
@ -166,79 +161,61 @@ async function update() {
|
||||
v-model="rpc"
|
||||
/> -->
|
||||
<select v-model="rpc" class="select select-bordered w-full flex-1">
|
||||
<option v-for="(item, index) in rpcList" :key="index">
|
||||
{{ item?.address }}/consensus_state
|
||||
</option>
|
||||
<option v-for="(item, index) in rpcList" :key="index">{{ item?.address }}/consensus_state</option>
|
||||
</select>
|
||||
<button class="btn btn-primary" @click="onChange">{{ $t('consensus.monitor') }}</button>
|
||||
<button class="btn btn-primary" @click="onChange">
|
||||
{{ $t('consensus.monitor') }}
|
||||
</button>
|
||||
</label>
|
||||
</div>
|
||||
<div v-if="httpstatus !== 200" class="text-error mt-1">
|
||||
{{ httpstatus }}: {{ httpStatusText }}
|
||||
</div>
|
||||
<div v-if="httpstatus !== 200" class="text-error mt-1">{{ httpstatus }}: {{ httpStatusText }}</div>
|
||||
</div>
|
||||
<!-- cards -->
|
||||
<div class="mt-4" v-if="roundState['height/round/step']">
|
||||
<div class="grid grid-cols-1 md:!grid-cols-4 auto-cols-auto gap-4 pb-4">
|
||||
<div
|
||||
class="bg-base-100 px-4 py-3 rounded shadow flex justify-between items-center"
|
||||
>
|
||||
<div class="bg-base-100 px-4 py-3 rounded shadow flex justify-between items-center">
|
||||
<div class="text-sm mb-1 flex flex-col truncate">
|
||||
<h4 class="text-lg font-semibold text-main">{{ rate }}</h4>
|
||||
<span class="text-md">{{ $t('consensus.onboard_rate') }}</span>
|
||||
</div>
|
||||
<div class="avatar placeholder">
|
||||
<div
|
||||
class="bg-rose-100 text-neutral-content rounded-full w-12 h-12"
|
||||
>
|
||||
<div class="bg-rose-100 text-neutral-content rounded-full w-12 h-12">
|
||||
<span class="text-2xl text-error font-semibold">{{ $t('consensus.o') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Height -->
|
||||
<div
|
||||
class="bg-base-100 px-4 py-3 rounded shadow flex justify-between items-center"
|
||||
>
|
||||
<div class="bg-base-100 px-4 py-3 rounded shadow flex justify-between items-center">
|
||||
<div class="text-sm mb-1 flex flex-col truncate">
|
||||
<h4 class="text-lg font-semibold text-main">{{ height }}</h4>
|
||||
<span class="text-md">{{ $t('account.height') }}</span>
|
||||
</div>
|
||||
<div class="avatar placeholder">
|
||||
<div
|
||||
class="bg-green-100 text-neutral-content rounded-full w-12 h-12"
|
||||
>
|
||||
<div class="bg-green-100 text-neutral-content rounded-full w-12 h-12">
|
||||
<span class="text-2xl text-success font-semibold">{{ $t('consensus.h') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Round -->
|
||||
<div
|
||||
class="bg-base-100 px-4 py-3 rounded shadow flex justify-between items-center"
|
||||
>
|
||||
<div class="bg-base-100 px-4 py-3 rounded shadow flex justify-between items-center">
|
||||
<div class="text-sm mb-1 flex flex-col truncate">
|
||||
<h4 class="text-lg font-semibold text-main">{{ round }}</h4>
|
||||
<span class="text-md">{{ $t('consensus.round') }}</span>
|
||||
</div>
|
||||
<div class="avatar placeholder">
|
||||
<div
|
||||
class="bg-violet-100 text-neutral-content rounded-full w-12 h-12"
|
||||
>
|
||||
<div class="bg-violet-100 text-neutral-content rounded-full w-12 h-12">
|
||||
<span class="text-2xl text-primary font-semibold">{{ $t('consensus.r') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Step -->
|
||||
<div
|
||||
class="bg-base-100 px-4 py-3 rounded shadow flex justify-between items-center"
|
||||
>
|
||||
<div class="bg-base-100 px-4 py-3 rounded shadow flex justify-between items-center">
|
||||
<div class="text-sm mb-1 flex flex-col truncate">
|
||||
<h4 class="text-lg font-semibold text-main">{{ step }}</h4>
|
||||
<span class="text-md">{{ $t('consensus.step') }}</span>
|
||||
</div>
|
||||
<div class="avatar placeholder">
|
||||
<div
|
||||
class="bg-blue-100 text-neutral-content rounded-full w-12 h-12"
|
||||
>
|
||||
<div class="bg-blue-100 text-neutral-content rounded-full w-12 h-12">
|
||||
<span class="text-2xl text-info font-semibold">{{ $t('consensus.s') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -246,21 +223,18 @@ async function update() {
|
||||
</div>
|
||||
</div>
|
||||
<!-- update -->
|
||||
<div
|
||||
class="bg-base-100 p-4 rounded shadow"
|
||||
v-if="roundState['height/round/step']"
|
||||
>
|
||||
<div class="bg-base-100 p-4 rounded shadow" v-if="roundState['height/round/step']">
|
||||
<div class="flex flex-1 flex-col truncate">
|
||||
<h2 class="text-sm card-title text-error mb-6">
|
||||
{{ $t('consensus.updated_at') }} {{ newTime || '' }}
|
||||
</h2>
|
||||
<h2 class="text-sm card-title text-error mb-6">{{ $t('consensus.updated_at') }} {{ newTime || '' }}</h2>
|
||||
<div v-for="item in roundState.height_vote_set" :key="item.round">
|
||||
<div class="text-xs mb-1">{{ $t('consensus.round') }}: {{ item.round }}</div>
|
||||
<div class="text-xs mb-1">
|
||||
{{ $t('consensus.round') }}: {{ item.round }}
|
||||
</div>
|
||||
<div class="text-xs break-words">{{ item.prevotes_bit_array }}</div>
|
||||
|
||||
<div class="flex flex-rows flex-wrap py-6">
|
||||
<div
|
||||
class=" w-48 rounded-3xl h-5 text-sm px-2 text-slate-200 leading-5"
|
||||
class="w-48 rounded-3xl h-5 text-sm px-2 leading-5"
|
||||
v-for="(pre, i) in item.prevotes"
|
||||
:key="i"
|
||||
size="sm"
|
||||
@ -269,17 +243,24 @@ async function update() {
|
||||
<span class="flex flex-rows justify-between">
|
||||
<span class="truncate">{{ showName(i, 'nil-Vote') }} </span>
|
||||
<span>
|
||||
<span class="tooltip " :data-tip="pre"
|
||||
:class="{
|
||||
'bg-green-400': String(pre).toLowerCase() !== 'nil-vote',
|
||||
'bg-red-400': String(pre).toLowerCase() === 'nil-vote'
|
||||
}"
|
||||
> </span>
|
||||
<span class="tooltip ml-1" :data-tip="item.precommits[i]"
|
||||
:class="{
|
||||
'bg-green-400': String(item.precommits[i]).toLowerCase() !== 'nil-vote',
|
||||
'bg-red-400': String(item.precommits[i]).toLowerCase() === 'nil-vote'
|
||||
}"> </span>
|
||||
<span
|
||||
class="tooltip"
|
||||
:data-tip="pre"
|
||||
:class="{
|
||||
'bg-green-400': String(pre).toLowerCase() !== 'nil-vote',
|
||||
'bg-red-400': String(pre).toLowerCase() === 'nil-vote',
|
||||
}"
|
||||
> </span
|
||||
>
|
||||
<span
|
||||
class="tooltip ml-1"
|
||||
:data-tip="item.precommits[i]"
|
||||
:class="{
|
||||
'bg-green-400': String(item.precommits[i]).toLowerCase() !== 'nil-vote',
|
||||
'bg-red-400': String(item.precommits[i]).toLowerCase() === 'nil-vote',
|
||||
}"
|
||||
> </span
|
||||
>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -287,17 +268,11 @@ async function update() {
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- alert-info -->
|
||||
<div
|
||||
class="text-[#00cfe8] bg-[rgba(0,207,232,0.12)] rounded shadow mt-4 alert-info"
|
||||
>
|
||||
<div
|
||||
class="drop-shadow-md px-4 pt-2 pb-2"
|
||||
style="box-shadow: rgba(0, 207, 232, 0.4) 0px 6px 15px -7px"
|
||||
>
|
||||
<div class="text-[#00cfe8] bg-[rgba(0,207,232,0.12)] rounded shadow mt-4 alert-info">
|
||||
<div class="drop-shadow-md px-4 pt-2 pb-2" style="box-shadow: rgba(0, 207, 232, 0.4) 0px 6px 15px -7px">
|
||||
<h2 class="text-base font-semibold">{{ $t('consensus.tips') }}</h2>
|
||||
</div>
|
||||
<div class="px-4 py-4">
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
import { BaseRestClient } from '@/libs/client';
|
||||
import { adapter, type AbstractRegistry, type Request } from '@/libs/registry';
|
||||
import {
|
||||
adapter,
|
||||
type AbstractRegistry,
|
||||
type Request,
|
||||
} from '@/libs/api/registry';
|
||||
import { defineStore } from 'pinia';
|
||||
import type {
|
||||
CodeInfo,
|
||||
@ -13,107 +17,115 @@ import type {
|
||||
import { toBase64 } from '@cosmjs/encoding';
|
||||
import { useBlockchain } from '@/stores';
|
||||
import { PageRequest } from '@/types';
|
||||
import { get } from '@/libs';
|
||||
|
||||
export interface WasmRequestRegistry extends AbstractRegistry {
|
||||
cosmwasm_code: Request<PaginabledCodeInfos>;
|
||||
cosmwasm_code_id: Request<CodeInfo>;
|
||||
cosmwasm_code_id_contracts: Request<PaginabledContracts>;
|
||||
cosmwasm_param: Request<WasmParam>;
|
||||
cosmwasm_contract_address: Request<{
|
||||
address: string;
|
||||
contract_info: ContractInfo;
|
||||
}>;
|
||||
cosmwasm_contract_address_history: Request<PaginabledContractHistory>;
|
||||
cosmwasm_contract_address_raw_query_data: Request<any>;
|
||||
cosmwasm_contract_address_smart_query_data: Request<any>;
|
||||
cosmwasm_contract_address_state: Request<PaginabledContractStates>;
|
||||
cosmwasm_wasm_contracts_creator: Request<PaginabledContracts>;
|
||||
cosmwasm_code: Request<PaginabledCodeInfos>;
|
||||
cosmwasm_code_id: Request<CodeInfo>;
|
||||
cosmwasm_code_id_contracts: Request<PaginabledContracts>;
|
||||
cosmwasm_param: Request<WasmParam>;
|
||||
cosmwasm_contract_address: Request<{
|
||||
address: string;
|
||||
contract_info: ContractInfo;
|
||||
}>;
|
||||
cosmwasm_contract_address_history: Request<PaginabledContractHistory>;
|
||||
cosmwasm_contract_address_raw_query_data: Request<any>;
|
||||
cosmwasm_contract_address_smart_query_data: Request<any>;
|
||||
cosmwasm_contract_address_state: Request<PaginabledContractStates>;
|
||||
cosmwasm_wasm_contracts_creator: Request<PaginabledContracts>;
|
||||
}
|
||||
|
||||
export const DEFAULT_VERSION: WasmRequestRegistry = {
|
||||
cosmwasm_code: { url: '/cosmwasm/wasm/v1/code', adapter },
|
||||
cosmwasm_code_id: { url: '/cosmwasm/wasm/v1/code/{code_id}', adapter },
|
||||
cosmwasm_code_id_contracts: {
|
||||
url: '/cosmwasm/wasm/v1/code/{code_id}/contracts',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_param: { url: '/cosmwasm/wasm/v1/codes/params', adapter },
|
||||
cosmwasm_contract_address: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_history: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/history',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_raw_query_data: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/raw/{query_data}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_smart_query_data: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/smart/{query_data}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_state: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/state',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_wasm_contracts_creator: {
|
||||
url: '/cosmwasm/wasm/v1/contracts/creator/{creator_address}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
|
||||
export class WasmRestClient extends BaseRestClient<WasmRequestRegistry> {
|
||||
getWasmCodeList(pr?: PageRequest) {
|
||||
// if(!pr) pr = new PageRequest()
|
||||
// const query = `?${pr.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_code, {} /*query*/);
|
||||
}
|
||||
|
||||
export const DEFAULT_VERSION: WasmRequestRegistry = {
|
||||
cosmwasm_code: { url: '/cosmwasm/wasm/v1/code', adapter },
|
||||
cosmwasm_code_id: { url: '/cosmwasm/wasm/v1/code/{code_id}', adapter },
|
||||
cosmwasm_code_id_contracts: {
|
||||
url: '/cosmwasm/wasm/v1/code/{code_id}/contracts',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_param: { url: '/cosmwasm/wasm/v1/codes/params', adapter },
|
||||
cosmwasm_contract_address: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_history: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/history',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_raw_query_data: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/raw/{query_data}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_smart_query_data: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/smart/{query_data}',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_contract_address_state: {
|
||||
url: '/cosmwasm/wasm/v1/contract/{address}/state',
|
||||
adapter,
|
||||
},
|
||||
cosmwasm_wasm_contracts_creator: {
|
||||
url: '/cosmwasm/wasm/v1/contracts/creator/{creator_address}',
|
||||
adapter,
|
||||
},
|
||||
};
|
||||
|
||||
export class WasmRestClient extends BaseRestClient<WasmRequestRegistry> {
|
||||
getWasmCodeList(pr?: PageRequest) {
|
||||
// if(!pr) pr = new PageRequest()
|
||||
// const query = `?${pr.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_code, {}, /*query*/);
|
||||
}
|
||||
getWasmCodeById(code_id: string) {
|
||||
return this.request(this.registry.cosmwasm_code, { code_id }); // `code_id` is a param in above url
|
||||
}
|
||||
getWasmCodeContracts(code_id: string, page?: PageRequest) {
|
||||
// if(!page) page = new PageRequest()
|
||||
// const query = `?${page.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_code_id_contracts, { code_id });
|
||||
}
|
||||
getWasmParams() {
|
||||
return this.request(this.registry.cosmwasm_param, {});
|
||||
}
|
||||
getWasmContracts(address: string) {
|
||||
return this.request(this.registry.cosmwasm_contract_address, { address });
|
||||
}
|
||||
getWasmContractsByCreator(creator_address: string, page?: PageRequest) {
|
||||
// if(!page) page = new PageRequest()
|
||||
// const query = `?${page.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_wasm_contracts_creator, { creator_address });
|
||||
}
|
||||
getWasmContractHistory(address: string) {
|
||||
return this.request(this.registry.cosmwasm_contract_address_history, {
|
||||
address,
|
||||
});
|
||||
}
|
||||
getWasmContractRawQuery(address: string, query: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode(query));
|
||||
return this.request(
|
||||
this.registry.cosmwasm_contract_address_raw_query_data,
|
||||
{ address, query_data }
|
||||
);
|
||||
}
|
||||
getWasmContractSmartQuery(address: string, query: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode(query));
|
||||
return this.request(
|
||||
this.registry.cosmwasm_contract_address_smart_query_data,
|
||||
{ address, query_data }
|
||||
);
|
||||
}
|
||||
getWasmContractStates(address: string, pr: PageRequest) {
|
||||
if(!pr) pr = new PageRequest()
|
||||
const query = `?${pr.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_contract_address_state, {
|
||||
address,
|
||||
}, query);
|
||||
}
|
||||
getWasmCodeById(code_id: string) {
|
||||
return this.request(this.registry.cosmwasm_code, { code_id }); // `code_id` is a param in above url
|
||||
}
|
||||
|
||||
getWasmCodeContracts(code_id: string, page?: PageRequest) {
|
||||
// if(!page) page = new PageRequest()
|
||||
// const query = `?${page.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_code_id_contracts, { code_id });
|
||||
}
|
||||
getWasmParams() {
|
||||
return this.request(this.registry.cosmwasm_param, {});
|
||||
}
|
||||
getWasmContracts(address: string) {
|
||||
return this.request(this.registry.cosmwasm_contract_address, { address });
|
||||
}
|
||||
getWasmContractsByCreator(creator_address: string, page?: PageRequest) {
|
||||
// if(!page) page = new PageRequest()
|
||||
// const query = `?${page.toQueryString()}`
|
||||
return this.request(this.registry.cosmwasm_wasm_contracts_creator, { creator_address });
|
||||
}
|
||||
getWasmContractHistory(address: string) {
|
||||
return this.request(this.registry.cosmwasm_contract_address_history, {
|
||||
address,
|
||||
});
|
||||
}
|
||||
getWasmContractRawQuery(address: string, query: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode(query));
|
||||
return this.request(this.registry.cosmwasm_contract_address_raw_query_data, { address, query_data });
|
||||
}
|
||||
getWasmContractSmartQuery(address: string, query: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode(query));
|
||||
return this.get(this.registry.cosmwasm_contract_address_smart_query_data, { address, query_data });
|
||||
}
|
||||
async getWasmContractQueries(address: string) {
|
||||
const query_data = toBase64(new TextEncoder().encode('{"":""}'));
|
||||
const { code, message } = await this.get(this.registry.cosmwasm_contract_address_smart_query_data, {
|
||||
address,
|
||||
query_data,
|
||||
});
|
||||
let re = /`(\w+)`/g;
|
||||
let x = String(message).match(re);
|
||||
return code === 2 && x ? x.map((e) => e.replaceAll('`', '')) : [];
|
||||
}
|
||||
getWasmContractStates(address: string, pr: PageRequest) {
|
||||
if (!pr) pr = new PageRequest();
|
||||
const query = `?${pr.toQueryString()}`;
|
||||
return this.request(
|
||||
this.registry.cosmwasm_contract_address_state,
|
||||
{
|
||||
address,
|
||||
},
|
||||
query
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { useWasmStore } from '../WasmStore';
|
||||
import { ref } from 'vue';
|
||||
import type {
|
||||
ContractInfo,
|
||||
PaginabledContracts,
|
||||
} from '../types';
|
||||
import type { ContractInfo, PaginabledContracts } from '../types';
|
||||
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
import { PageRequest } from '@/types';
|
||||
@ -22,11 +19,11 @@ const wasmStore = useWasmStore();
|
||||
function loadContract(pageNum: number) {
|
||||
const pr = new PageRequest();
|
||||
pr.setPage(pageNum);
|
||||
if(String(props.code_id).search(/^[\d]+$/) > -1){
|
||||
if (String(props.code_id).search(/^[\d]+$/) > -1) {
|
||||
// query with code id
|
||||
wasmStore.wasmClient.getWasmCodeContracts(props.code_id, pr).then((x) => {
|
||||
response.value = x;
|
||||
})
|
||||
});
|
||||
} else {
|
||||
// query by creator
|
||||
wasmStore.wasmClient.getWasmContractsByCreator(props.code_id, pr).then((x) => {
|
||||
@ -34,64 +31,47 @@ function loadContract(pageNum: number) {
|
||||
contracts: x.contract_addresses,
|
||||
pagination: x.pagination,
|
||||
};
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
loadContract(1);
|
||||
|
||||
|
||||
|
||||
function showInfo(address: string) {
|
||||
wasmStore.wasmClient.getWasmContracts(address).then((x) => {
|
||||
info.value = x.contract_info;
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full">
|
||||
{{ $t('cosmwasm.contract_list_code') }}: {{ props.code_id }}
|
||||
</h2>
|
||||
<h2 class="card-title truncate w-full">{{ $t('cosmwasm.contract_list_code') }}: {{ props.code_id }}</h2>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full mt-4">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
<th style="position: relative; z-index: 2">{{ $t('cosmwasm.contract_list') }}</th>
|
||||
<th style="position: relative; z-index: 2">
|
||||
{{ $t('cosmwasm.contract_list') }}
|
||||
</th>
|
||||
<th>{{ $t('account.action') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(v, index) in response.contracts"
|
||||
:key="index"
|
||||
class="hover"
|
||||
>
|
||||
<tr v-for="(v, index) in response.contracts" :key="index" class="hover">
|
||||
<td>{{ v }}</td>
|
||||
<td>
|
||||
<label
|
||||
@click="showInfo(v)"
|
||||
for="modal-contract-detail"
|
||||
class="btn btn-primary btn-xs text-xs mr-2"
|
||||
>{{ $t('cosmwasm.btn_contract') }}</label
|
||||
>
|
||||
<RouterLink
|
||||
:to="`transactions?contract=${v}`"
|
||||
class="btn btn-primary btn-xs text-xs"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_details') }}
|
||||
<label @click="showInfo(v)" for="modal-contract-detail" class="btn btn-primary btn-xs text-xs mr-2">{{
|
||||
$t('cosmwasm.btn_contract')
|
||||
}}</label>
|
||||
<RouterLink :to="`transactions?contract=${v}`" class="btn btn-primary btn-xs text-xs">
|
||||
{{ $t('cosmwasm.btn_details') }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="flex justify-between">
|
||||
<PaginationBar
|
||||
:limit="pageRequest.limit"
|
||||
:total="response.pagination?.total"
|
||||
:callback="loadContract"
|
||||
/>
|
||||
<PaginationBar :limit="pageRequest.limit" :total="response.pagination?.total" :callback="loadContract" />
|
||||
<label
|
||||
for="wasm_instantiate_contract"
|
||||
class="btn btn-primary my-5"
|
||||
@ -112,12 +92,7 @@ function showInfo(address: string) {
|
||||
<div>
|
||||
<div class="flex items-center justify-between px-3 pt-2">
|
||||
<div class="text-lg">{{ $t('cosmwasm.contract_detail') }}</div>
|
||||
<label
|
||||
@click="infoDialog = false"
|
||||
for="modal-contract-detail"
|
||||
class="btn btn-sm btn-circle"
|
||||
>✕</label
|
||||
>
|
||||
<label @click="infoDialog = false" for="modal-contract-detail" class="btn btn-sm btn-circle">✕</label>
|
||||
</div>
|
||||
<div>
|
||||
<DynamicComponent :value="info" />
|
||||
@ -125,6 +100,5 @@ function showInfo(address: string) {
|
||||
</div>
|
||||
</label>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,302 +1,355 @@
|
||||
<script lang="ts" setup>
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
import { useBaseStore, useBlockchain, useFormatter, useTxDialog } from '@/stores';
|
||||
import { PageRequest, type PaginatedBalances, type PaginatedTxs } from '@/types';
|
||||
import {
|
||||
useBaseStore,
|
||||
useBlockchain,
|
||||
useFormatter,
|
||||
useTxDialog,
|
||||
} from '@/stores';
|
||||
import {
|
||||
PageRequest,
|
||||
type PaginatedBalances,
|
||||
type PaginatedTxs,
|
||||
} from '@/types';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useWasmStore } from '../WasmStore';
|
||||
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import type { ContractInfo, PaginabledContractStates, PaginabledContracts } from '../types';
|
||||
import { post } from '@/libs';
|
||||
import type { ContractInfo, PaginabledContractStates } from '../types';
|
||||
|
||||
import { JsonViewer } from "vue3-json-viewer"
|
||||
import { JsonViewer } from 'vue3-json-viewer';
|
||||
// if you used v1.0.5 or latster ,you should add import "vue3-json-viewer/dist/index.css"
|
||||
import "vue3-json-viewer/dist/index.css";
|
||||
import WasmVerification from '@/components/WasmVerification.vue';
|
||||
import 'vue3-json-viewer/dist/index.css';
|
||||
|
||||
const chainStore = useBlockchain();
|
||||
const baseStore = useBaseStore();
|
||||
const format = useFormatter();
|
||||
const wasmStore = useWasmStore();
|
||||
|
||||
const route = useRoute()
|
||||
const page = ref(new PageRequest())
|
||||
const route = useRoute();
|
||||
const page = ref(new PageRequest());
|
||||
const pageRequest = ref(new PageRequest());
|
||||
const response = ref({} as PaginabledContracts);
|
||||
|
||||
const txs = ref<PaginatedTxs>({ txs: [], tx_responses: [], pagination: { total: "0" } });
|
||||
const txs = ref<PaginatedTxs>({ txs: [], tx_responses: [], pagination: { total: '0' } });
|
||||
|
||||
const dialog = useTxDialog();
|
||||
const infoDialog = ref(false);
|
||||
const info = ref({} as ContractInfo);
|
||||
const state = ref({} as PaginabledContractStates);
|
||||
const selected = ref('');
|
||||
const balances = ref({} as PaginatedBalances)
|
||||
const balances = ref({} as PaginatedBalances);
|
||||
|
||||
const contractAddress = String(route.query.contract)
|
||||
const contractAddress = String(route.query.contract);
|
||||
|
||||
const history = JSON.parse(localStorage.getItem("contract_history") || "{}")
|
||||
const history = JSON.parse(localStorage.getItem('contract_history') || '{}');
|
||||
|
||||
if(history[chainStore.chainName]) {
|
||||
if(!history[chainStore.chainName].includes(contractAddress)) {
|
||||
history[chainStore.chainName].push(contractAddress)
|
||||
if(history[chainStore.chainName].length > 10) {
|
||||
history[chainStore.chainName].shift()
|
||||
}
|
||||
if (history[chainStore.chainName]) {
|
||||
if (!history[chainStore.chainName].includes(contractAddress)) {
|
||||
history[chainStore.chainName].push(contractAddress);
|
||||
if (history[chainStore.chainName].length > 10) {
|
||||
history[chainStore.chainName].shift();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
history[chainStore.chainName] = [contractAddress]
|
||||
history[chainStore.chainName] = [contractAddress];
|
||||
}
|
||||
localStorage.setItem("contract_history", JSON.stringify(history))
|
||||
localStorage.setItem('contract_history', JSON.stringify(history));
|
||||
|
||||
onMounted(() => {
|
||||
const address = contractAddress
|
||||
wasmStore.wasmClient.getWasmContracts(address).then((x) => {
|
||||
info.value = x.contract_info;
|
||||
const address = contractAddress;
|
||||
wasmStore.wasmClient.getWasmContracts(address).then((x) => {
|
||||
info.value = x.contract_info;
|
||||
});
|
||||
chainStore.rpc
|
||||
.getTxs("?order_by=2&events=execute._contract_address='{address}'", { address }, page.value)
|
||||
.then((res) => {
|
||||
txs.value = res;
|
||||
});
|
||||
chainStore.rpc.getTxs("?order_by=2&events=execute._contract_address='{address}'", { address }, page.value).then(res => {
|
||||
txs.value = res
|
||||
})
|
||||
|
||||
})
|
||||
wasmStore.wasmClient.getWasmContractQueries(contractAddress).then((res) => {
|
||||
console.log('queries: ', res);
|
||||
queries.value = res;
|
||||
if (res && res.length > 0) {
|
||||
selectQuery(res[0]);
|
||||
}
|
||||
});
|
||||
|
||||
showFunds();
|
||||
showState();
|
||||
});
|
||||
|
||||
function pageload(pageNum: number) {
|
||||
page.value.setPage(pageNum)
|
||||
const address = String(route.query.contract)
|
||||
chainStore.rpc.getTxs("?order_by=2&events=execute._contract_address='{address}'", { address }, page.value).then(res => {
|
||||
txs.value = res
|
||||
})
|
||||
page.value.setPage(pageNum);
|
||||
const address = String(route.query.contract);
|
||||
chainStore.rpc
|
||||
.getTxs("?order_by=2&events=execute._contract_address='{address}'", { address }, page.value)
|
||||
.then((res) => {
|
||||
txs.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function showFunds() {
|
||||
|
||||
const address = String(route.query.contract)
|
||||
chainStore.rpc.getBankBalances(address).then(res => {
|
||||
balances.value = res
|
||||
})
|
||||
const address = String(route.query.contract);
|
||||
chainStore.rpc.getBankBalances(address).then((res) => {
|
||||
balances.value = res;
|
||||
});
|
||||
}
|
||||
function showState() {
|
||||
const address = String(route.query.contract)
|
||||
selected.value = address;
|
||||
pageloadState(1);
|
||||
const address = String(route.query.contract);
|
||||
selected.value = address;
|
||||
pageloadState(1);
|
||||
}
|
||||
|
||||
function pageloadState(p: number) {
|
||||
pageRequest.value.setPage(p);
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractStates(selected.value, pageRequest.value)
|
||||
.then((x) => {
|
||||
state.value = x;
|
||||
});
|
||||
pageRequest.value.setPage(p);
|
||||
wasmStore.wasmClient.getWasmContractStates(selected.value, pageRequest.value).then((x) => {
|
||||
state.value = x;
|
||||
});
|
||||
}
|
||||
|
||||
function showQuery() {
|
||||
query.value = '';
|
||||
result.value = '';
|
||||
query.value = '';
|
||||
result.value = '';
|
||||
}
|
||||
|
||||
function selectQuery(method: string) {
|
||||
query.value = `{"${method}":{}}`;
|
||||
}
|
||||
|
||||
function queryContract() {
|
||||
try {
|
||||
if (selectedRadio.value === 'raw') {
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractRawQuery(contractAddress, query.value)
|
||||
.then((x) => {
|
||||
result.value = x;
|
||||
})
|
||||
.catch((err) => {
|
||||
result.value = err;
|
||||
});
|
||||
} else {
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractSmartQuery(contractAddress, query.value)
|
||||
.then((x) => {
|
||||
result.value = x;
|
||||
})
|
||||
.catch((err) => {
|
||||
result.value = err;
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
result.value = JSON.stringify(err); // not works for now
|
||||
try {
|
||||
if (selectedRadio.value === 'raw') {
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractRawQuery(contractAddress, query.value)
|
||||
.then((x) => {
|
||||
result.value = x;
|
||||
})
|
||||
.catch((err) => {
|
||||
result.value = err;
|
||||
});
|
||||
} else {
|
||||
wasmStore.wasmClient
|
||||
.getWasmContractSmartQuery(contractAddress, query.value)
|
||||
.then((x) => {
|
||||
result.value = x;
|
||||
})
|
||||
.catch((err) => {
|
||||
result.value = err;
|
||||
});
|
||||
}
|
||||
// TODO, show error in the result.
|
||||
} catch (err) {
|
||||
result.value = JSON.stringify(err); // not works for now
|
||||
}
|
||||
// TODO, show error in the result.
|
||||
}
|
||||
|
||||
const radioContent = [
|
||||
{
|
||||
title: 'Raw Query',
|
||||
desc: 'Return raw result',
|
||||
value: 'raw',
|
||||
},
|
||||
{
|
||||
title: 'Smart Query',
|
||||
desc: 'Return structure result if possible',
|
||||
value: 'smart',
|
||||
},
|
||||
];
|
||||
|
||||
const selectedRadio = ref('raw');
|
||||
const selectedRadio = ref('smart');
|
||||
const query = ref('');
|
||||
const result = ref({});
|
||||
const queries = ref<string[]>([]);
|
||||
const tab = ref('detail');
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full">
|
||||
{{ $t('cosmwasm.contract_detail') }}
|
||||
</h2>
|
||||
<DynamicComponent :value="info" />
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-4">
|
||||
<RouterLink :to="`../${info.code_id}/contracts`"><span class="btn btn-xs text-xs mr-2"> Back </span> </RouterLink>
|
||||
<label @click="showFunds()" for="modal-contract-funds" class="btn btn-primary btn-xs text-xs mr-2">{{
|
||||
$t('cosmwasm.btn_funds') }}</label>
|
||||
<label class="btn btn-primary btn-xs text-xs mr-2" for="modal-contract-states" @click="showState()">
|
||||
{{ $t('cosmwasm.btn_states') }}
|
||||
</label>
|
||||
<label for="modal-contract-query" class="btn btn-primary btn-xs text-xs mr-2" @click="showQuery()">
|
||||
{{ $t('cosmwasm.btn_query') }}
|
||||
</label>
|
||||
<label for="wasm_execute_contract" class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_execute_contract', { contract: contractAddress })">
|
||||
{{ $t('cosmwasm.btn_execute') }}
|
||||
</label>
|
||||
|
||||
<label for="wasm_migrate_contract" class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_migrate_contract', { contract: contractAddress })">
|
||||
{{ $t('cosmwasm.btn_migrate') }}
|
||||
</label>
|
||||
|
||||
<label for="wasm_update_admin" class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_update_admin', { contract: contractAddress })">
|
||||
{{ $t('cosmwasm.btn_update_admin') }}
|
||||
</label>
|
||||
|
||||
<label for="wasm_clear_admin" class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_clear_admin', { contract: contractAddress })">
|
||||
{{ $t('cosmwasm.btn_clear_admin') }}
|
||||
</label>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full mt-4 mb-2">Transactions</h2>
|
||||
<table class="table">
|
||||
<thead class=" bg-base-200">
|
||||
<tr>
|
||||
<td> {{ $t('ibc.height') }}</td>
|
||||
<td>{{ $t('ibc.txhash') }}</td>
|
||||
<td> {{ $t('ibc.messages') }}</td>
|
||||
<td>{{ $t('ibc.time') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="resp in txs?.tx_responses">
|
||||
<td>{{ resp.height }}</td>
|
||||
<td>
|
||||
<div class="text-xs truncate text-primary dark:invert">
|
||||
<RouterLink :to="`/${chainStore.chainName}/tx/${resp.txhash}`">{{ resp.txhash }}
|
||||
</RouterLink>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex">
|
||||
{{ format.messages(resp.tx.body.messages) }}
|
||||
<Icon v-if="resp.code === 0" icon="mdi-check" class="text-success text-lg" />
|
||||
<Icon v-else icon="mdi-multiply" class="text-error text-lg" />
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ format.toLocaleDate(resp.timestamp) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginationBar :limit="page.limit" :total="txs.pagination?.total" :callback="pageload" />
|
||||
</div>
|
||||
|
||||
<WasmVerification :contract="contractAddress"/>
|
||||
|
||||
<div>
|
||||
<input type="checkbox" id="modal-contract-funds" class="modal-toggle" />
|
||||
<label for="modal-contract-funds" class="modal cursor-pointer">
|
||||
<label class="modal-box relative p-2" for="">
|
||||
<div>
|
||||
<div class="flex items-center justify-between px-3 pt-2">
|
||||
<div class="text-lg">{{ $t('cosmwasm.contract_balances') }}</div>
|
||||
<label for="modal-contract-funds" class="btn btn-sm btn-circle">✕</label>
|
||||
</div>
|
||||
<ul class="menu mt-5">
|
||||
<li v-for="b in balances.balances">
|
||||
<a class="flex justify-between"><span>{{ format.formatToken(b) }}</span> {{ b.amount }} </a>
|
||||
</li>
|
||||
<li v-if="balances.pagination?.total === '0'" class="my-10 text-center">{{
|
||||
$t('cosmwasm.no_escrowed_assets') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</label>
|
||||
</label>
|
||||
|
||||
<input type="checkbox" id="modal-contract-states" class="modal-toggle" />
|
||||
<label for="modal-contract-states" class="modal cursor-pointer">
|
||||
<label class="modal-box !w-11/12 !max-w-5xl" for="">
|
||||
<div>
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg">{{ $t('cosmwasm.contract_states') }}</div>
|
||||
<label for="modal-contract-states" class="btn btn-sm btn-circle">✕</label>
|
||||
</div>
|
||||
<div class="overflow-auto">
|
||||
<JsonViewer :value="state.models?.map(v => ({key: format.hexToString(v.key), value: JSON.parse(format.base64ToString(v.value))}))||''" :theme="baseStore.theme||'dark'" style="background: transparent;" copyable boxed sort :expand-depth="5"/>
|
||||
<PaginationBar :limit="pageRequest.limit" :total="state.pagination?.total"
|
||||
:callback="pageloadState" />
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</label>
|
||||
|
||||
<input type="checkbox" id="modal-contract-query" class="modal-toggle" />
|
||||
<label for="modal-contract-query" class="modal cursor-pointer">
|
||||
<label class="modal-box !w-11/12 !max-w-5xl" for="">
|
||||
<div>
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg font-semibold">{{ $t('cosmwasm.query_contract') }}</div>
|
||||
<label for="modal-contract-query" class="btn btn-sm btn-circle">✕</label>
|
||||
</div>
|
||||
<div class="px-3">
|
||||
<div>
|
||||
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||
<div class="form-control border rounded px-4" v-for="(item, index) of radioContent"
|
||||
:key="index" :class="{ 'pt-2': index === 0 }">
|
||||
<label class="label cursor-pointer justify-start"
|
||||
@click="selectedRadio = item?.value">
|
||||
<input type="radio" name="radio-10" class="radio radio-sm radio-primary mr-4"
|
||||
:checked="item?.value === selectedRadio"
|
||||
style="border: 1px solid #d2d6dc" />
|
||||
<div>
|
||||
<div class="text-base font-semibold">
|
||||
{{ item?.title }}
|
||||
</div>
|
||||
<div class="text-xs">{{ item?.desc }}</div>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<textarea v-model="query" placeholder="Query String, {}" label="Query String"
|
||||
class="my-2 textarea textarea-bordered w-full" />
|
||||
|
||||
<JsonViewer :value="result" :theme="baseStore.theme" style="background: transparent;" copyable boxed sort :expand-depth="5"/>
|
||||
|
||||
</div>
|
||||
<div class="mt-4 mb-4 text-center">
|
||||
<button class="btn btn-primary px-4 text-white" @click="queryContract()">
|
||||
{{ $t('cosmwasm.query_contract') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === 'detail' }" @click="tab = 'detail'">{{
|
||||
$t('cosmwasm.contract_detail')
|
||||
}}</a>
|
||||
<a
|
||||
class="tab text-gray-400 uppercase"
|
||||
:class="{ 'tab-active': tab === 'transaction' }"
|
||||
@click="tab = 'transaction'"
|
||||
>Transactions</a
|
||||
>
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === 'query' }" @click="tab = 'query'">Query</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-show="tab === 'detail'">
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full">
|
||||
{{ $t('cosmwasm.contract_detail') }}
|
||||
</h2>
|
||||
<DynamicComponent :value="info" />
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2">
|
||||
<div class="text-lg">{{ $t('cosmwasm.contract_balances') }}</div>
|
||||
</div>
|
||||
<ul class="menu mt-5">
|
||||
<li v-for="b in balances.balances">
|
||||
<a class="flex justify-between"
|
||||
><span>{{ format.formatToken(b) }}</span> {{ b.amount }}
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="balances.pagination?.total === '0'" class="my-10 text-center">
|
||||
{{ $t('cosmwasm.no_escrowed_assets') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg">{{ $t('cosmwasm.contract_states') }}</div>
|
||||
</div>
|
||||
<div class="overflow-auto">
|
||||
<JsonViewer
|
||||
:value="
|
||||
state.models?.map((v) => ({
|
||||
key: format.hexToString(v.key),
|
||||
value: JSON.parse(format.base64ToString(v.value)),
|
||||
})) || ''
|
||||
"
|
||||
:theme="baseStore.theme || 'dark'"
|
||||
style="background: transparent"
|
||||
copyable
|
||||
boxed
|
||||
sort
|
||||
:expand-depth="5"
|
||||
/>
|
||||
<PaginationBar :limit="pageRequest.limit" :total="state.pagination?.total" :callback="pageloadState" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mb-4">
|
||||
<RouterLink :to="`../${info.code_id}/contracts`"
|
||||
><span class="btn btn-xs text-xs mr-2"> Back </span>
|
||||
</RouterLink>
|
||||
|
||||
<label
|
||||
for="wasm_migrate_contract"
|
||||
class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_migrate_contract', { contract: contractAddress })"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_migrate') }}
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="wasm_update_admin"
|
||||
class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_update_admin', { contract: contractAddress })"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_update_admin') }}
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="wasm_clear_admin"
|
||||
class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_clear_admin', { contract: contractAddress })"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_clear_admin') }}
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="wasm_execute_contract"
|
||||
class="btn btn-primary btn-xs text-xs mr-2"
|
||||
@click="dialog.open('wasm_execute_contract', { contract: contractAddress })"
|
||||
>
|
||||
{{ $t('cosmwasm.btn_execute') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'transaction'" class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full mt-4 mb-2">Transactions</h2>
|
||||
<table class="table">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
<td>{{ $t('ibc.height') }}</td>
|
||||
<td>{{ $t('ibc.txhash') }}</td>
|
||||
<td>{{ $t('ibc.messages') }}</td>
|
||||
<td>{{ $t('ibc.time') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="resp in txs?.tx_responses">
|
||||
<td>{{ resp.height }}</td>
|
||||
<td>
|
||||
<div class="text-xs truncate text-primary dark:invert">
|
||||
<RouterLink :to="`/${chainStore.chainName}/tx/${resp.txhash}`">{{ resp.txhash }} </RouterLink>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex">
|
||||
{{ format.messages(resp.tx.body.messages) }}
|
||||
<Icon v-if="resp.code === 0" icon="mdi-check" class="text-success text-lg" />
|
||||
<Icon v-else icon="mdi-multiply" class="text-error text-lg" />
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ format.toLocaleDate(resp.timestamp) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginationBar :limit="page.limit" :total="txs.pagination?.total" :callback="pageload" />
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'query'">
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg font-semibold">{{ $t('cosmwasm.suggested_messages') }}</div>
|
||||
</div>
|
||||
<div class="px-3">
|
||||
<div>
|
||||
<div>
|
||||
<span v-for="q in queries" class="btn btn-xs mx-1" @click="selectQuery(q)">{{ q }}</span>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="query"
|
||||
placeholder="Query String, {}"
|
||||
label="Query String"
|
||||
class="my-2 textarea textarea-bordered w-full"
|
||||
/>
|
||||
|
||||
<div class="mt-4 mb-4 text-center">
|
||||
<button class="btn btn-primary btn-sm px-4 text-white" @click="queryContract()">
|
||||
{{ $t('cosmwasm.btn_query') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="tab === 'execute'">
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg font-semibold">{{ $t('cosmwasm.suggested_messages') }}</div>
|
||||
</div>
|
||||
<div class="px-3">
|
||||
<div>
|
||||
<div>
|
||||
<span v-for="q in queries" class="btn btn-xs mx-1" @click="selectQuery(q)">{{ q }}</span>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="query"
|
||||
placeholder="Query String, {}"
|
||||
label="Query String"
|
||||
class="my-2 textarea textarea-bordered w-full"
|
||||
/>
|
||||
|
||||
<div class="mt-4 mb-4 text-center">
|
||||
<button class="btn btn-primary btn-sm px-4 text-white" @click="queryContract()">
|
||||
{{ $t('cosmwasm.btn_execute') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="tab === 'execute' || tab === 'query'" class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<div class="flex items-center justify-between px-3 pt-2 mb-4">
|
||||
<div class="text-lg font-semibold">{{ $t('cosmwasm.result') }}</div>
|
||||
</div>
|
||||
<JsonViewer
|
||||
:value="result"
|
||||
:theme="baseStore.theme"
|
||||
style="background: transparent"
|
||||
copyable
|
||||
boxed
|
||||
sort
|
||||
:expand-depth="5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -11,88 +11,93 @@ const props = defineProps(['chain']);
|
||||
|
||||
const codes = ref({} as PaginabledCodeInfos);
|
||||
|
||||
const pageRequest = ref(new PageRequest())
|
||||
const pageRequest = ref(new PageRequest());
|
||||
const wasmStore = useWasmStore();
|
||||
const dialog = useTxDialog()
|
||||
const creator = ref("")
|
||||
const field = ref("contract")
|
||||
const history = ref([])
|
||||
const dialog = useTxDialog();
|
||||
const creator = ref('');
|
||||
const field = ref('contract');
|
||||
const history = ref([]);
|
||||
|
||||
function pageload(pageNum: number) {
|
||||
pageRequest.value.setPage(pageNum)
|
||||
wasmStore.wasmClient.getWasmCodeList(pageRequest.value).then((x) => {
|
||||
codes.value = x;
|
||||
});
|
||||
pageRequest.value.setPage(pageNum);
|
||||
wasmStore.wasmClient.getWasmCodeList(pageRequest.value).then((x) => {
|
||||
codes.value = x;
|
||||
});
|
||||
}
|
||||
pageload(1)
|
||||
pageload(1);
|
||||
|
||||
onMounted(() => {
|
||||
const historyStore = JSON.parse(localStorage.getItem("contract_history") || "{}")
|
||||
history.value = historyStore[props.chain] || []
|
||||
})
|
||||
const historyStore = JSON.parse(localStorage.getItem('contract_history') || '{}');
|
||||
history.value = historyStore[props.chain] || [];
|
||||
});
|
||||
|
||||
function myContracts() {
|
||||
if(field.value === "contract")
|
||||
router.push(`/${props.chain}/cosmwasm/0/transactions?contract=${creator.value}`)
|
||||
else if(field.value === "creator")
|
||||
router.push(`/${props.chain}/cosmwasm/${creator.value}/contracts`)
|
||||
if (field.value === 'contract') router.push(`/${props.chain}/cosmwasm/0/transactions?contract=${creator.value}`);
|
||||
else if (field.value === 'creator') router.push(`/${props.chain}/cosmwasm/${creator.value}/contracts`);
|
||||
}
|
||||
const togo = ref("")
|
||||
const togo = ref('');
|
||||
function gotoHistory() {
|
||||
router.push(`/${props.chain}/cosmwasm/0/transactions?contract=${togo.value}`)
|
||||
router.push(`/${props.chain}/cosmwasm/0/transactions?contract=${togo.value}`);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full mb-4">{{ $t('cosmwasm.title') }}</h2>
|
||||
<div class="grid grid-flow-col auto-cols-max gap-4 overflow-hidden">
|
||||
<div class="join w-full border border-primary">
|
||||
<select v-model="field" class="select select-primary"><option value="contract">Contract</option><option value="creator">Creator</option></select>
|
||||
<input v-model="creator" type=text class="input input-bordered w-full join-item" placeholder="address" />
|
||||
<button class="join-item btn btn-primary" @click="myContracts()">{{ $t('cosmwasm.btn_query') }}</button>
|
||||
</div>
|
||||
<div>
|
||||
<select v-model="togo" class="select select-primary" @change="gotoHistory()">
|
||||
<option value="">History</option>
|
||||
<option v-for="(v, index) in history" :key="index" :value="v" >...{{ String(v).substring(45) }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full mt-4 text-sm">
|
||||
<thead class=" bg-base-200">
|
||||
<tr>
|
||||
<th>{{ $t('cosmwasm.code_id') }}</th>
|
||||
<th>{{ $t('cosmwasm.code_hash') }}</th>
|
||||
<th>{{ $t('cosmwasm.creator') }}</th>
|
||||
<th>{{ $t('cosmwasm.permissions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(v, index) in codes.code_infos" :key="index">
|
||||
<td>{{ v.code_id }}</td>
|
||||
<td>
|
||||
<RouterLink :to="`/${props.chain}/cosmwasm/${v.code_id}/contracts`"
|
||||
class="truncate max-w-[200px] block text-primary dark:invert" :title="v.data_hash">
|
||||
{{ v.data_hash }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
<td>{{ v.creator }}</td>
|
||||
<td>
|
||||
{{ v.instantiate_permission?.permission }}
|
||||
<span>{{ v.instantiate_permission?.address }}
|
||||
{{ v.instantiate_permission?.addresses.join(', ') }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="flex justify-between">
|
||||
<PaginationBar :limit="pageRequest.limit" :total="codes.pagination?.total" :callback="pageload" />
|
||||
<label for="wasm_store_code" class="btn btn-primary my-5" @click="dialog.open('wasm_store_code', {})">{{ $t('cosmwasm.btn_up_sc') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title truncate w-full mb-4">{{ $t('cosmwasm.title') }}</h2>
|
||||
<div class="grid grid-flow-col auto-cols-max gap-4 overflow-hidden">
|
||||
<div class="join w-full border border-primary">
|
||||
<select v-model="field" class="select select-primary">
|
||||
<option value="contract">Contract</option>
|
||||
<option value="creator">Creator</option>
|
||||
</select>
|
||||
<input v-model="creator" type="text" class="input input-bordered w-full join-item" placeholder="address" />
|
||||
<button class="join-item btn btn-primary" @click="myContracts()">{{ $t('cosmwasm.btn_query') }}</button>
|
||||
</div>
|
||||
<div>
|
||||
<select v-model="togo" class="select select-primary" @change="gotoHistory()">
|
||||
<option value="">History</option>
|
||||
<option v-for="(v, index) in history" :key="index" :value="v">...{{ String(v).substring(45) }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full mt-4 text-sm">
|
||||
<thead class="bg-base-200">
|
||||
<tr>
|
||||
<th>{{ $t('cosmwasm.code_id') }}</th>
|
||||
<th>{{ $t('cosmwasm.code_hash') }}</th>
|
||||
<th>{{ $t('cosmwasm.creator') }}</th>
|
||||
<th>{{ $t('cosmwasm.permissions') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(v, index) in codes.code_infos" :key="index">
|
||||
<td>{{ v.code_id }}</td>
|
||||
<td>
|
||||
<RouterLink
|
||||
:to="`/${props.chain}/cosmwasm/${v.code_id}/contracts`"
|
||||
class="truncate max-w-[200px] block text-primary dark:invert"
|
||||
:title="v.data_hash"
|
||||
>
|
||||
{{ v.data_hash }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
<td>{{ v.creator }}</td>
|
||||
<td>
|
||||
{{ v.instantiate_permission?.permission }}
|
||||
<span>{{ v.instantiate_permission?.address }} {{ v.instantiate_permission?.addresses.join(', ') }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="flex justify-between">
|
||||
<PaginationBar :limit="pageRequest.limit" :total="codes.pagination?.total" :callback="pageload" />
|
||||
<label for="wasm_store_code" class="btn btn-primary my-5" @click="dialog.open('wasm_store_code', {})">{{
|
||||
$t('cosmwasm.btn_up_sc')
|
||||
}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<route>
|
||||
|
||||
@ -7,163 +7,180 @@ import { ref, onMounted, computed } from 'vue';
|
||||
const chainStore = useBlockchain();
|
||||
const format = useFormatter();
|
||||
interface FaucetResponse {
|
||||
status: string;
|
||||
result: any;
|
||||
message: string;
|
||||
status: string;
|
||||
result: any;
|
||||
message: string;
|
||||
}
|
||||
|
||||
const address = ref('');
|
||||
const faucet = ref('');
|
||||
const balances = ref([]);
|
||||
const faucetModal = ref(false);
|
||||
const ret = ref({} as FaucetResponse);
|
||||
const ret = ref({} as FaucetResponse);
|
||||
const configChecker = ref('');
|
||||
|
||||
const checklist = computed(() => {
|
||||
const endpoint = chainStore.current?.endpoints?.rest
|
||||
const bs = balances.value.length > 0 && balances.value.findIndex((v:any) => v.amount <= 10) === -1;
|
||||
return [
|
||||
{ title: 'Rest Endpoint', status: endpoint && endpoint[0].address !== '' },
|
||||
{ title: 'Faucet Configured', status: chainStore.current?.faucet !== undefined },
|
||||
{ title: 'Faucet Account', status: faucet.value !== ''},
|
||||
{ title: 'Faucet Balance', status: bs},
|
||||
];
|
||||
const endpoint = chainStore.current?.endpoints?.rest;
|
||||
const bs = balances.value.length > 0 && balances.value.findIndex((v: any) => v.amount <= 10) === -1;
|
||||
return [
|
||||
{ title: 'Rest Endpoint', status: endpoint && endpoint[0].address !== '' },
|
||||
{ title: 'Faucet Configured', status: chainStore.current?.faucet !== undefined },
|
||||
{ title: 'Faucet Account', status: faucet.value !== '' },
|
||||
{ title: 'Faucet Balance', status: bs },
|
||||
];
|
||||
});
|
||||
|
||||
const notReady = computed(() => {
|
||||
for (const it of checklist.value) {
|
||||
if (!it.status) return true;
|
||||
}
|
||||
return false;
|
||||
for (const it of checklist.value) {
|
||||
if (!it.status) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const validAddress = computed(() => {
|
||||
if (!address.value) return true;
|
||||
return address.value.startsWith(chainStore.current?.bech32Prefix || '1');
|
||||
if (!address.value) return true;
|
||||
return address.value.startsWith(chainStore.current?.bech32Prefix || '1');
|
||||
});
|
||||
|
||||
const faucetUrl = computed(() => {
|
||||
return `https://faucet.ping.pub/${chainStore.current?.chainName}`;
|
||||
// return `http://localhost:3000/${chainStore.current?.chainName}`;
|
||||
return `https://faucet.ping.pub/${chainStore.current?.chainName}`;
|
||||
// return `http://localhost:3000/${chainStore.current?.chainName}`;
|
||||
});
|
||||
|
||||
|
||||
function claim() {
|
||||
|
||||
ret.value = {} as FaucetResponse;
|
||||
const prefix = chainStore.current?.bech32Prefix || 'cosmos';
|
||||
if (!address.value ) return;
|
||||
faucetModal.value = true;
|
||||
// @ts-ignore
|
||||
get(`${faucetUrl.value}/send/${address.value}`).then( (res: FaucetResponse) => {
|
||||
console.log(res);
|
||||
ret.value = res;
|
||||
});
|
||||
ret.value = {} as FaucetResponse;
|
||||
const prefix = chainStore.current?.bech32Prefix || 'cosmos';
|
||||
if (!address.value) return;
|
||||
faucetModal.value = true;
|
||||
// @ts-ignore
|
||||
get(`${faucetUrl.value}/send/${address.value}`).then((res: FaucetResponse) => {
|
||||
console.log(res);
|
||||
ret.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
function balance() {
|
||||
get(`${faucetUrl.value}/balance`).then(res => {
|
||||
if(res.status === 'error') {
|
||||
configChecker.value = res.message;
|
||||
return;
|
||||
}
|
||||
balances.value = res.result?.balance;
|
||||
faucet.value = res.result?.address;
|
||||
});
|
||||
get(`${faucetUrl.value}/balance`).then((res) => {
|
||||
if (res.status === 'error') {
|
||||
configChecker.value = res.message;
|
||||
return;
|
||||
}
|
||||
balances.value = res.result?.balance;
|
||||
faucet.value = res.result?.address;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (chainStore.current && chainStore.current.faucet) {
|
||||
balance();
|
||||
}
|
||||
if (chainStore.current && chainStore.current.faucet) {
|
||||
balance();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex flex-col items-center justify-center mb-6 mt-14 gap-4">
|
||||
<img v-if="chainStore.current?.logo" :src="`${chainStore.current?.logo}`" class="w-16 rounded-md" />
|
||||
<div v-else class="w-16 rounded-full">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150.000000 132.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0.000000,132.000000) scale(0.100000,-0.100000)" fill="#666CFF" class=""
|
||||
stroke="none">
|
||||
<path d="M500 1310 l-125 -5 -182 -315 c-100 -173 -182 -321 -182 -329 -1 -7
|
||||
<div>
|
||||
<div class="flex flex-col items-center justify-center mb-6 mt-14 gap-4">
|
||||
<img v-if="chainStore.current?.logo" :src="`${chainStore.current?.logo}`" class="w-16 rounded-md" />
|
||||
<div v-else class="w-16 rounded-full">
|
||||
<svg
|
||||
version="1.0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 150.000000 132.000000"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
>
|
||||
<g transform="translate(0.000000,132.000000) scale(0.100000,-0.100000)" fill="#666CFF" class="" stroke="none">
|
||||
<path
|
||||
d="M500 1310 l-125 -5 -182 -315 c-100 -173 -182 -321 -182 -329 -1 -7
|
||||
81 -159 181 -337 l183 -324 372 0 371 0 186 325 c102 179 186 330 186 337 0 7
|
||||
-82 157 -182 335 l-183 323 -250 -2 c-137 -1 -306 -5 -375 -8z m588 -454 c61
|
||||
-106 112 -197 112 -201 0 -4 -50 -95 -111 -201 l-112 -194 -231 0 -231 0 -105
|
||||
181 c-58 100 -109 190 -114 200 -6 14 17 63 104 213 l112 196 232 0 231 0 113
|
||||
-194z" />
|
||||
<path d="M591 1001 l-54 -6 -87 -150 -88 -150 176 -3 c97 -1 181 -1 187 2 9 3
|
||||
165 267 183 308 4 9 -233 7 -317 -1z" />
|
||||
<path d="M872 824 l-90 -159 36 -66 c113 -201 147 -258 153 -251 8 8 179 302
|
||||
179 307 0 2 -37 68 -83 147 -46 78 -88 151 -94 162 -9 16 -24 -5 -101 -140z" />
|
||||
<path d="M360 625 c0 -7 148 -263 172 -297 l19 -28 186 0 c101 0 183 3 181 8
|
||||
-1 4 -43 78 -93 165 l-90 157 -187 0 c-104 0 -188 -2 -188 -5z" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="text-primary text-3xl md:!text-6xl font-bold capitalize">
|
||||
{{ chainStore.chainName }} Faucet
|
||||
</h1>
|
||||
</div>
|
||||
<div class="bg-base-100 my-5 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title">Get Tokens</h2>
|
||||
<input type="text" v-model="address" class="mt-4 mb-4 w-full border border-gray-300 rounded-md p-2"
|
||||
:class="{'input-error' : !validAddress}"
|
||||
:disabled="notReady" placeholder="Enter your address" />
|
||||
<button class="btn btn-primary w-full bg-primary text-white" :disabled="notReady" @click="claim()">Get
|
||||
Tokens</button>
|
||||
</div>
|
||||
|
||||
<AdBanner id="home-banner-ad" unit="banner" />
|
||||
|
||||
<div class="bg-base-100 my-5 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title">Enable Faucet</h2>
|
||||
<div class="mt-4">
|
||||
<span class="text-base"> 1. Submit chain configuration</span>
|
||||
<div class="mockup-code bg-base-200 my-2 gap-4">
|
||||
<div v-for="it in checklist">
|
||||
<pre><code class="text-gray-800 dark:invert">{{ it.title }}: </code>{{ it.status ? '✅' : '❌' }} </pre>
|
||||
</div>
|
||||
|
||||
<pre class=" text-xs text-red-500">{{ configChecker }}</pre>
|
||||
<pre></pre>
|
||||
<a class=" btn-ghost text-white rounded-md p-2 ml-4"
|
||||
href="https://github.com/ping-pub/ping.pub/blob/main/faucet.md">Update</a>
|
||||
</div>
|
||||
|
||||
<span class="text-base"> 2. Fund the faucet account</span>
|
||||
<div class="mockup-code bg-base-200 my-2">
|
||||
<pre data-prefix=">"><code class=" text-gray-800 dark:invert"> Faucet Address: {{ faucet }} </code></pre>
|
||||
<pre
|
||||
data-prefix=">"><code class="text-gray-800 dark:invert"> Balances: {{ format.formatTokens(balances) }} </code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="checkbox" v-model="faucetModal" id="my_modal_6" class="modal-toggle" />
|
||||
<div class="modal" role="dialog">
|
||||
<div class="modal-box">
|
||||
<div v-if="ret.status === 'error'">
|
||||
<h3 class="font-bold text-red-500"> Error </h3>
|
||||
<div>{{ ret.message }}</div>
|
||||
</div>
|
||||
<div v-else-if="ret.status === 'ok'">
|
||||
<h3 class="font-bold text-green-500"> Token Sent! </h3>
|
||||
<div class=" text-center mt-4"><RouterLink :to="`/${chainStore.chainName}/tx/${ret.result.txhash}`">View Transaction</RouterLink></div>
|
||||
</div>
|
||||
<h3 v-else class="font-bold text-lg"> Processing <span class="loading loading-bars loading-sm"></span> </h3>
|
||||
|
||||
<div class="modal-action">
|
||||
<label for="my_modal_6" class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</label>
|
||||
</div>
|
||||
<p class="py-2">
|
||||
<div>
|
||||
<AdBanner id="popup-ad" unit="popup" />
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
-194z"
|
||||
/>
|
||||
<path
|
||||
d="M591 1001 l-54 -6 -87 -150 -88 -150 176 -3 c97 -1 181 -1 187 2 9 3
|
||||
165 267 183 308 4 9 -233 7 -317 -1z"
|
||||
/>
|
||||
<path
|
||||
d="M872 824 l-90 -159 36 -66 c113 -201 147 -258 153 -251 8 8 179 302
|
||||
179 307 0 2 -37 68 -83 147 -46 78 -88 151 -94 162 -9 16 -24 -5 -101 -140z"
|
||||
/>
|
||||
<path
|
||||
d="M360 625 c0 -7 148 -263 172 -297 l19 -28 186 0 c101 0 183 3 181 8
|
||||
-1 4 -43 78 -93 165 l-90 157 -187 0 c-104 0 -188 -2 -188 -5z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<h1 class="text-primary text-3xl md:!text-6xl font-bold capitalize">{{ chainStore.chainName }} Faucet</h1>
|
||||
</div>
|
||||
<div class="bg-base-100 my-5 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title">Get Tokens</h2>
|
||||
<input
|
||||
type="text"
|
||||
v-model="address"
|
||||
class="mt-4 mb-4 w-full border border-gray-300 rounded-md p-2"
|
||||
:class="{ 'input-error': !validAddress }"
|
||||
:disabled="notReady"
|
||||
placeholder="Enter your address"
|
||||
/>
|
||||
<button class="btn btn-primary w-full bg-primary text-white" :disabled="notReady" @click="claim()">
|
||||
Get Tokens
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<AdBanner id="home-banner-ad" unit="banner" />
|
||||
|
||||
<div class="bg-base-100 my-5 px-4 pt-3 pb-4 rounded shadow">
|
||||
<h2 class="card-title">Enable Faucet</h2>
|
||||
<div class="mt-4">
|
||||
<span class="text-base"> 1. Submit chain configuration</span>
|
||||
<div class="mockup-code bg-base-200 my-2 gap-4">
|
||||
<div v-for="it in checklist">
|
||||
<pre><code class="text-gray-800 dark:invert">{{ it.title }}: </code>{{ it.status ? '✅' : '❌' }} </pre>
|
||||
</div>
|
||||
|
||||
<pre class="text-xs text-red-500">{{ configChecker }}</pre>
|
||||
<pre></pre>
|
||||
<a
|
||||
class="btn-ghost text-white rounded-md p-2 ml-4"
|
||||
href="https://github.com/ping-pub/ping.pub/blob/main/faucet.md"
|
||||
>Update</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<span class="text-base"> 2. Fund the faucet account</span>
|
||||
<div class="mockup-code bg-base-200 my-2">
|
||||
<pre data-prefix=">"><code class=" text-gray-800 dark:invert"> Faucet Address: {{ faucet }} </code></pre>
|
||||
<pre
|
||||
data-prefix=">"
|
||||
><code class="text-gray-800 dark:invert"> Balances: {{ format.formatTokens(balances) }} </code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="checkbox" v-model="faucetModal" id="my_modal_6" class="modal-toggle" />
|
||||
<div class="modal" role="dialog">
|
||||
<div class="modal-box">
|
||||
<div v-if="ret.status === 'error'">
|
||||
<h3 class="font-bold text-red-500">Error</h3>
|
||||
<div>{{ ret.message }}</div>
|
||||
</div>
|
||||
<div v-else-if="ret.status === 'ok'">
|
||||
<h3 class="font-bold text-green-500">Token Sent!</h3>
|
||||
<div class="text-center mt-4">
|
||||
<RouterLink :to="`/${chainStore.chainName}/tx/${ret.result.txhash}`">View Transaction</RouterLink>
|
||||
</div>
|
||||
</div>
|
||||
<h3 v-else class="font-bold text-lg">Processing <span class="loading loading-bars loading-sm"></span></h3>
|
||||
|
||||
<div class="modal-action">
|
||||
<label for="my_modal_6" class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</label>
|
||||
</div>
|
||||
<div class="py-2">
|
||||
<div>
|
||||
<AdBanner id="popup-ad" unit="popup" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -2,27 +2,13 @@
|
||||
import { computed } from '@vue/reactivity';
|
||||
import MdEditor from 'md-editor-v3';
|
||||
import ObjectElement from '@/components/dynamic/ObjectElement.vue';
|
||||
import {
|
||||
useBaseStore,
|
||||
useBlockchain,
|
||||
useFormatter,
|
||||
useGovStore,
|
||||
useStakingStore,
|
||||
useTxDialog,
|
||||
} from '@/stores';
|
||||
import {
|
||||
PageRequest,
|
||||
type GovProposal,
|
||||
type GovVote,
|
||||
type PaginatedProposalDeposit,
|
||||
type Pagination,
|
||||
} from '@/types';
|
||||
import { useBaseStore, useBlockchain, useFormatter, useGovStore, useStakingStore, useTxDialog } from '@/stores';
|
||||
import { PageRequest, type GovProposal, type GovVote, type PaginatedProposalDeposit, type Pagination } from '@/types';
|
||||
import { ref, reactive } from 'vue';
|
||||
import Countdown from '@/components/Countdown.vue';
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
import { fromBech32, toHex } from '@cosmjs/encoding';
|
||||
|
||||
|
||||
const props = defineProps(['proposal_id', 'chain']);
|
||||
const proposal = ref({} as GovProposal);
|
||||
const format = useFormatter();
|
||||
@ -32,7 +18,7 @@ const stakingStore = useStakingStore();
|
||||
const chainStore = useBlockchain();
|
||||
|
||||
store.fetchProposal(props.proposal_id).then((res) => {
|
||||
const proposalDetail = reactive(res.proposal);
|
||||
let proposalDetail = reactive(res.proposal);
|
||||
// when status under the voting, final_tally_result are no data, should request fetchTally
|
||||
if (res.proposal?.status === 'PROPOSAL_STATUS_VOTING_PERIOD') {
|
||||
store.fetchTally(props.proposal_id).then((tallRes) => {
|
||||
@ -41,35 +27,35 @@ store.fetchProposal(props.proposal_id).then((res) => {
|
||||
}
|
||||
proposal.value = proposalDetail;
|
||||
// load origin params if the proposal is param change
|
||||
if(proposalDetail.content?.changes) {
|
||||
proposalDetail.content?.changes.forEach((item) => {
|
||||
chainStore.rpc.getParams(item.subspace, item.key).then((res) => {
|
||||
if(proposal.value.content && res.param) {
|
||||
if(proposal.value.content.current){
|
||||
proposal.value.content.current.push(res.param);
|
||||
} else {
|
||||
proposal.value.content.current = [res.param];
|
||||
};
|
||||
if (proposalDetail.content?.changes) {
|
||||
proposalDetail.content?.changes.forEach((item) => {
|
||||
chainStore.rpc.getParams(item.subspace, item.key).then((res) => {
|
||||
if (proposal.value.content && res.param) {
|
||||
if (proposal.value.content.current) {
|
||||
proposal.value.content.current.push(res.param);
|
||||
} else {
|
||||
proposal.value.content.current = [res.param];
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const msgType = proposalDetail.content['@type'] || '';
|
||||
if(msgType.endsWith('MsgUpdateParams')) {
|
||||
if(msgType.indexOf('staking') > -1) {
|
||||
const msgType = proposalDetail.content?.['@type'] || '';
|
||||
if (msgType.endsWith('MsgUpdateParams')) {
|
||||
if (msgType.indexOf('staking') > -1) {
|
||||
chainStore.rpc.getStakingParams().then((res) => {
|
||||
addCurrentParams(res);
|
||||
});
|
||||
} else if(msgType.indexOf('gov') > -1) {
|
||||
} else if (msgType.indexOf('gov') > -1) {
|
||||
chainStore.rpc.getGovParamsVoting().then((res) => {
|
||||
addCurrentParams(res);
|
||||
});
|
||||
} else if(msgType.indexOf('distribution') > -1) {
|
||||
} else if (msgType.indexOf('distribution') > -1) {
|
||||
chainStore.rpc.getDistributionParams().then((res) => {
|
||||
addCurrentParams(res);
|
||||
});
|
||||
} else if(msgType.indexOf('slashing') > -1) {
|
||||
} else if (msgType.indexOf('slashing') > -1) {
|
||||
chainStore.rpc.getSlashingParams().then((res) => {
|
||||
addCurrentParams(res);
|
||||
});
|
||||
@ -78,7 +64,7 @@ store.fetchProposal(props.proposal_id).then((res) => {
|
||||
});
|
||||
|
||||
function addCurrentParams(res: any) {
|
||||
if(proposal.value.content && res.params) {
|
||||
if (proposal.value.content && res.params) {
|
||||
proposal.value.content.params = [proposal.value.content?.params];
|
||||
proposal.value.content.current = [res.params];
|
||||
}
|
||||
@ -128,7 +114,9 @@ const upgradeCountdown = computed((): number => {
|
||||
if (height > 0) {
|
||||
const base = useBaseStore();
|
||||
const current = Number(base.latest?.block?.header?.height || 0);
|
||||
return (height - current) * Number((base.blocktime / 1000).toFixed()) * 1000;
|
||||
return (
|
||||
(height - current) * Number((base.blocktime / 1000).toFixed()) * 1000
|
||||
);
|
||||
}
|
||||
const now = new Date();
|
||||
const end = new Date(proposal.value.content?.plan?.time || '');
|
||||
@ -197,12 +185,14 @@ const processList = computed(() => {
|
||||
});
|
||||
|
||||
function showValidatorName(voter: string) {
|
||||
const { data } = fromBech32(voter);
|
||||
const hex = toHex(data);
|
||||
const v = stakingStore.validators.find(
|
||||
(x) => toHex(fromBech32(x.operator_address).data) === hex
|
||||
);
|
||||
return v ? v.description.moniker : voter;
|
||||
try {
|
||||
const { data } = fromBech32(voter);
|
||||
const hex = toHex(data);
|
||||
const v = stakingStore.validators.find((x) => toHex(fromBech32(x.operator_address).data) === hex);
|
||||
return v ? v.description.moniker : voter;
|
||||
} catch (e) {
|
||||
return voter;
|
||||
}
|
||||
}
|
||||
|
||||
function pageload(p: number) {
|
||||
@ -213,27 +203,28 @@ function pageload(p: number) {
|
||||
});
|
||||
}
|
||||
|
||||
function metaItem(metadata: string|undefined): { title: string; summary: string } {
|
||||
return metadata ? JSON.parse(metadata) : {}
|
||||
function metaItem(metadata: string | undefined): { title: string; summary: string } {
|
||||
if (!metadata) {
|
||||
return { title: '', summary: '' };
|
||||
} else if (metadata.startsWith('{') && metadata.endsWith('}')) {
|
||||
return JSON.parse(metadata);
|
||||
}
|
||||
return { title: metadata, summary: '' };
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title flex flex-col md:!justify-between md:!flex-row mb-2">
|
||||
<h2
|
||||
class="card-title flex flex-col md:!justify-between md:!flex-row mb-2"
|
||||
>
|
||||
<p class="truncate w-full">
|
||||
{{ proposal_id }}. {{ proposal.title || proposal.content?.title || metaItem(proposal?.metadata)?.title }}
|
||||
{{ proposal_id }}. {{ proposal.title || proposal.content?.title || metaItem(proposal?.metadata)?.title }}
|
||||
</p>
|
||||
<div
|
||||
class="badge badge-ghost"
|
||||
:class="
|
||||
color === 'success'
|
||||
? 'text-yes'
|
||||
: color === 'error'
|
||||
? 'text-no'
|
||||
: 'text-info'
|
||||
"
|
||||
:class="color === 'success' ? 'text-yes' : color === 'error' ? 'text-no' : 'text-info'"
|
||||
>
|
||||
{{ status }}
|
||||
</div>
|
||||
@ -241,9 +232,13 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<div class="">
|
||||
<ObjectElement :value="proposal.content" />
|
||||
</div>
|
||||
<div v-if="proposal.summary && !proposal.content?.description || metaItem(proposal?.metadata)?.summary ">
|
||||
<div v-if="(proposal.summary && !proposal.content?.description) || metaItem(proposal?.metadata)?.summary">
|
||||
<MdEditor
|
||||
:model-value="format.multiLine(proposal.summary || metaItem(proposal?.metadata)?.summary)"
|
||||
:model-value="
|
||||
format.multiLine(
|
||||
proposal.summary || metaItem(proposal?.metadata)?.summary
|
||||
)
|
||||
"
|
||||
previewOnly
|
||||
class="md-editor-recover"
|
||||
></MdEditor>
|
||||
@ -258,16 +253,11 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<div class="mb-1" v-for="(item, index) of processList" :key="index">
|
||||
<label class="block text-sm mb-1">{{ item.name }}</label>
|
||||
<div class="h-5 w-full relative">
|
||||
<div
|
||||
class="absolute inset-x-0 inset-y-0 w-full opacity-10 rounded-sm"
|
||||
:class="`${item.class}`"
|
||||
></div>
|
||||
<div class="absolute inset-x-0 inset-y-0 w-full opacity-10 rounded-sm" :class="`${item.class}`"></div>
|
||||
<div
|
||||
class="absolute inset-x-0 inset-y-0 rounded-sm"
|
||||
:class="`${item.class}`"
|
||||
:style="`width: ${
|
||||
item.value === '-' || item.value === 'NaN%' ? '0%' : item.value
|
||||
}`"
|
||||
:style="`width: ${item.value === '-' || item.value === 'NaN%' ? '0%' : item.value}`"
|
||||
></div>
|
||||
<p
|
||||
class="absolute inset-x-0 inset-y-0 text-center text-sm text-[#666] dark:text-[#eee] flex items-center justify-center"
|
||||
@ -299,7 +289,8 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<div class="flex items-center mb-4 mt-2">
|
||||
<div class="w-2 h-2 rounded-full bg-error mr-3"></div>
|
||||
<div class="text-base flex-1 text-main">
|
||||
{{ $t('gov.submit_at') }}: {{ format.toDay(proposal.submit_time) }}
|
||||
{{ $t('gov.submit_at') }}:
|
||||
{{ format.toDay(proposal.submit_time) }}
|
||||
</div>
|
||||
<div class="text-sm">{{ shortTime(proposal.submit_time) }}</div>
|
||||
</div>
|
||||
@ -329,7 +320,8 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<div class="flex items-center">
|
||||
<div class="w-2 h-2 rounded-full bg-yes mr-3"></div>
|
||||
<div class="text-base flex-1 text-main">
|
||||
{{ $t('gov.vote_start_from') }} {{ format.toDay(proposal.voting_start_time) }}
|
||||
{{ $t('gov.vote_start_from') }}
|
||||
{{ format.toDay(proposal.voting_start_time) }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{{ shortTime(proposal.voting_start_time) }}
|
||||
@ -343,33 +335,26 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<div class="flex items-center mb-1">
|
||||
<div class="w-2 h-2 rounded-full bg-success mr-3"></div>
|
||||
<div class="text-base flex-1 text-main">
|
||||
{{ $t('gov.vote_end') }} {{ format.toDay(proposal.voting_end_time) }}
|
||||
{{ $t('gov.vote_end') }}
|
||||
{{ format.toDay(proposal.voting_end_time) }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{{ shortTime(proposal.voting_end_time) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="pl-5 text-sm">
|
||||
{{ $t('gov.current_status') }}: {{ $t(`gov.proposal_statuses.${proposal.status}`) }}
|
||||
{{ $t('gov.current_status') }}:
|
||||
{{ $t(`gov.proposal_statuses.${proposal.status}`) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="mt-4"
|
||||
v-if="
|
||||
proposal?.content?.['@type']?.endsWith('SoftwareUpgradeProposal')
|
||||
"
|
||||
>
|
||||
<div class="mt-4" v-if="proposal?.content?.['@type']?.endsWith('SoftwareUpgradeProposal')">
|
||||
<div class="flex items-center">
|
||||
<div class="w-2 h-2 rounded-full bg-warning mr-3"></div>
|
||||
<div class="text-base flex-1 text-main">
|
||||
{{ $t('gov.upgrade_plan') }}:
|
||||
<span v-if="Number(proposal.content?.plan?.height || '0') > 0">
|
||||
(EST)</span
|
||||
>
|
||||
<span v-else>{{
|
||||
format.toDay(proposal.content?.plan?.time)
|
||||
}}</span>
|
||||
<span v-if="Number(proposal.content?.plan?.height || '0') > 0"> (EST)</span>
|
||||
<span v-else>{{ format.toDay(proposal.content?.plan?.time) }}</span>
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{{ shortTime(proposal.voting_end_time) }}
|
||||
@ -391,7 +376,7 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
<tr v-for="(item, index) of votes" :key="index">
|
||||
<td class="py-2 text-sm">{{ showValidatorName(item.voter) }}</td>
|
||||
<td
|
||||
v-if="item.option"
|
||||
v-if="item.option && item.option !== 'VOTE_OPTION_UNSPECIFIED'"
|
||||
class="py-2 text-sm"
|
||||
:class="{
|
||||
'text-yes': item.option === 'VOTE_OPTION_YES',
|
||||
@ -400,20 +385,17 @@ function metaItem(metadata: string|undefined): { title: string; summary: string
|
||||
>
|
||||
{{ String(item.option).replace('VOTE_OPTION_', '') }}
|
||||
</td>
|
||||
<td
|
||||
v-if="item.options"
|
||||
class="py-2 text-sm"
|
||||
>
|
||||
{{ item.options.map(x => `${x.option.replace('VOTE_OPTION_', '')}:${format.percent(x.weight)}`).join(', ') }}
|
||||
<td v-if="item.options" class="py-2 text-sm">
|
||||
{{
|
||||
item.options
|
||||
.map((x) => `${x.option.replace('VOTE_OPTION_', '')}:${format.percent(x.weight)}`)
|
||||
.join(', ')
|
||||
}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginationBar
|
||||
:limit="pageRequest.limit"
|
||||
:total="pageResponse.total"
|
||||
:callback="pageload"
|
||||
/>
|
||||
<PaginationBar :limit="pageRequest.limit" :total="pageResponse.total" :callback="pageload" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -7,40 +7,44 @@ import { PageRequest } from '@/types';
|
||||
|
||||
const tab = ref('2');
|
||||
const store = useGovStore();
|
||||
const pageRequest = ref(new PageRequest())
|
||||
const pageRequest = ref(new PageRequest());
|
||||
|
||||
onMounted(() => {
|
||||
store.fetchProposals('2').then((x) => {
|
||||
if (x?.proposals?.length === 0) {
|
||||
tab.value = '3';
|
||||
store.fetchProposals('3');
|
||||
}
|
||||
store.fetchProposals('3');
|
||||
store.fetchProposals('4');
|
||||
});
|
||||
store.fetchProposals('2').then((x) => {
|
||||
if (x?.proposals?.length === 0) {
|
||||
tab.value = '3';
|
||||
store.fetchProposals('3');
|
||||
}
|
||||
store.fetchProposals('3');
|
||||
store.fetchProposals('4');
|
||||
});
|
||||
});
|
||||
|
||||
const changeTab = (val: '2' | '3' | '4') => {
|
||||
tab.value = val;
|
||||
tab.value = val;
|
||||
};
|
||||
|
||||
function page(p: number) {
|
||||
pageRequest.value.setPage(p)
|
||||
store.fetchProposals(tab.value, pageRequest.value)
|
||||
pageRequest.value.setPage(p);
|
||||
store.fetchProposals(tab.value, pageRequest.value);
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4 text-center">
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === '2' }" @click="changeTab('2')">{{ $t('gov.voting') }}</a>
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === '3' }" @click="changeTab('3')">{{ $t('gov.passed') }}</a>
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === '4' }"
|
||||
@click="changeTab('4')">{{ $t('gov.rejected') }}</a>
|
||||
</div>
|
||||
<ProposalListItem :proposals="store?.proposals[tab]" />
|
||||
<PaginationBar :total="store?.proposals[tab]?.pagination?.total" :limit="pageRequest.limit" :callback="page" />
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4 text-center">
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === '2' }" @click="changeTab('2')">{{
|
||||
$t('gov.voting')
|
||||
}}</a>
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === '3' }" @click="changeTab('3')">{{
|
||||
$t('gov.passed')
|
||||
}}</a>
|
||||
<a class="tab text-gray-400 uppercase" :class="{ 'tab-active': tab === '4' }" @click="changeTab('4')">{{
|
||||
$t('gov.rejected')
|
||||
}}</a>
|
||||
</div>
|
||||
<ProposalListItem :proposals="store?.proposals[tab]" />
|
||||
<PaginationBar :total="store?.proposals[tab]?.pagination?.total" :limit="pageRequest.limit" :callback="page" />
|
||||
</div>
|
||||
</template>
|
||||
<route>
|
||||
{
|
||||
|
||||
12
src/modules/[chain]/group/index.vue
Normal file
12
src/modules/[chain]/group/index.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { fromBech32, fromHex, toBech32 } from '@cosmjs/encoding';
|
||||
|
||||
let x = toBech32('neutronvaloper1', fromHex('3363E8F97B02ECC00289E72173D827543047ACDA'));
|
||||
let add = fromBech32('cosmosvaloper1jxv0u20scum4trha72c7ltfgfqef6nsch7q6cu');
|
||||
let op = toBech32('neutronvaloper1', add.data);
|
||||
console.log(x);
|
||||
console.log(op);
|
||||
</script>
|
||||
<template>
|
||||
<div>address:</div>
|
||||
</template>
|
||||
@ -1,63 +1,105 @@
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import {useBlockchain} from '@/stores'
|
||||
import ChainRegistryClient from '@ping-pub/chain-registry-client';
|
||||
import type { IBCPath, IBCInfo, } from '@ping-pub/chain-registry-client/dist/types';
|
||||
import type { Channel } from '@/types';
|
||||
import { useBlockchain } from '@/stores';
|
||||
import { ChainRegistryClient } from '@chain-registry/client';
|
||||
import type { IBCData } from '@chain-registry/types/ibc_data.schema';
|
||||
import router from '@/router';
|
||||
import fetch from 'cross-fetch';
|
||||
|
||||
const IBC_USE_GITHUB_API = import.meta.env.VITE_IBC_USE_GITHUB_API === 'true';
|
||||
const PINGPUB_API_URL = import.meta.env.VITE_PINGPUB_API_URL || 'https://registry.ping.pub';
|
||||
const GITHUB_API_URL =
|
||||
import.meta.env.VITE_GITHUB_API_URL || 'https://api.github.com/repos/cosmos/chain-registry/contents';
|
||||
const IBC_API_URL = IBC_USE_GITHUB_API ? GITHUB_API_URL : PINGPUB_API_URL;
|
||||
|
||||
export const useIBCModule = defineStore('module-ibc', {
|
||||
state: () => {
|
||||
return {
|
||||
paths: [] as IBCPath[],
|
||||
connectionId: "" as string,
|
||||
registryConf: {} as IBCInfo,
|
||||
info: [] as IBCData[],
|
||||
connectionId: '' as string,
|
||||
registryConf: {} as IBCData,
|
||||
};
|
||||
},
|
||||
getters: {
|
||||
chain() {
|
||||
return useBlockchain()
|
||||
return useBlockchain();
|
||||
},
|
||||
commonIBCs(): any {
|
||||
return this.paths.filter((x: IBCPath) => x.path.search(this.chain.current?.prettyName || this.chain.chainName) > -1)
|
||||
chainName(): string {
|
||||
return this.chain.chainName;
|
||||
},
|
||||
isFirstChain(): boolean {
|
||||
return (
|
||||
this.registryConf.chain_1.chain_name === this.chain.current?.prettyName ||
|
||||
this.registryConf.chain_1.chain_name === this.chain.chainName
|
||||
);
|
||||
},
|
||||
sourceField(): string {
|
||||
return this.registryConf?.chain_1?.chain_name === this.chain.current?.prettyName || this.chain.chainName ? 'chain_1': 'chain_2'
|
||||
return this.isFirstChain ? 'chain_1' : 'chain_2';
|
||||
},
|
||||
destField() : string {
|
||||
return this.registryConf?.chain_1?.chain_name === this.chain.current?.prettyName || this.chain.chainName ? 'chain_2': 'chain_1'
|
||||
destField(): string {
|
||||
return this.isFirstChain ? 'chain_2' : 'chain_1';
|
||||
},
|
||||
registryChannels(): any {
|
||||
return this.registryConf.channels
|
||||
}
|
||||
return this.registryConf.channels;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
load() {
|
||||
const client = new ChainRegistryClient();
|
||||
client.fetchIBCPaths().then(res => {
|
||||
this.paths = res
|
||||
const prefix = this.chain.current?.networkType?.includes('testnet') ? 'testnets/' : '';
|
||||
const client = new ChainRegistryClient({
|
||||
chainNames: [this.chainName],
|
||||
baseUrl: IBC_USE_GITHUB_API ? undefined : new URL(`${prefix}`, PINGPUB_API_URL + '/').toString(),
|
||||
});
|
||||
this.fetchIBCUrls().then((res) => {
|
||||
res.forEach((element: any) => {
|
||||
if (element.download_url) {
|
||||
client.urls.push(element.download_url);
|
||||
}
|
||||
});
|
||||
client.fetchUrls().then(() => {
|
||||
const info = client.getChainIbcData(this.chainName);
|
||||
this.info = info.sort((a, b) => {
|
||||
// Sort by remote chain name (not equal to this.chainName)
|
||||
const getRemote = (x: any) =>
|
||||
x?.chain_1?.chain_name === this.chain.current?.prettyName ||
|
||||
x?.chain_1?.chain_name === this.chain.chainName
|
||||
? x.chain_2.chain_name
|
||||
: x.chain_1.chain_name;
|
||||
return getRemote(a).localeCompare(getRemote(b));
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
fetchConnection(path: string) {
|
||||
const client = new ChainRegistryClient();
|
||||
client.fetchIBCPathInfo(path).then(res => {
|
||||
const isFirstChain = res.chain_1.chain_name === this.chain.current?.prettyName || res.chain_1.chain_name === this.chain.chainName;
|
||||
async fetchIBCUrls(): Promise<any[]> {
|
||||
const prefix = this.chain.current?.networkType?.includes('testnet') ? 'testnets/' : '';
|
||||
const ibcEndpoint = new URL(`${prefix}_IBC`, IBC_API_URL + '/').toString();
|
||||
console.log('Fetching IBC URLs from:', IBC_API_URL);
|
||||
let entries = await fetch(ibcEndpoint)
|
||||
.then((res) => res.json())
|
||||
.then((data: any) => (Array.isArray(data) ? data.filter((x: any) => x.name.match(this.chainName)) : []));
|
||||
|
||||
const connId = isFirstChain
|
||||
? res.chain_1.connection_id
|
||||
: res.chain_2.connection_id;
|
||||
|
||||
this.registryConf = res;
|
||||
this.showConnection(connId);
|
||||
})
|
||||
// If using PINGPUB_API_URL, add thedownload URLs
|
||||
if (IBC_API_URL == PINGPUB_API_URL) {
|
||||
return entries.map((entry: any) => {
|
||||
entry.download_url = new URL(`${prefix}_IBC/${entry.name}`, PINGPUB_API_URL + '/').toString();
|
||||
return entry;
|
||||
});
|
||||
}
|
||||
return entries;
|
||||
},
|
||||
fetchConnection(index: number) {
|
||||
this.registryConf = this.info[index];
|
||||
const connId = this.isFirstChain
|
||||
? this.registryConf.chain_1.connection_id
|
||||
: this.registryConf.chain_2.connection_id;
|
||||
this.showConnection(connId);
|
||||
},
|
||||
showConnection(connId?: string | number) {
|
||||
if(!connId) {
|
||||
this.registryConf = {} as any
|
||||
if (!connId) {
|
||||
this.registryConf = {} as any;
|
||||
}
|
||||
const path = `/${this.chain.chainName}/ibc/connection/${connId || `connection-${this.connectionId || 0}`}`
|
||||
router.push(path)
|
||||
}
|
||||
const path = `/${this.chain.chainName}/ibc/connection/${connId || `connection-${this.connectionId || 0}`}`;
|
||||
router.push(path);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,61 +1,93 @@
|
||||
<script lang="ts" setup>
|
||||
import PaginationBar from '@/components/PaginationBar.vue';
|
||||
import { useBlockchain, useFormatter } from '@/stores';
|
||||
import { useBlockchain } from '@/stores';
|
||||
import { PageRequest, type Connection, type Pagination } from '@/types';
|
||||
import { computed, onMounted } from 'vue';
|
||||
import { onMounted } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import ChainRegistryClient from '@ping-pub/chain-registry-client';
|
||||
import type { IBCPath } from '@ping-pub/chain-registry-client/dist/types';
|
||||
import router from '@/router';
|
||||
import { useIBCModule } from './connStore';
|
||||
|
||||
const props = defineProps(['chain']);
|
||||
const chainStore = useBlockchain();
|
||||
const ibcStore = useIBCModule()
|
||||
const ibcStore = useIBCModule();
|
||||
const list = ref([] as Connection[]);
|
||||
const pageRequest = ref(new PageRequest())
|
||||
const pageResponse = ref({} as Pagination)
|
||||
const pageRequest = ref(new PageRequest());
|
||||
const pageResponse = ref({} as Pagination);
|
||||
const tab = ref('registry');
|
||||
|
||||
onMounted(() => {
|
||||
pageload(1)
|
||||
ibcStore.load()
|
||||
pageload(1);
|
||||
ibcStore.load();
|
||||
});
|
||||
|
||||
function pageload(p: number) {
|
||||
pageRequest.value.setPage(p)
|
||||
pageRequest.value.setPage(p);
|
||||
chainStore.rpc.getIBCConnections(pageRequest.value).then((x) => {
|
||||
list.value = x.connections;
|
||||
pageResponse.value = x.pagination
|
||||
if(x.pagination.total && Number(x.pagination.total) > 0) {
|
||||
ibcStore.showConnection(0)
|
||||
pageResponse.value = x.pagination;
|
||||
if (x.pagination.total && Number(x.pagination.total) > 0) {
|
||||
ibcStore.showConnection(list.value[0].id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
|
||||
<div class="flex flex-wrap gap-4 items-center">
|
||||
<div class="flex flex-wrap gap-4 items-center">
|
||||
<h2 class="card-title py-4">{{ $t('ibc.title') }}</h2>
|
||||
<div class="tabs tabs-boxed">
|
||||
<a class="tab" :class="{ 'tab-active': tab === 'registry' }" @click="tab = 'registry'">{{ $t('ibc.registry') }}</a>
|
||||
<a class="tab" :class="{ 'tab-active': tab === 'favorite' }" @click="tab = 'favorite'">{{ $t('module.favorite') }}</a>
|
||||
<a
|
||||
class="tab"
|
||||
:class="{ 'tab-active': tab === 'registry' }"
|
||||
@click="tab = 'registry'"
|
||||
>{{ $t('ibc.registry') }}</a
|
||||
>
|
||||
<a
|
||||
class="tab"
|
||||
:class="{ 'tab-active': tab === 'favorite' }"
|
||||
@click="tab = 'favorite'"
|
||||
>{{ $t('module.favorite') }}</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div v-show="tab === 'registry'" class="flex flex-wrap gap-1 p-4 ">
|
||||
<span v-for="s in ibcStore.commonIBCs" class="btn btn-xs btn-link mr-1" @click="ibcStore.fetchConnection(s.path)">{{ s.from }}
|
||||
⇌ {{ s.to }}</span>
|
||||
<div v-show="tab === 'registry'" class="flex flex-wrap gap-1 p-4">
|
||||
<span
|
||||
v-for="(s, i) in ibcStore.info"
|
||||
class="btn btn-xs btn-link mr-1"
|
||||
@click="ibcStore.fetchConnection(i)"
|
||||
>{{
|
||||
s.chain_1.chain_name === ibcStore.chainName
|
||||
? s.chain_2.chain_name
|
||||
: s.chain_1.chain_name
|
||||
}}
|
||||
⇌
|
||||
{{
|
||||
s.chain_1.chain_name === ibcStore.chainName
|
||||
? s.chain_1.chain_name
|
||||
: s.chain_2.chain_name
|
||||
}}</span
|
||||
>
|
||||
</div>
|
||||
<div v-show="tab === 'favorite'" class="flex flex-wrap gap-1 p-4 ">
|
||||
<div v-show="tab === 'favorite'" class="flex flex-wrap gap-1 p-4">
|
||||
<div class="join border border-primary">
|
||||
<button class="join-item px-2">{{ $t('ibc.connection_id') }}:</button>
|
||||
<input v-model="ibcStore.connectionId" type=number class="input input-bordered w-40 join-item" min="0"
|
||||
:max="pageResponse.total || 0" :placeholder="`0~${pageResponse.total}`" />
|
||||
<button class="join-item btn btn-primary" @click="ibcStore.showConnection()">{{ $t('ibc.btn_apply') }}</button>
|
||||
<button class="join-item px-2">
|
||||
{{ $t('ibc.connection_id') }}:
|
||||
</button>
|
||||
<input
|
||||
v-model="ibcStore.connectionId"
|
||||
type="number"
|
||||
class="input input-bordered w-40 join-item"
|
||||
min="0"
|
||||
:max="pageResponse.total || 0"
|
||||
:placeholder="`0~${pageResponse.total}`"
|
||||
/>
|
||||
<button
|
||||
class="join-item btn btn-primary"
|
||||
@click="ibcStore.showConnection()"
|
||||
>
|
||||
{{ $t('ibc.btn_apply') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import { formatSeconds } from '@/libs/utils';
|
||||
import { useBaseStore, useBlockchain, useFormatter } from '@/stores';
|
||||
import { type Connection, type ClientState, type Channel, PageRequest, type TxResponse, type PaginatedTxs } from '@/types';
|
||||
import {
|
||||
type Connection,
|
||||
type ClientState,
|
||||
type Channel,
|
||||
PageRequest,
|
||||
type TxResponse,
|
||||
type PaginatedTxs,
|
||||
} from '@/types';
|
||||
import { computed, onMounted } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import { useIBCModule } from '../connStore';
|
||||
@ -12,33 +19,31 @@ const props = defineProps(['chain', 'connection_id']);
|
||||
const chainStore = useBlockchain();
|
||||
const baseStore = useBaseStore();
|
||||
const format = useFormatter();
|
||||
const ibcStore = useIBCModule()
|
||||
const ibcStore = useIBCModule();
|
||||
const conn = ref({} as Connection);
|
||||
const clientState = ref({} as { client_id: string; client_state: ClientState });
|
||||
const channels = ref([] as Channel[]);
|
||||
|
||||
const connId = computed(() => {
|
||||
return props.connection_id || 0
|
||||
})
|
||||
return props.connection_id || 0;
|
||||
});
|
||||
|
||||
const loading = ref(false)
|
||||
const txs = ref({} as PaginatedTxs)
|
||||
const direction = ref('')
|
||||
const channel_id = ref('')
|
||||
const port_id = ref('')
|
||||
const page = ref(new PageRequest())
|
||||
page.value.limit = 5
|
||||
const loading = ref(false);
|
||||
const txs = ref({} as PaginatedTxs);
|
||||
const direction = ref('');
|
||||
const channel_id = ref('');
|
||||
const port_id = ref('');
|
||||
const page = ref(new PageRequest());
|
||||
page.value.limit = 5;
|
||||
|
||||
onMounted(() => {
|
||||
if (connId.value) {
|
||||
chainStore.rpc.getIBCConnectionsById(connId.value).then((x) => {
|
||||
conn.value = x.connection;
|
||||
});
|
||||
chainStore.rpc
|
||||
.getIBCConnectionsClientState(connId.value)
|
||||
.then((x) => {
|
||||
clientState.value = x.identified_client_state;
|
||||
});
|
||||
chainStore.rpc.getIBCConnectionsClientState(connId.value).then((x) => {
|
||||
clientState.value = x.identified_client_state;
|
||||
});
|
||||
chainStore.rpc.getIBCConnectionsChannels(connId.value).then((x) => {
|
||||
channels.value = x.channels;
|
||||
});
|
||||
@ -53,37 +58,47 @@ function loadChannel(channel: string, port: string) {
|
||||
|
||||
function pageload(pageNum: number) {
|
||||
if (direction.value === 'In') {
|
||||
fetchSendingTxs(channel_id.value, port_id.value, pageNum -1)
|
||||
fetchSendingTxs(channel_id.value, port_id.value, pageNum - 1);
|
||||
} else {
|
||||
fetchSendingTxs(channel_id.value, port_id.value, pageNum -1)
|
||||
fetchSendingTxs(channel_id.value, port_id.value, pageNum - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function fetchSendingTxs(channel: string, port: string, pageNum = 0) {
|
||||
|
||||
page.value.setPage(pageNum)
|
||||
loading.value = true
|
||||
direction.value = 'Out'
|
||||
channel_id.value = channel
|
||||
port_id.value = port
|
||||
txs.value = {} as PaginatedTxs
|
||||
chainStore.rpc.getTxs("?order_by=2&events=send_packet.packet_src_channel='{channel}'&events=send_packet.packet_src_port='{port}'", { channel, port }, page.value).then(res => {
|
||||
txs.value = res
|
||||
})
|
||||
.finally(() => loading.value = false)
|
||||
page.value.setPage(pageNum);
|
||||
loading.value = true;
|
||||
direction.value = 'Out';
|
||||
channel_id.value = channel;
|
||||
port_id.value = port;
|
||||
txs.value = {} as PaginatedTxs;
|
||||
chainStore.rpc
|
||||
.getTxs(
|
||||
"?order_by=2&events=send_packet.packet_src_channel='{channel}'&events=send_packet.packet_src_port='{port}'",
|
||||
{ channel, port },
|
||||
page.value
|
||||
)
|
||||
.then((res) => {
|
||||
txs.value = res;
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
function fetchRecevingTxs(channel: string, port: string, pageNum = 0) {
|
||||
page.value.setPage(pageNum)
|
||||
loading.value = true
|
||||
direction.value = 'In'
|
||||
channel_id.value = channel
|
||||
port_id.value = port
|
||||
txs.value = {} as PaginatedTxs
|
||||
chainStore.rpc.getTxs("?order_by=2&events=recv_packet.packet_dst_channel='{channel}'&events=recv_packet.packet_dst_port='{port}'", { channel, port }, page.value).then(res => {
|
||||
txs.value = res
|
||||
})
|
||||
.finally(() => loading.value = false)
|
||||
page.value.setPage(pageNum);
|
||||
loading.value = true;
|
||||
direction.value = 'In';
|
||||
channel_id.value = channel;
|
||||
port_id.value = port;
|
||||
txs.value = {} as PaginatedTxs;
|
||||
chainStore.rpc
|
||||
.getTxs(
|
||||
"?order_by=2&events=recv_packet.packet_dst_channel='{channel}'&events=recv_packet.packet_dst_port='{port}'",
|
||||
{ channel, port },
|
||||
page.value
|
||||
)
|
||||
.then((res) => {
|
||||
txs.value = res;
|
||||
})
|
||||
.finally(() => (loading.value = false));
|
||||
}
|
||||
|
||||
function color(v: string) {
|
||||
@ -95,12 +110,14 @@ function color(v: string) {
|
||||
</script>
|
||||
<template>
|
||||
<div class="">
|
||||
<div class="px-4 pt-3 pb-4 bg-base-200 rounded mb-4 shadow ">
|
||||
<div class="px-4 pt-3 pb-4 bg-base-200 rounded mb-4 shadow">
|
||||
<div class="mx-auto max-w-7xl px-6 lg:!px-8">
|
||||
<dl class="grid grid-cols-1 gap-x-6 text-center lg:!grid-cols-3">
|
||||
<div class="mx-auto flex items-center">
|
||||
<div>
|
||||
<div class="order-first text-3xl font-semibold tracking-tight text-main mb-1">
|
||||
<div
|
||||
class="order-first text-3xl font-semibold tracking-tight text-main mb-1"
|
||||
>
|
||||
{{ baseStore.latest?.block?.header?.chain_id }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
@ -111,13 +128,15 @@ function color(v: string) {
|
||||
<div class="mx-auto flex items-center">
|
||||
<div :class="{ 'text-success': conn.state?.indexOf('_OPEN') > -1 }">
|
||||
<span class="text-lg rounded-full">⇌</span>
|
||||
<div class=" text-c">
|
||||
<div class="text-c">
|
||||
{{ conn.state }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx-auto">
|
||||
<div class="order-first text-3xl font-semibold tracking-tight text-main mb-2">
|
||||
<div
|
||||
class="order-first text-3xl font-semibold tracking-tight text-main mb-2"
|
||||
>
|
||||
{{ clientState.client_state?.chain_id }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
@ -129,8 +148,12 @@ function color(v: string) {
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
|
||||
<h2 class="card-title mb-4 overflow-hidden">{{ $t('ibc.title_2') }}<span class="ml-2 text-sm">{{
|
||||
clientState.client_state?.['@type'] }}</span></h2>
|
||||
<h2 class="card-title mb-4 overflow-hidden">
|
||||
{{ $t('ibc.title_2')
|
||||
}}<span class="ml-2 text-sm">{{
|
||||
clientState.client_state?.['@type']
|
||||
}}</span>
|
||||
</h2>
|
||||
<div class="overflow-x-auto grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<table class="table table-sm capitalize">
|
||||
<thead class="bg-base-200">
|
||||
@ -149,15 +172,21 @@ function color(v: string) {
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-52">{{ $t('ibc.trusting_period') }}:</td>
|
||||
<td>{{ formatSeconds(clientState.client_state?.trusting_period) }}</td>
|
||||
<td>
|
||||
{{ formatSeconds(clientState.client_state?.trusting_period) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-52">{{ $t('ibc.unbonding_period') }}:</td>
|
||||
<td>{{ formatSeconds(clientState.client_state?.unbonding_period) }}</td>
|
||||
<td>
|
||||
{{ formatSeconds(clientState.client_state?.unbonding_period) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-52">{{ $t('ibc.max_clock_drift') }}:</td>
|
||||
<td>{{ formatSeconds(clientState.client_state?.max_clock_drift) }}</td>
|
||||
<td>
|
||||
{{ formatSeconds(clientState.client_state?.max_clock_drift) }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-52">{{ $t('ibc.frozen_height') }}:</td>
|
||||
@ -178,23 +207,32 @@ function color(v: string) {
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="flex justify-between"><span>{{ $t('ibc.allow_update_after_expiry') }}:</span> <span>{{
|
||||
clientState.client_state?.allow_update_after_expiry }}</span></div>
|
||||
<div class="flex justify-between">
|
||||
<span>{{ $t('ibc.allow_update_after_expiry') }}:</span>
|
||||
<span>{{
|
||||
clientState.client_state?.allow_update_after_expiry
|
||||
}}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<div class="flex justify-between"><span>{{ $t('ibc.allow_update_after_misbehaviour') }}: </span> <span>{{
|
||||
clientState.client_state?.allow_update_after_misbehaviour }}</span></div>
|
||||
<div class="flex justify-between">
|
||||
<span>{{ $t('ibc.allow_update_after_misbehaviour') }}: </span>
|
||||
<span>{{
|
||||
clientState.client_state?.allow_update_after_misbehaviour
|
||||
}}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="w-52">{{ $t('ibc.upgrade_path') }}:</td>
|
||||
<td class="text-right">{{ clientState.client_state?.upgrade_path.join(', ') }}</td>
|
||||
<td class="text-right">
|
||||
{{ clientState.client_state?.upgrade_path.join(', ') }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow overflow-hidden">
|
||||
@ -204,7 +242,9 @@ function color(v: string) {
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('ibc.txs') }}</th>
|
||||
<th style="position: relative; z-index: 2">{{ $t('ibc.channel_id') }}</th>
|
||||
<th style="position: relative; z-index: 2">
|
||||
{{ $t('ibc.channel_id') }}
|
||||
</th>
|
||||
<th>{{ $t('ibc.port_id') }}</th>
|
||||
<th>{{ $t('ibc.state') }}</th>
|
||||
<th>{{ $t('ibc.counterparty') }}</th>
|
||||
@ -214,39 +254,29 @@ function color(v: string) {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="v in ibcStore.registryChannels">
|
||||
<td>
|
||||
<div class="flex gap-1">
|
||||
<button class="btn btn-xs"
|
||||
@click="fetchSendingTxs(v[ibcStore.sourceField].channel_id, v[ibcStore.sourceField].port_id)"
|
||||
:disabled="loading">
|
||||
<span v-if="loading" class="loading loading-spinner loading-sm"></span>
|
||||
{{ $t('ibc.btn_out') }}
|
||||
</button>
|
||||
<button class="btn btn-xs"
|
||||
@click="fetchRecevingTxs(v[ibcStore.sourceField].channel_id, v[ibcStore.sourceField].port_id)"
|
||||
:disabled="loading">
|
||||
<span v-if="loading" class="loading loading-spinner loading-sm"></span>
|
||||
{{ $t('ibc.btn_in') }}
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<a href="#">{{
|
||||
v[ibcStore.sourceField].channel_id
|
||||
}}</a>
|
||||
</td>
|
||||
<td>{{ v[ibcStore.sourceField].port_id }}</td>
|
||||
</tr>
|
||||
<tr v-for="v in channels">
|
||||
<td>
|
||||
<div class="flex gap-1">
|
||||
<button class="btn btn-xs" @click="fetchSendingTxs(v.channel_id, v.port_id)" :disabled="loading">
|
||||
<span v-if="loading" class="loading loading-spinner loading-sm"></span>
|
||||
<button
|
||||
class="btn btn-xs"
|
||||
@click="fetchSendingTxs(v.channel_id, v.port_id)"
|
||||
:disabled="loading"
|
||||
>
|
||||
<span
|
||||
v-if="loading"
|
||||
class="loading loading-spinner loading-sm"
|
||||
></span>
|
||||
{{ $t('ibc.btn_out') }}
|
||||
</button>
|
||||
<button class="btn btn-xs" @click="fetchRecevingTxs(v.channel_id, v.port_id)" :disabled="loading">
|
||||
<span v-if="loading" class="loading loading-spinner loading-sm"></span>
|
||||
<button
|
||||
class="btn btn-xs"
|
||||
@click="fetchRecevingTxs(v.channel_id, v.port_id)"
|
||||
:disabled="loading"
|
||||
>
|
||||
<span
|
||||
v-if="loading"
|
||||
class="loading loading-spinner loading-sm"
|
||||
></span>
|
||||
{{ $t('ibc.btn_in') }}
|
||||
</button>
|
||||
</div>
|
||||
@ -258,8 +288,14 @@ function color(v: string) {
|
||||
</td>
|
||||
<td>{{ v.port_id }}</td>
|
||||
<td>
|
||||
<div class="text-xs truncate relative py-2 px-4 rounded-full w-fit" :class="`text-${color(v.state)}`">
|
||||
<span class="inset-x-0 inset-y-0 opacity-10 absolute" :class="`bg-${color(v.state)}`"></span>
|
||||
<div
|
||||
class="text-xs truncate relative py-2 px-4 rounded-full w-fit"
|
||||
:class="`text-${color(v.state)}`"
|
||||
>
|
||||
<span
|
||||
class="inset-x-0 inset-y-0 opacity-10 absolute"
|
||||
:class="`bg-${color(v.state)}`"
|
||||
></span>
|
||||
{{ v.state }}
|
||||
</div>
|
||||
</td>
|
||||
@ -275,13 +311,15 @@ function color(v: string) {
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="channel_id">
|
||||
<h3 class=" card-title capitalize">Transactions ({{ channel_id }} {{ port_id }} {{ direction }}) </h3>
|
||||
<h3 class="card-title capitalize">
|
||||
Transactions ({{ channel_id }} {{ port_id }} {{ direction }})
|
||||
</h3>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td> {{ $t('ibc.height') }}</td>
|
||||
<td>{{ $t('ibc.height') }}</td>
|
||||
<td>{{ $t('ibc.txhash') }}</td>
|
||||
<td> {{ $t('ibc.messages') }}</td>
|
||||
<td>{{ $t('ibc.messages') }}</td>
|
||||
<td>{{ $t('ibc.time') }}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -290,13 +328,20 @@ function color(v: string) {
|
||||
<td>{{ resp.height }}</td>
|
||||
<td>
|
||||
<div class="text-xs truncate text-primary dark:invert">
|
||||
<RouterLink :to="`/${chainStore.chainName}/tx/${resp.txhash}`">{{ resp.txhash }}</RouterLink>
|
||||
<RouterLink
|
||||
:to="`/${chainStore.chainName}/tx/${resp.txhash}`"
|
||||
>{{ resp.txhash }}</RouterLink
|
||||
>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex">
|
||||
{{ format.messages(resp.tx.body.messages) }}
|
||||
<Icon v-if="resp.code === 0" icon="mdi-check" class="text-success text-lg" />
|
||||
<Icon
|
||||
v-if="resp.code === 0"
|
||||
icon="mdi-check"
|
||||
class="text-success text-lg"
|
||||
/>
|
||||
<Icon v-else icon="mdi-multiply" class="text-error text-lg" />
|
||||
</div>
|
||||
</td>
|
||||
@ -304,7 +349,11 @@ function color(v: string) {
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<PaginationBar :limit="page.limit" :total="txs.pagination?.total" :callback="pageload" />
|
||||
<PaginationBar
|
||||
:limit="page.limit"
|
||||
:total="txs.pagination?.total"
|
||||
:callback="pageload"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// router.push(`/${props.chain}/ibc/connection/connection-0`)
|
||||
</script>
|
||||
<template>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</template>
|
||||
<route>
|
||||
{
|
||||
@ -11,4 +11,4 @@
|
||||
order: 9
|
||||
}
|
||||
}
|
||||
</route>
|
||||
</route>
|
||||
|
||||
@ -3,22 +3,14 @@ import MdEditor from 'md-editor-v3';
|
||||
import PriceMarketChart from '@/components/charts/PriceMarketChart.vue';
|
||||
|
||||
import { Icon } from '@iconify/vue';
|
||||
import {
|
||||
useBlockchain,
|
||||
useFormatter,
|
||||
useTxDialog,
|
||||
useWalletStore,
|
||||
useStakingStore,
|
||||
useParamStore,
|
||||
} from '@/stores';
|
||||
import { useBlockchain, useFormatter, useTxDialog, useWalletStore, useStakingStore, useParamStore } from '@/stores';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useIndexModule, colorMap } from './indexStore';
|
||||
import { useIndexModule, colorMap, tickerUrl } from './indexStore';
|
||||
import { computed } from '@vue/reactivity';
|
||||
|
||||
import CardStatisticsVertical from '@/components/CardStatisticsVertical.vue';
|
||||
import ProposalListItem from '@/components/ProposalListItem.vue';
|
||||
import ArrayObjectElement from '@/components/dynamic/ArrayObjectElement.vue'
|
||||
import AdBanner from '@/components/ad/AdBanner.vue';
|
||||
import ArrayObjectElement from '@/components/dynamic/ArrayObjectElement.vue';
|
||||
|
||||
const props = defineProps(['chain']);
|
||||
|
||||
@ -28,7 +20,7 @@ const walletStore = useWalletStore();
|
||||
const format = useFormatter();
|
||||
const dialog = useTxDialog();
|
||||
const stakingStore = useStakingStore();
|
||||
const paramStore = useParamStore()
|
||||
const paramStore = useParamStore();
|
||||
const coinInfo = computed(() => {
|
||||
return store.coinInfo;
|
||||
});
|
||||
@ -36,50 +28,49 @@ const coinInfo = computed(() => {
|
||||
onMounted(() => {
|
||||
store.loadDashboard();
|
||||
walletStore.loadMyAsset();
|
||||
paramStore.handleAbciInfo()
|
||||
paramStore.handleAbciInfo();
|
||||
// if(!(coinInfo.value && coinInfo.value.name)) {
|
||||
// }
|
||||
});
|
||||
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
|
||||
|
||||
const currName = ref("")
|
||||
const currName = ref('');
|
||||
blockchain.$subscribe((m, s) => {
|
||||
if (s.chainName !== currName.value) {
|
||||
currName.value = s.chainName
|
||||
currName.value = s.chainName;
|
||||
store.loadDashboard();
|
||||
walletStore.loadMyAsset();
|
||||
paramStore.handleAbciInfo()
|
||||
paramStore.handleAbciInfo();
|
||||
}
|
||||
});
|
||||
function shortName(name: string, id: string) {
|
||||
return name.toLowerCase().startsWith('ibc/') ||
|
||||
name.toLowerCase().startsWith('0x')
|
||||
? id
|
||||
: name;
|
||||
return name.toLowerCase().startsWith('ibc/') || name.toLowerCase().startsWith('0x') ? id : name;
|
||||
}
|
||||
|
||||
const comLinks = [
|
||||
{
|
||||
name: 'Website',
|
||||
icon: 'mdi-web',
|
||||
href: store.homepage,
|
||||
},
|
||||
{
|
||||
name: 'Twitter',
|
||||
icon: 'mdi-twitter',
|
||||
href: store.twitter,
|
||||
},
|
||||
{
|
||||
name: 'Telegram',
|
||||
icon: 'mdi-telegram',
|
||||
href: store.telegram,
|
||||
},
|
||||
{
|
||||
name: 'Github',
|
||||
icon: 'mdi-github',
|
||||
href: store.github,
|
||||
},
|
||||
];
|
||||
const comLinks = computed(() => {
|
||||
return [
|
||||
{
|
||||
name: 'Website',
|
||||
icon: 'mdi-web',
|
||||
href: store.homepage,
|
||||
},
|
||||
{
|
||||
name: 'Twitter',
|
||||
icon: 'mdi-twitter',
|
||||
href: store.twitter,
|
||||
},
|
||||
{
|
||||
name: 'Telegram',
|
||||
icon: 'mdi-telegram',
|
||||
href: store.telegram,
|
||||
},
|
||||
{
|
||||
name: 'Github',
|
||||
icon: 'mdi-github',
|
||||
href: store.github,
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
// wallet box
|
||||
const change = computed(() => {
|
||||
@ -98,31 +89,30 @@ const color = computed(() => {
|
||||
});
|
||||
|
||||
function updateState() {
|
||||
walletStore.loadMyAsset()
|
||||
walletStore.loadMyAsset();
|
||||
}
|
||||
|
||||
function trustColor(v: string) {
|
||||
return `text-${colorMap(v)}`
|
||||
return `text-${colorMap(v)}`;
|
||||
}
|
||||
|
||||
const quantity = ref(100)
|
||||
const quantity = ref(100);
|
||||
const qty = computed({
|
||||
get: () => {
|
||||
return parseFloat(quantity.value.toFixed(6))
|
||||
return parseFloat(quantity.value.toFixed(6));
|
||||
},
|
||||
set: val => {
|
||||
quantity.value = val
|
||||
}
|
||||
})
|
||||
set: (val) => {
|
||||
quantity.value = val;
|
||||
},
|
||||
});
|
||||
const amount = computed({
|
||||
get: () => {
|
||||
return quantity.value * ticker.value.converted_last.usd || 0
|
||||
return quantity.value * ticker.value.converted_last.usd || 0;
|
||||
},
|
||||
set: val => {
|
||||
quantity.value = val / ticker.value.converted_last.usd || 0
|
||||
}
|
||||
})
|
||||
|
||||
set: (val) => {
|
||||
quantity.value = val / ticker.value.converted_last.usd || 0;
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -131,20 +121,25 @@ const amount = computed({
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 p-4">
|
||||
<div class="col-span-2 md:col-span-1">
|
||||
<div class="text-xl font-semibold text-main">
|
||||
{{ coinInfo.name }} (<span class="uppercase">{{
|
||||
coinInfo.symbol
|
||||
}}</span>)
|
||||
{{ coinInfo.name }} (<span class="uppercase">{{ coinInfo.symbol }}</span
|
||||
>)
|
||||
</div>
|
||||
<div class="text-xs mt-2">
|
||||
{{ $t('index.rank') }}:
|
||||
<div class="badge text-xs badge-error bg-[#fcebea] dark:bg-[#41384d] text-red-400">
|
||||
<div
|
||||
class="badge text-xs badge-error bg-[#fcebea] dark:bg-[#41384d] text-red-400"
|
||||
>
|
||||
#{{ coinInfo.market_cap_rank }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-4 flex flex-wrap items-center">
|
||||
<a v-for="(item, index) of comLinks" :key="index" :href="item.href"
|
||||
class="link link-primary px-2 py-1 rounded-sm no-underline hover:text-primary hover:bg-gray-100 dark:hover:bg-slate-800 flex items-center">
|
||||
<a
|
||||
v-for="(item, index) of comLinks"
|
||||
:key="index"
|
||||
:href="item.href"
|
||||
class="link link-primary px-2 py-1 rounded-sm no-underline hover:text-primary hover:bg-gray-100 dark:hover:bg-slate-800 flex items-center"
|
||||
>
|
||||
<Icon :icon="item?.icon" />
|
||||
<span class="ml-1 text-sm uppercase">{{ item?.name }}</span>
|
||||
</a>
|
||||
@ -154,9 +149,12 @@ const amount = computed({
|
||||
<div class="dropdown dropdown-hover w-full">
|
||||
<label>
|
||||
<div
|
||||
class="bg-gray-100 dark:bg-[#384059] flex items-center justify-between px-4 py-2 cursor-pointer rounded">
|
||||
class="bg-gray-100 dark:bg-[#384059] flex items-center justify-between px-4 py-2 cursor-pointer rounded"
|
||||
>
|
||||
<div>
|
||||
<div class="font-semibold text-xl text-[#666] dark:text-white">
|
||||
<div
|
||||
class="font-semibold text-xl text-[#666] dark:text-white"
|
||||
>
|
||||
{{ ticker?.market?.name || '' }}
|
||||
</div>
|
||||
<div class="text-info text-sm">
|
||||
@ -167,22 +165,31 @@ const amount = computed({
|
||||
</div>
|
||||
|
||||
<div class="text-right">
|
||||
<div class="text-xl font-semibold text-[#666] dark:text-white">
|
||||
<div
|
||||
class="text-xl font-semibold text-[#666] dark:text-white"
|
||||
>
|
||||
${{ ticker?.converted_last?.usd }}
|
||||
</div>
|
||||
<div class="text-sm" :class="store.priceColor">
|
||||
{{ store.priceChange }}%
|
||||
</div>
|
||||
<div class="text-sm" :class="store.priceColor">{{ store.priceChange }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</label>
|
||||
<div class="dropdown-content pt-1">
|
||||
<div class="h-64 overflow-auto w-full shadow rounded">
|
||||
<ul class="menu w-full bg-gray-100 rounded dark:bg-[#384059]">
|
||||
<li v-for="(item, index) in store.coinInfo.tickers" :key="index" @click="store.selectTicker(index)">
|
||||
<div class="flex items-center justify-between hover:bg-base-100">
|
||||
<li
|
||||
v-for="(item, index) in store.coinInfo.tickers"
|
||||
:key="index"
|
||||
@click="store.selectTicker(index)"
|
||||
>
|
||||
<div
|
||||
class="flex items-center justify-between hover:bg-base-100"
|
||||
>
|
||||
<div class="flex-1">
|
||||
<div class="text-main text-sm" :class="trustColor(item.trust_score)">
|
||||
<div
|
||||
class="text-main text-sm"
|
||||
:class="trustColor(item.trust_score)"
|
||||
>
|
||||
{{ item?.market?.name }}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||
@ -192,9 +199,7 @@ const amount = computed({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-base text-main">
|
||||
${{ item?.converted_last?.usd }}
|
||||
</div>
|
||||
<div class="text-base text-main">${{ item?.converted_last?.usd }}</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@ -204,37 +209,85 @@ const amount = computed({
|
||||
|
||||
<div class="flex">
|
||||
<label class="btn btn-primary !px-1 my-5 mr-2" for="calculator">
|
||||
<svg class="w-8 h-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <rect x="4" y="2" width="16" height="20" rx="2"></rect> <line x1="8" x2="16" y1="6" y2="6"></line> <line x1="16" x2="16" y1="14" y2="18"></line> <path d="M16 10h.01"></path> <path d="M12 10h.01"></path> <path d="M8 10h.01"></path> <path d="M12 14h.01"></path> <path d="M8 14h.01"></path> <path d="M12 18h.01"></path> <path d="M8 18h.01"></path> </g></svg>
|
||||
<svg
|
||||
class="w-8 h-8"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#ffffff"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<rect x="4" y="2" width="16" height="20" rx="2"></rect>
|
||||
<line x1="8" x2="16" y1="6" y2="6"></line>
|
||||
<line x1="16" x2="16" y1="14" y2="18"></line>
|
||||
<path d="M16 10h.01"></path>
|
||||
<path d="M12 10h.01"></path>
|
||||
<path d="M8 10h.01"></path>
|
||||
<path d="M12 14h.01"></path>
|
||||
<path d="M8 14h.01"></path>
|
||||
<path d="M12 18h.01"></path>
|
||||
<path d="M8 18h.01"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</label>
|
||||
<!-- Put this part before </body> tag -->
|
||||
<input type="checkbox" id="calculator" class="modal-toggle" />
|
||||
<div class="modal">
|
||||
<div class="modal-box">
|
||||
<h3 class="text-lg font-bold">{{ $t('index.price_calculator') }}</h3>
|
||||
<h3 class="text-lg font-bold">
|
||||
{{ $t('index.price_calculator') }}
|
||||
</h3>
|
||||
<div class="flex flex-col w-full mt-5">
|
||||
<div class="grid h-20 flex-grow card rounded-box place-items-center">
|
||||
<div
|
||||
class="grid h-20 flex-grow card rounded-box place-items-center"
|
||||
>
|
||||
<div class="join w-full">
|
||||
<label class="join-item btn">
|
||||
<span class="uppercase">{{ coinInfo.symbol }}</span>
|
||||
</label>
|
||||
<input type="number" v-model="qty" min="0" placeholder="Input a number" class="input grow input-bordered join-item" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="qty"
|
||||
min="0"
|
||||
placeholder="Input a number"
|
||||
class="input grow input-bordered join-item"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider">=</div>
|
||||
<div class="grid h-20 flex-grow card rounded-box place-items-center">
|
||||
<div
|
||||
class="grid h-20 flex-grow card rounded-box place-items-center"
|
||||
>
|
||||
<div class="join w-full">
|
||||
<label class="join-item btn">
|
||||
<span>USD</span>
|
||||
</label>
|
||||
<input type="number" v-model="amount" min="0" placeholder="Input amount" class="join-item grow input input-bordered" />
|
||||
<input
|
||||
type="number"
|
||||
v-model="amount"
|
||||
min="0"
|
||||
placeholder="Input amount"
|
||||
class="join-item grow input input-bordered"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="modal-backdrop" for="calculator">{{ $t('index.close') }}</label>
|
||||
<label class="modal-backdrop" for="calculator">{{
|
||||
$t('index.close')
|
||||
}}</label>
|
||||
</div>
|
||||
<a class="my-5 !text-white btn grow" :class="{'!btn-success': store.trustColor === 'green', '!btn-warning': store.trustColor === 'yellow'}" :href="ticker.trade_url"
|
||||
target="_blank">
|
||||
<a
|
||||
class="my-5 !text-white btn grow"
|
||||
:class="{ '!btn-success': store.trustColor === 'green', '!btn-warning': store.trustColor === 'yellow' }"
|
||||
:href="tickerUrl(ticker.trade_url)"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t('index.buy') }} {{ coinInfo.symbol || '' }}
|
||||
</a>
|
||||
</div>
|
||||
@ -247,11 +300,16 @@ const amount = computed({
|
||||
</div>
|
||||
<div class="h-[1px] w-full bg-gray-100 dark:bg-[#384059]"></div>
|
||||
<div class="max-h-[250px] overflow-auto p-4 text-sm">
|
||||
<MdEditor :model-value="coinInfo.description?.en" previewOnly></MdEditor>
|
||||
<MdEditor
|
||||
:model-value="coinInfo.description?.en"
|
||||
previewOnly
|
||||
></MdEditor>
|
||||
</div>
|
||||
<div class="mx-4 flex flex-wrap items-center">
|
||||
<div v-for="tag in coinInfo.categories"
|
||||
class="mr-2 mb-4 text-xs bg-gray-100 dark:bg-[#384059] px-3 rounded-full py-1">
|
||||
<div
|
||||
v-for="tag in coinInfo.categories"
|
||||
class="mr-2 mb-4 text-xs bg-gray-100 dark:bg-[#384059] px-3 rounded-full py-1"
|
||||
>
|
||||
{{ tag }}
|
||||
</div>
|
||||
</div>
|
||||
@ -263,67 +321,71 @@ const amount = computed({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AdBanner id="chain-home-banner-ad" unit="banner" width="970px" height="90px" />
|
||||
|
||||
<div v-if="blockchain.supportModule('governance')" class="bg-base-100 rounded mt-4 shadow">
|
||||
<div
|
||||
v-if="blockchain.supportModule('governance')"
|
||||
class="bg-base-100 rounded mt-4 shadow"
|
||||
>
|
||||
<div class="px-4 pt-4 pb-2 text-lg font-semibold text-main">
|
||||
{{ $t('index.active_proposals') }}
|
||||
</div>
|
||||
<div class="px-4 pb-4">
|
||||
<ProposalListItem :proposals="store?.proposals" />
|
||||
</div>
|
||||
<div class="pb-8 text-center" v-if="store.proposals?.proposals?.length === 0">
|
||||
<div
|
||||
class="pb-8 text-center"
|
||||
v-if="store.proposals?.proposals?.length === 0"
|
||||
>
|
||||
{{ $t('index.no_active_proposals') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-base-100 rounded mt-4 shadow">
|
||||
<div class="flex justify-between px-4 pt-4 pb-2 text-lg font-semibold text-main">
|
||||
<span class="truncate" >{{ walletStore.currentAddress || 'Not Connected' }}</span>
|
||||
<RouterLink v-if="walletStore.currentAddress"
|
||||
<span class="truncate">{{ walletStore.currentAddress || 'Not Connected' }}</span>
|
||||
<RouterLink
|
||||
v-if="walletStore.currentAddress"
|
||||
class="float-right text-sm cursor-pointert link link-primary no-underline font-medium"
|
||||
:to="`/${chain}/account/${walletStore.currentAddress}`">{{ $t('index.more') }}</RouterLink>
|
||||
:to="`/${chain}/account/${walletStore.currentAddress}`"
|
||||
>{{ $t('index.more') }}</RouterLink
|
||||
>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:!grid-cols-4 auto-cols-auto gap-4 px-4 pb-6">
|
||||
<div
|
||||
class="grid grid-cols-1 md:!grid-cols-4 auto-cols-auto gap-4 px-4 pb-6"
|
||||
>
|
||||
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
|
||||
<div class="text-sm mb-1">{{ $t('account.balance') }}</div>
|
||||
<div class="text-lg font-semibold text-main">
|
||||
{{ format.formatToken(walletStore.balanceOfStakingToken) }}
|
||||
</div>
|
||||
<div class="text-sm" :class="color">
|
||||
${{ format.tokenValue(walletStore.balanceOfStakingToken) }}
|
||||
</div>
|
||||
<div class="text-sm" :class="color">${{ format.tokenValue(walletStore.balanceOfStakingToken) }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
|
||||
<div class="text-sm mb-1">{{ $t('module.staking') }}</div>
|
||||
<div class="text-lg font-semibold text-main">
|
||||
{{ format.formatToken(walletStore.stakingAmount) }}
|
||||
</div>
|
||||
<div class="text-sm" :class="color">
|
||||
${{ format.tokenValue(walletStore.stakingAmount) }}
|
||||
</div>
|
||||
<div class="text-sm" :class="color">${{ format.tokenValue(walletStore.stakingAmount) }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
|
||||
<div class="text-sm mb-1">{{ $t('index.reward') }}</div>
|
||||
<div class="text-lg font-semibold text-main">
|
||||
{{ format.formatToken(walletStore.rewardAmount) }}
|
||||
</div>
|
||||
<div class="text-sm" :class="color">
|
||||
${{ format.tokenValue(walletStore.rewardAmount) }}
|
||||
</div>
|
||||
<div class="text-sm" :class="color">${{ format.tokenValue(walletStore.rewardAmount) }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-100 dark:bg-[#373f59] rounded-sm px-4 py-3">
|
||||
<div class="text-sm mb-1">{{ $t('index.unbonding') }}</div>
|
||||
<div class="text-lg font-semibold text-main">
|
||||
{{ format.formatToken(walletStore.unbondingAmount) }}
|
||||
</div>
|
||||
<div class="text-sm" :class="color">
|
||||
${{ format.tokenValue(walletStore.unbondingAmount) }}
|
||||
</div>
|
||||
<div class="text-sm" :class="color">${{ format.tokenValue(walletStore.unbondingAmount) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="walletStore.delegations.length > 0" class="px-4 pb-4 overflow-auto">
|
||||
<div
|
||||
v-if="walletStore.delegations.length > 0"
|
||||
class="px-4 pb-4 overflow-auto"
|
||||
>
|
||||
<table class="table table-compact w-full table-zebra">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -336,12 +398,11 @@ const amount = computed({
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in walletStore.delegations" :key="index">
|
||||
<td>
|
||||
<RouterLink class="link link-primary no-underline" :to="`/${chain}/staking/${item?.delegation?.validator_address}`">
|
||||
{{
|
||||
format.validatorFromBech32(
|
||||
item?.delegation?.validator_address
|
||||
)
|
||||
}}
|
||||
<RouterLink
|
||||
class="link link-primary no-underline"
|
||||
:to="`/${chain}/staking/${item?.delegation?.validator_address}`"
|
||||
>
|
||||
{{ format.validatorFromBech32(item?.delegation?.validator_address) }}
|
||||
</RouterLink>
|
||||
</td>
|
||||
<td>{{ format.formatToken(item?.balance) }}</td>
|
||||
@ -349,20 +410,29 @@ const amount = computed({
|
||||
{{
|
||||
format.formatTokens(
|
||||
walletStore?.rewards?.rewards?.find(
|
||||
(el) =>
|
||||
el?.validator_address ===
|
||||
item?.delegation?.validator_address
|
||||
)?.reward)
|
||||
(el) => el?.validator_address === item?.delegation?.validator_address
|
||||
)?.reward
|
||||
)
|
||||
}}
|
||||
</td>
|
||||
<td>
|
||||
<div>
|
||||
<label for="delegate" class="btn !btn-xs !btn-primary btn-ghost rounded-sm mr-2"
|
||||
@click="dialog.open('delegate', { validator_address: item.delegation.validator_address }, updateState)">
|
||||
<label
|
||||
for="delegate"
|
||||
class="btn !btn-xs !btn-primary btn-ghost rounded-sm mr-2"
|
||||
@click="
|
||||
dialog.open('delegate', { validator_address: item.delegation.validator_address }, updateState)
|
||||
"
|
||||
>
|
||||
{{ $t('account.btn_delegate') }}
|
||||
</label>
|
||||
<label for="withdraw" class="btn !btn-xs !btn-primary btn-ghost rounded-sm"
|
||||
@click="dialog.open('withdraw', { validator_address: item.delegation.validator_address }, updateState)">
|
||||
<label
|
||||
for="withdraw"
|
||||
class="btn !btn-xs !btn-primary btn-ghost rounded-sm"
|
||||
@click="
|
||||
dialog.open('withdraw', { validator_address: item.delegation.validator_address }, updateState)
|
||||
"
|
||||
>
|
||||
{{ $t('index.btn_withdraw_reward') }}
|
||||
</label>
|
||||
</div>
|
||||
@ -374,14 +444,25 @@ const amount = computed({
|
||||
|
||||
<div class="grid grid-cols-3 gap-4 px-4 pb-6 mt-4">
|
||||
<label for="PingTokenConvert" class="btn btn-primary text-white">{{ $t('index.btn_swap') }}</label>
|
||||
<label for="send" class="btn !bg-yes !border-yes text-white" @click="dialog.open('send', {}, updateState)">{{ $t('account.btn_send') }}</label>
|
||||
<label for="delegate" class="btn !bg-info !border-info text-white"
|
||||
@click="dialog.open('delegate', {}, updateState)">{{ $t('account.btn_delegate') }}</label>
|
||||
<RouterLink to="/wallet/receive" class="btn !bg-info !border-info text-white hidden">{{ $t('index.receive') }}</RouterLink>
|
||||
<label for="send" class="btn !bg-yes !border-yes text-white" @click="dialog.open('send', {}, updateState)">{{
|
||||
$t('account.btn_send')
|
||||
}}</label>
|
||||
<label
|
||||
for="delegate"
|
||||
class="btn !bg-info !border-info text-white"
|
||||
@click="dialog.open('delegate', {}, updateState)"
|
||||
>{{ $t('account.btn_delegate') }}</label
|
||||
>
|
||||
<RouterLink to="/wallet/receive" class="btn !bg-info !border-info text-white hidden">{{
|
||||
$t('index.receive')
|
||||
}}</RouterLink>
|
||||
</div>
|
||||
<Teleport to="body">
|
||||
<ping-token-convert :chain-name="blockchain?.current?.prettyName" :endpoint="blockchain?.endpoint?.address"
|
||||
:hd-path="walletStore?.connectedWallet?.hdPath"></ping-token-convert>
|
||||
<ping-token-convert
|
||||
:chain-name="blockchain?.current?.prettyName"
|
||||
:endpoint="blockchain?.endpoint?.address"
|
||||
:hd-path="walletStore?.connectedWallet?.hdPath"
|
||||
></ping-token-convert>
|
||||
</Teleport>
|
||||
</div>
|
||||
|
||||
@ -390,7 +471,10 @@ const amount = computed({
|
||||
{{ $t('index.app_versions') }}
|
||||
</div>
|
||||
<!-- Application Version -->
|
||||
<ArrayObjectElement :value="paramStore.appVersion?.items" :thead="false" />
|
||||
<ArrayObjectElement
|
||||
:value="paramStore.appVersion?.items"
|
||||
:thead="false"
|
||||
/>
|
||||
<div class="h-4"></div>
|
||||
</div>
|
||||
|
||||
@ -398,7 +482,7 @@ const amount = computed({
|
||||
<div class="px-4 pt-4 pb-2 text-lg font-semibold text-main">
|
||||
{{ $t('index.node_info') }}
|
||||
</div>
|
||||
<ArrayObjectElement :value="paramStore.nodeVersion?.items" :thead="false" />
|
||||
<ArrayObjectElement :value="paramStore.nodeVersion?.items" :thead="false" />
|
||||
<div class="h-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user