@mictonode/widget 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +3 -0
- package/.github/workflows/npm-publish.yml +34 -0
- package/.prettierrc.json +9 -0
- package/.vscode/extensions.json +3 -0
- package/README.md +41 -0
- package/dist/main-172a6585.js +404361 -0
- package/dist/ping-widget.js +13 -0
- package/dist/ping-widget.umd.cjs +96 -0
- package/dist/query.lcd-2adf1c0c.js +129 -0
- package/dist/query.rpc.Query-b53908e7.js +2316 -0
- package/dist/tx.rpc.msg-d234ff40.js +37 -0
- package/dist/tx.rpc.msg-f7a80a78.js +21 -0
- package/example/.vscode/extensions.json +3 -0
- package/example/README.md +7 -0
- package/example/index.html +12 -0
- package/example/package.json +19 -0
- package/example/pnpm-lock.yaml +465 -0
- package/example/public/cdn.html +19 -0
- package/example/src/App.vue +73 -0
- package/example/src/main.js +4 -0
- package/example/vite.config.js +7 -0
- package/index.html +12 -0
- package/lib/components/ConnectWallet/index.vue +267 -0
- package/lib/components/TokenConvert/index.vue +1033 -0
- package/lib/components/TokenConvert/tokens.ts +37 -0
- package/lib/components/TxDialog/index.vue +432 -0
- package/lib/components/TxDialog/messages/Delegate.vue +194 -0
- package/lib/components/TxDialog/messages/Deposit.vue +97 -0
- package/lib/components/TxDialog/messages/MsgDelegate.ts +0 -0
- package/lib/components/TxDialog/messages/Redelegate.vue +189 -0
- package/lib/components/TxDialog/messages/Send.vue +142 -0
- package/lib/components/TxDialog/messages/Transfer.vue +248 -0
- package/lib/components/TxDialog/messages/Unbond.vue +137 -0
- package/lib/components/TxDialog/messages/Vote.vue +63 -0
- package/lib/components/TxDialog/messages/Withdraw.vue +56 -0
- package/lib/components/TxDialog/messages/WithdrawCommission.vue +75 -0
- package/lib/components/TxDialog/wasm/ClearAdmin.vue +56 -0
- package/lib/components/TxDialog/wasm/ExecuteContract.vue +99 -0
- package/lib/components/TxDialog/wasm/InstantiateContract.vue +109 -0
- package/lib/components/TxDialog/wasm/MigrateContract.vue +77 -0
- package/lib/components/TxDialog/wasm/MigrateContract2.vue +77 -0
- package/lib/components/TxDialog/wasm/StoreCode.vue +101 -0
- package/lib/components/TxDialog/wasm/UpdateAdmin.vue +75 -0
- package/lib/main.css +7 -0
- package/lib/main.ts +23 -0
- package/lib/utils/TokenUnitConverter.ts +45 -0
- package/lib/utils/format.ts +16 -0
- package/lib/utils/http.ts +223 -0
- package/lib/utils/type.ts +77 -0
- package/lib/wallet/EthermintMessageAdapter.ts +136 -0
- package/lib/wallet/UniClient.ts +152 -0
- package/lib/wallet/Wallet.ts +138 -0
- package/lib/wallet/wallets/InitiaWallet.ts +189 -0
- package/lib/wallet/wallets/KeplerWallet.ts +158 -0
- package/lib/wallet/wallets/LeapWallet.ts +142 -0
- package/lib/wallet/wallets/LedgerWallet.ts +203 -0
- package/lib/wallet/wallets/MetamaskSnapWallet.ts +105 -0
- package/lib/wallet/wallets/MetamaskWallet.ts +173 -0
- package/lib/wallet/wallets/OKXWallet.ts +202 -0
- package/lib/wallet/wallets/UnisatWallet.ts +198 -0
- package/package.json +102 -0
- package/postcss.config.js +6 -0
- package/src/App.vue +241 -0
- package/src/main.ts +4 -0
- package/src/vite-env.d.ts +1 -0
- package/tailwind.config.js +34 -0
- package/tsconfig.json +25 -0
- package/tsconfig.node.json +10 -0
- package/vite.config.ts +62 -0
- package/vue-shim.d.ts +6 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export interface TokenConfig {
|
|
2
|
+
denom: string;
|
|
3
|
+
symbol: string;
|
|
4
|
+
ibcDenom: string;
|
|
5
|
+
decimals: number;
|
|
6
|
+
coinImageUrl: string;
|
|
7
|
+
sourceChannelId?: string;
|
|
8
|
+
destChannelId?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const tokens : TokenConfig[]= [
|
|
12
|
+
{
|
|
13
|
+
denom: "uosmo",
|
|
14
|
+
symbol: "OSMO",
|
|
15
|
+
ibcDenom: "uosmo", // use base denom for native token
|
|
16
|
+
decimals: 6,
|
|
17
|
+
coinImageUrl: "https://app.osmosis.zone/tokens/osmo.svg",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
denom: "uatom",
|
|
21
|
+
symbol: "ATOM",
|
|
22
|
+
ibcDenom: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
|
|
23
|
+
sourceChannelId: "channel-0",
|
|
24
|
+
destChannelId: "channel-141",
|
|
25
|
+
decimals: 6,
|
|
26
|
+
coinImageUrl: "https://app.osmosis.zone/tokens/atom.svg",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
denom: "uusdc",
|
|
30
|
+
symbol: "USDC",
|
|
31
|
+
ibcDenom: "ibc/D189335C6E4A68B513C10AB227BF1C1D38C746766278BA3EEB4FB14124F1D858",
|
|
32
|
+
sourceChannelId: "channel-208",
|
|
33
|
+
destChannelId: "",
|
|
34
|
+
decimals: 6,
|
|
35
|
+
coinImageUrl: "https://app.osmosis.zone/tokens/usdc.svg",
|
|
36
|
+
},
|
|
37
|
+
]
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, computed } from 'vue';
|
|
3
|
+
import {
|
|
4
|
+
getAccount,
|
|
5
|
+
getBalance,
|
|
6
|
+
getBalanceMetadata,
|
|
7
|
+
getIBCDenomMetadata,
|
|
8
|
+
getLatestBlock,
|
|
9
|
+
getStakingParam,
|
|
10
|
+
getTxByHash,
|
|
11
|
+
} from '../../utils/http';
|
|
12
|
+
import { BroadcastMode, Coin, CoinMetadata } from '../../utils/type';
|
|
13
|
+
import { WalletName, readWallet } from '../../../lib/wallet/Wallet';
|
|
14
|
+
import { UniClient } from '../../../lib/wallet/UniClient';
|
|
15
|
+
|
|
16
|
+
// cosmos sdk messages
|
|
17
|
+
import Delegate from './messages/Delegate.vue';
|
|
18
|
+
import Deposit from './messages/Deposit.vue';
|
|
19
|
+
import Redelegate from './messages/Redelegate.vue';
|
|
20
|
+
import Send from './messages/Send.vue';
|
|
21
|
+
import Transfer from './messages/Transfer.vue';
|
|
22
|
+
import Unbond from './messages/Unbond.vue';
|
|
23
|
+
import Vote from './messages/Vote.vue';
|
|
24
|
+
import Withdraw from './messages/Withdraw.vue';
|
|
25
|
+
import WithdrawCommission from './messages/WithdrawCommission.vue';
|
|
26
|
+
|
|
27
|
+
// wasm msgs
|
|
28
|
+
import StoreCode from './wasm/StoreCode.vue';
|
|
29
|
+
import ExecuteContract from './wasm/ExecuteContract.vue';
|
|
30
|
+
import InstantiateContract from './wasm/InstantiateContract.vue';
|
|
31
|
+
import ChainRegistryClient from '@ping-pub/chain-registry-client';
|
|
32
|
+
import MigrateContract from './wasm/MigrateContract.vue';
|
|
33
|
+
import UpdateAdmin from './wasm/UpdateAdmin.vue';
|
|
34
|
+
import ClearAdmin from './wasm/ClearAdmin.vue';
|
|
35
|
+
|
|
36
|
+
const props = defineProps({
|
|
37
|
+
type: String,
|
|
38
|
+
endpoint: { type: String, required: true },
|
|
39
|
+
sender: { type: String, required: true },
|
|
40
|
+
hdPath: String,
|
|
41
|
+
registryName: String,
|
|
42
|
+
params: String,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const msgType = computed(() => {
|
|
46
|
+
switch (props.type?.toLowerCase()) {
|
|
47
|
+
case 'send':
|
|
48
|
+
return Send;
|
|
49
|
+
case 'delegate':
|
|
50
|
+
return Delegate;
|
|
51
|
+
case 'withdraw':
|
|
52
|
+
return Withdraw;
|
|
53
|
+
case 'withdraw_commission':
|
|
54
|
+
return WithdrawCommission;
|
|
55
|
+
case 'redelegate':
|
|
56
|
+
return Redelegate;
|
|
57
|
+
case 'transfer':
|
|
58
|
+
return Transfer;
|
|
59
|
+
case 'unbond':
|
|
60
|
+
return Unbond;
|
|
61
|
+
case 'vote':
|
|
62
|
+
return Vote;
|
|
63
|
+
case 'deposit':
|
|
64
|
+
return Deposit;
|
|
65
|
+
case 'wasm_store_code':
|
|
66
|
+
return StoreCode;
|
|
67
|
+
case 'wasm_execute_contract':
|
|
68
|
+
return ExecuteContract;
|
|
69
|
+
case 'wasm_instantiate_contract':
|
|
70
|
+
return InstantiateContract;
|
|
71
|
+
case 'wasm_migrate_contract':
|
|
72
|
+
return MigrateContract;
|
|
73
|
+
case 'wasm_update_admin':
|
|
74
|
+
return UpdateAdmin;
|
|
75
|
+
case 'wasm_clear_admin':
|
|
76
|
+
return ClearAdmin;
|
|
77
|
+
default:
|
|
78
|
+
return Send;
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const advance = ref(false);
|
|
83
|
+
const sending = ref(false);
|
|
84
|
+
const balance = ref([] as Coin[]);
|
|
85
|
+
const metadatas = ref({} as Record<string, CoinMetadata>);
|
|
86
|
+
const emit = defineEmits(['submited', 'confirmed', 'view']);
|
|
87
|
+
|
|
88
|
+
// functional variable
|
|
89
|
+
const p = ref({} as { fees: Coin });
|
|
90
|
+
const view = ref('input'); // input, submitting
|
|
91
|
+
const open = ref(false);
|
|
92
|
+
const error = ref('');
|
|
93
|
+
|
|
94
|
+
// input field
|
|
95
|
+
const msgBox = ref({
|
|
96
|
+
msgs: [],
|
|
97
|
+
isValid: { ok: false, error: '' },
|
|
98
|
+
initial: function () { },
|
|
99
|
+
});
|
|
100
|
+
const feeAmount = ref(2000);
|
|
101
|
+
const feeDenom = ref('');
|
|
102
|
+
const gasInfo = ref(200000);
|
|
103
|
+
const memo = ref('');
|
|
104
|
+
const chainId = ref('cosmoshub-4');
|
|
105
|
+
// const chainId = ref('taproot-1');
|
|
106
|
+
const broadcast = ref(BroadcastMode.SYNC);
|
|
107
|
+
|
|
108
|
+
async function initData() {
|
|
109
|
+
if (open.value && props.endpoint && props.sender) {
|
|
110
|
+
metadatas.value = {}
|
|
111
|
+
view.value = 'input';
|
|
112
|
+
p.value = JSON.parse(props.params || '{}')
|
|
113
|
+
memo.value = props.type?.toLowerCase() === 'send' ? '' : 'mictonode.com'
|
|
114
|
+
|
|
115
|
+
feeAmount.value = Number(p.value?.fees?.amount || 2000)
|
|
116
|
+
feeDenom.value = balance.value[0]?.denom;
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
getBalance(props.endpoint, props.sender).then((x) => {
|
|
120
|
+
balance.value = x.balances;
|
|
121
|
+
x.balances?.forEach((coin) => {
|
|
122
|
+
// only load for native tokens
|
|
123
|
+
if (coin.denom.length < 12)
|
|
124
|
+
getBalanceMetadata(props.endpoint, coin.denom).then(
|
|
125
|
+
(meta) => {
|
|
126
|
+
if(meta.metadata) metadatas.value[coin.denom] = meta.metadata;
|
|
127
|
+
}
|
|
128
|
+
).catch(()=>{});
|
|
129
|
+
if(coin.denom.startsWith('ibc/')) {
|
|
130
|
+
getIBCDenomMetadata(coin.denom).then(
|
|
131
|
+
(meta) => {
|
|
132
|
+
if(meta) metadatas.value[coin.denom] = meta;
|
|
133
|
+
}
|
|
134
|
+
).catch(()=>{});
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// load metadata from registry
|
|
140
|
+
if (props.registryName && Object.keys(metadatas.value).length === 0) {
|
|
141
|
+
const client = new ChainRegistryClient()
|
|
142
|
+
client.fetchAssetsList(props.registryName).then(x => {
|
|
143
|
+
x.assets.forEach(a => {
|
|
144
|
+
metadatas.value[a.base] = a as CoinMetadata
|
|
145
|
+
})
|
|
146
|
+
}).catch(() => {
|
|
147
|
+
console.log(`Chain: ${props.registryName } was not found on Cosmos Registry`)
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
getLatestBlock(props.endpoint).then((x) => {
|
|
151
|
+
chainId.value = x.block.header.chain_id;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Every sub component should have a initial function
|
|
155
|
+
if (msgBox.value && msgBox.value.initial) msgBox.value.initial();
|
|
156
|
+
|
|
157
|
+
// load fee denom
|
|
158
|
+
getStakingParam(props.endpoint).then((res) => {
|
|
159
|
+
feeDenom.value = res?.params?.bond_denom;
|
|
160
|
+
})
|
|
161
|
+
} catch (err) {
|
|
162
|
+
error.value = String(err);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// account.value = await getAccount(props.endpoint, props.sender).then(x => x.account);
|
|
166
|
+
sending.value = false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function sendTx() {
|
|
170
|
+
try {
|
|
171
|
+
if (!props.sender) throw new Error('Sender should not be empty!');
|
|
172
|
+
if (!props.endpoint) throw new Error('Endpoint is empty');
|
|
173
|
+
if (!feeDenom.value) throw new Error('Fee Denom is empty');
|
|
174
|
+
if (!msgBox.value.isValid.ok)
|
|
175
|
+
throw new Error(msgBox.value.isValid.error);
|
|
176
|
+
|
|
177
|
+
sending.value = true; // disable sending btn
|
|
178
|
+
|
|
179
|
+
const acc = await getAccount(props.endpoint, props.sender);
|
|
180
|
+
const messages = msgBox.value.msgs;
|
|
181
|
+
|
|
182
|
+
const tx = {
|
|
183
|
+
chainId: chainId.value,
|
|
184
|
+
signerAddress: props.sender,
|
|
185
|
+
messages,
|
|
186
|
+
fee: {
|
|
187
|
+
gas: "400000",
|
|
188
|
+
amount: [
|
|
189
|
+
{ amount: String(feeAmount.value), denom: feeDenom.value },
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
memo: memo.value,
|
|
193
|
+
signerData: {
|
|
194
|
+
accountNumber: Number(acc.account.account_number),
|
|
195
|
+
sequence: Number(acc.account.sequence),
|
|
196
|
+
chainId: chainId.value,
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
// console.log('tx:', tx);
|
|
200
|
+
const current = readWallet(props.hdPath);
|
|
201
|
+
const wallet = current ? current.wallet : WalletName.Keplr;
|
|
202
|
+
const client = new UniClient(wallet, {
|
|
203
|
+
chainId: chainId.value,
|
|
204
|
+
hdPath: current.hdPath,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if(!advance.value) {
|
|
208
|
+
await client.simulate(props.endpoint, tx, broadcast.value).then(gas => {
|
|
209
|
+
// update tx gas
|
|
210
|
+
tx.fee.gas = (gas * 1.25).toFixed()
|
|
211
|
+
}).catch(() => {
|
|
212
|
+
// sending.value = false;
|
|
213
|
+
// error.value = "Failed to simulate tx gas: " + err;
|
|
214
|
+
advance.value = true;
|
|
215
|
+
})
|
|
216
|
+
} else {
|
|
217
|
+
tx.fee.gas = gasInfo.value.toString()
|
|
218
|
+
}
|
|
219
|
+
//==============================================================================
|
|
220
|
+
if (wallet === WalletName.Initia) {
|
|
221
|
+
const response = await client.sign(tx);
|
|
222
|
+
hash.value = response.txhash
|
|
223
|
+
showResult(response.txhash);
|
|
224
|
+
|
|
225
|
+
emit('submited', {
|
|
226
|
+
hash: response.txhash,
|
|
227
|
+
eventType: props.type,
|
|
228
|
+
});
|
|
229
|
+
} else {
|
|
230
|
+
const txRaw = await client.sign(tx);
|
|
231
|
+
const response = await client.broadcastTx(props.endpoint, txRaw, broadcast.value);
|
|
232
|
+
hash.value = response.tx_response.txhash
|
|
233
|
+
showResult(response.tx_response.txhash);
|
|
234
|
+
|
|
235
|
+
emit('submited', {
|
|
236
|
+
hash: response.tx_response.txhash,
|
|
237
|
+
eventType: props.type,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
//==============================================================================
|
|
241
|
+
} catch (e) {
|
|
242
|
+
sending.value = false;
|
|
243
|
+
error.value = String(e);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function viewTransaction() {
|
|
251
|
+
emit('view', {
|
|
252
|
+
hash: hash.value,
|
|
253
|
+
eventType: props.type,
|
|
254
|
+
});
|
|
255
|
+
open.value = false
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function showTitle() {
|
|
259
|
+
return (props.type || 'Sending Transaction').replace(/\_/g, ' ');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const delay = ref(0);
|
|
263
|
+
const step = ref(0);
|
|
264
|
+
const msg = ref('');
|
|
265
|
+
const sleep = 6000;
|
|
266
|
+
const hash = ref('')
|
|
267
|
+
|
|
268
|
+
function showResult(hash: string) {
|
|
269
|
+
view.value = 'submitting';
|
|
270
|
+
delay.value = 1;
|
|
271
|
+
step.value = 20;
|
|
272
|
+
error.value = '';
|
|
273
|
+
msg.value = 'Proccessing...';
|
|
274
|
+
setTimeout(() => {
|
|
275
|
+
fetchTx(hash);
|
|
276
|
+
}, sleep);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function fetchTx(tx: string) {
|
|
280
|
+
delay.value += 1;
|
|
281
|
+
step.value += 20;
|
|
282
|
+
getTxByHash(props.endpoint, tx)
|
|
283
|
+
.then((res) => {
|
|
284
|
+
step.value = 100;
|
|
285
|
+
if (res.tx_response.code > 0) {
|
|
286
|
+
error.value = res.tx_response.raw_log;
|
|
287
|
+
} else {
|
|
288
|
+
msg.value = `Congratulations! ${showTitle()} completed successfully.`;
|
|
289
|
+
emit('confirmed', { hash: tx, eventType: props.type });
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
.catch(() => {
|
|
293
|
+
if (delay.value < 5) {
|
|
294
|
+
setTimeout(() => fetchTx(tx), sleep);
|
|
295
|
+
} else {
|
|
296
|
+
error.value = 'Timeout';
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
</script>
|
|
301
|
+
<template>
|
|
302
|
+
<div class="text-gray-600 rounded-lg">
|
|
303
|
+
<!-- Put this part before </body> tag -->
|
|
304
|
+
<input v-model="open" type="checkbox" :id="type" class="modal-toggle" @change="initData()" />
|
|
305
|
+
<label :for="type" class="modal cursor-pointer">
|
|
306
|
+
<label class="modal-box relative p-5" :class="{ '!w-11/12 !max-w-5xl': String(type).startsWith('wasm') }" for="">
|
|
307
|
+
<label :for="type" class="btn btn-sm btn-circle absolute right-4 top-4">✕</label>
|
|
308
|
+
<h3 class="text-lg font-bold capitalize dark:text-gray-300">
|
|
309
|
+
{{ showTitle() }}
|
|
310
|
+
</h3>
|
|
311
|
+
|
|
312
|
+
<div v-if="!sender" class="text-center h-16 items-center rounded-md">
|
|
313
|
+
No wallet connected!
|
|
314
|
+
</div>
|
|
315
|
+
|
|
316
|
+
<div v-if="sender">
|
|
317
|
+
<div v-if="view === 'input'">
|
|
318
|
+
<component :is="msgType" ref="msgBox" :endpoint="endpoint" :sender="sender" :balances="balance"
|
|
319
|
+
:metadata="metadatas" :params="props.params" />
|
|
320
|
+
<form class="space-y-6" action="#" method="POST">
|
|
321
|
+
<div :class="advance ? '' : 'hidden'">
|
|
322
|
+
<div class="form-control">
|
|
323
|
+
<label class="label">
|
|
324
|
+
<span class="label-text">Fees</span>
|
|
325
|
+
</label>
|
|
326
|
+
<label class="input-group flex items-center">
|
|
327
|
+
<input v-model="feeAmount" type="text" placeholder="0.001"
|
|
328
|
+
class="input border border-gray-300 dark:border-gray-600 flex-1 w-0 dark:text-gray-300" />
|
|
329
|
+
<select v-model="feeDenom"
|
|
330
|
+
class="select input border border-gray-300 dark:border-gray-600 w-[200px]">
|
|
331
|
+
<option disabled selected>
|
|
332
|
+
Select Fee Token
|
|
333
|
+
</option>
|
|
334
|
+
<option v-for="t in balance">
|
|
335
|
+
{{ t.denom }}
|
|
336
|
+
</option>
|
|
337
|
+
</select>
|
|
338
|
+
</label>
|
|
339
|
+
</div>
|
|
340
|
+
<div class="form-control">
|
|
341
|
+
<label class="label">
|
|
342
|
+
<span class="label-text">Gas</span>
|
|
343
|
+
</label>
|
|
344
|
+
<input v-model="gasInfo" type="number" placeholder="2000000"
|
|
345
|
+
class="input border border-gray-300 dark:border-gray-600 dark:text-gray-300" />
|
|
346
|
+
</div>
|
|
347
|
+
<div class="form-control">
|
|
348
|
+
<label class="label">
|
|
349
|
+
<span class="label-text">Memo</span>
|
|
350
|
+
</label>
|
|
351
|
+
<input v-model="memo" type="text" placeholder="Memo"
|
|
352
|
+
class="input border border-gray-300 dark:border-gray-600 dark:text-gray-300" />
|
|
353
|
+
</div>
|
|
354
|
+
<div class="form-control">
|
|
355
|
+
<label class="label">
|
|
356
|
+
<span class="label-text">Broadcast Mode</span>
|
|
357
|
+
</label>
|
|
358
|
+
<select v-model="broadcast"
|
|
359
|
+
class="select input border border-gray-300 dark:border-gray-600 w-[200px]">
|
|
360
|
+
<option :value="BroadcastMode.SYNC">Sync</option>
|
|
361
|
+
<option :value="BroadcastMode.ASYNC">Async</option>
|
|
362
|
+
<option :value="BroadcastMode.BLOCK">Block</option>
|
|
363
|
+
</select>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
</form>
|
|
367
|
+
|
|
368
|
+
<div v-show="error" class="mt-5 alert alert-error shadow-lg" @click="error = ''">
|
|
369
|
+
<div class="flex">
|
|
370
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current flex-shrink-0 h-6 w-6"
|
|
371
|
+
fill="none" viewBox="0 0 24 24">
|
|
372
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
373
|
+
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
374
|
+
</svg>
|
|
375
|
+
<span>{{ error }}.</span>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
<div class="modal-action flex justify-between items-center">
|
|
380
|
+
<div class="flex items-center cursor-pointer">
|
|
381
|
+
<input v-model="advance" type="checkbox" :id="`${type}-advance`"
|
|
382
|
+
class="checkbox checkbox-sm checkbox-primary mr-2" /><label :for="`${type}-advance`"
|
|
383
|
+
class="cursor-pointer dark:text-gray-400">Advance</label>
|
|
384
|
+
</div>
|
|
385
|
+
<button class="btn glass rounded-lg" @click="sendTx()" :disabled="sending">
|
|
386
|
+
<span v-if="sending" class="loading loading-spinner"></span>
|
|
387
|
+
Send
|
|
388
|
+
</button>
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
|
|
392
|
+
<div v-if="view === 'submitting'">
|
|
393
|
+
<div class="my-10">
|
|
394
|
+
<div v-if="error" class="my-5 text-center text-red-500 rounded-md">
|
|
395
|
+
{{ error }}
|
|
396
|
+
</div>
|
|
397
|
+
<div v-else class="my-5 text-center text-lg text-green-500 rounded-md">
|
|
398
|
+
{{ msg }}
|
|
399
|
+
</div>
|
|
400
|
+
<div class="overflow-hidden h-5 mb-2 text-xs flex rounded bg-green-100">
|
|
401
|
+
<div :style="`width: ${step}%`"
|
|
402
|
+
class="shadow-none flex flex-col text-center whitespace-nowrap text-white justify-center bg-green-400">
|
|
403
|
+
</div>
|
|
404
|
+
</div>
|
|
405
|
+
<div class="flex items-center justify-between">
|
|
406
|
+
<div>
|
|
407
|
+
<span
|
|
408
|
+
class="text-xs font-semibold inline-block py-1 px-2 rounded text-gray-600 dark:text-white">
|
|
409
|
+
Submitted
|
|
410
|
+
</span>
|
|
411
|
+
</div>
|
|
412
|
+
<div class="text-right">
|
|
413
|
+
<span class="text-xs font-semibold inline-block text-gray-600 dark:text-white">
|
|
414
|
+
{{ step }}%
|
|
415
|
+
</span>
|
|
416
|
+
</div>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
<label class="mt-10 flex justify-center text-sm" @click="viewTransaction">
|
|
420
|
+
<span>View Transaction</span>
|
|
421
|
+
</label>
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
</label>
|
|
425
|
+
</label>
|
|
426
|
+
</div>
|
|
427
|
+
</template>
|
|
428
|
+
<script lang="ts">
|
|
429
|
+
export default {
|
|
430
|
+
name: 'TxDialog',
|
|
431
|
+
};
|
|
432
|
+
</script>
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { ComputedRef, PropType, computed, onMounted, ref } from 'vue';
|
|
3
|
+
import {
|
|
4
|
+
getActiveValidators,
|
|
5
|
+
getInactiveValidators,
|
|
6
|
+
getStakingParam,
|
|
7
|
+
} from '../../../utils/http';
|
|
8
|
+
import { decimal2percent } from '../../../utils/format';
|
|
9
|
+
import { Coin, CoinMetadata } from '../../../utils/type';
|
|
10
|
+
import { TokenUnitConverter } from '../../../utils/TokenUnitConverter';
|
|
11
|
+
import { MsgDelegate } from "@initia/initia.proto/initia/mstaking/v1/tx"
|
|
12
|
+
|
|
13
|
+
const props = defineProps({
|
|
14
|
+
endpoint: { type: String, required: true },
|
|
15
|
+
sender: { type: String, required: true },
|
|
16
|
+
balances: Object as PropType<Coin[]>,
|
|
17
|
+
metadata: Object as PropType<Record<string, CoinMetadata>>,
|
|
18
|
+
params: String,
|
|
19
|
+
});
|
|
20
|
+
const params = computed(() => JSON.parse(props.params || '{}'));
|
|
21
|
+
|
|
22
|
+
const validator = ref('');
|
|
23
|
+
|
|
24
|
+
const activeValidators = ref([]);
|
|
25
|
+
const inactiveValidators = ref([]);
|
|
26
|
+
const stakingDenom = ref('');
|
|
27
|
+
const unbondingTime = ref('');
|
|
28
|
+
const amount = ref('');
|
|
29
|
+
const amountDenom = ref('');
|
|
30
|
+
|
|
31
|
+
function isInitiaChain(endpoint: string): boolean {
|
|
32
|
+
return props.endpoint.indexOf("init") !== -1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const msgs = computed(() => {
|
|
36
|
+
const convert = new TokenUnitConverter(props.metadata);
|
|
37
|
+
const baseAmount = convert.displayToBase(stakingDenom.value, {
|
|
38
|
+
amount: String(amount.value),
|
|
39
|
+
denom: amountDenom.value,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (isInitiaChain(props.endpoint)) {
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
typeUrl: '/initia.mstaking.v1.MsgDelegate',
|
|
46
|
+
value: MsgDelegate.fromPartial({
|
|
47
|
+
delegatorAddress: props.sender,
|
|
48
|
+
validatorAddress: validator.value,
|
|
49
|
+
amount: [baseAmount],
|
|
50
|
+
}),
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
} else {
|
|
54
|
+
return [
|
|
55
|
+
{
|
|
56
|
+
typeUrl: '/cosmos.staking.v1beta1.MsgDelegate',
|
|
57
|
+
value: {
|
|
58
|
+
delegatorAddress: props.sender,
|
|
59
|
+
validatorAddress: validator.value,
|
|
60
|
+
amount: baseAmount,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const list: ComputedRef<
|
|
68
|
+
{
|
|
69
|
+
operator_address: string;
|
|
70
|
+
description: { moniker: string };
|
|
71
|
+
commission: { commission_rates: { rate: string } };
|
|
72
|
+
status: string;
|
|
73
|
+
}[]
|
|
74
|
+
> = computed(() => {
|
|
75
|
+
return [...activeValidators.value, ...inactiveValidators.value];
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const available = computed(() => {
|
|
79
|
+
const convert = new TokenUnitConverter(props.metadata);
|
|
80
|
+
const base = props.balances?.find(
|
|
81
|
+
(x) => x.denom === stakingDenom.value
|
|
82
|
+
) || { amount: '0', denom: stakingDenom.value };
|
|
83
|
+
return {
|
|
84
|
+
base,
|
|
85
|
+
display: convert.baseToUnit(base, amountDenom.value),
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
function loadInactiveValidators() {
|
|
90
|
+
getInactiveValidators(props.endpoint).then((x) => {
|
|
91
|
+
inactiveValidators.value = x.validators;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const units = computed(() => {
|
|
96
|
+
if (!props.metadata || !props.metadata[stakingDenom.value]) {
|
|
97
|
+
amountDenom.value = stakingDenom.value;
|
|
98
|
+
return [{ denom: stakingDenom.value, exponent: 0, aliases: [] }];
|
|
99
|
+
}
|
|
100
|
+
const list = props.metadata[stakingDenom.value].denom_units.sort(
|
|
101
|
+
(a, b) => b.exponent - a.exponent
|
|
102
|
+
);
|
|
103
|
+
if (list.length > 0) amountDenom.value = list[0].denom;
|
|
104
|
+
return list;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const isValid = computed(() => {
|
|
108
|
+
let ok = true;
|
|
109
|
+
let error = '';
|
|
110
|
+
if (!validator.value) {
|
|
111
|
+
ok = false;
|
|
112
|
+
error = 'Validator is empty';
|
|
113
|
+
}
|
|
114
|
+
if (!(Number(amount.value) > 0)) {
|
|
115
|
+
ok = false;
|
|
116
|
+
error = 'Amount should be great than 0';
|
|
117
|
+
}
|
|
118
|
+
if (!amountDenom.value) {
|
|
119
|
+
ok = false;
|
|
120
|
+
error = 'Amount Denom is empty';
|
|
121
|
+
}
|
|
122
|
+
return { ok, error };
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
function initial() {
|
|
126
|
+
activeValidators.value = [];
|
|
127
|
+
validator.value = params.value.validator_address;
|
|
128
|
+
getStakingParam(props.endpoint).then((x) => {
|
|
129
|
+
stakingDenom.value = x.params.bond_denom || '';
|
|
130
|
+
unbondingTime.value = x.params.unbonding_time;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
getActiveValidators(props.endpoint).then((x) => {
|
|
134
|
+
activeValidators.value = x.validators;
|
|
135
|
+
if (!params.value.validator_address) {
|
|
136
|
+
validator.value = x.validators.find(
|
|
137
|
+
(v) => v.description.identity === '6783E9F948541962'
|
|
138
|
+
)?.operator_address;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
defineExpose({ msgs, isValid, initial });
|
|
144
|
+
</script>
|
|
145
|
+
<template>
|
|
146
|
+
<div>
|
|
147
|
+
<div class="form-control">
|
|
148
|
+
<label class="label">
|
|
149
|
+
<span class="label-text">Sender</span>
|
|
150
|
+
</label>
|
|
151
|
+
<input
|
|
152
|
+
:value="sender"
|
|
153
|
+
type="text"
|
|
154
|
+
class="text-gray-600 dark:text-white input border !border-gray-300 dark:!border-gray-600"
|
|
155
|
+
/>
|
|
156
|
+
</div>
|
|
157
|
+
<div class="form-control">
|
|
158
|
+
<label class="label">
|
|
159
|
+
<span class="label-text">Validator</span>
|
|
160
|
+
<a class="label-text" @click="loadInactiveValidators()"
|
|
161
|
+
>Show Inactive</a
|
|
162
|
+
>
|
|
163
|
+
</label>
|
|
164
|
+
<select v-model="validator" class="select select-bordered dark:text-white">
|
|
165
|
+
<option value="">Select a validator</option>
|
|
166
|
+
<option v-for="v in list" :value="v.operator_address">
|
|
167
|
+
{{ v.description.moniker }} ({{
|
|
168
|
+
decimal2percent(v.commission.commission_rates.rate)
|
|
169
|
+
}}%)
|
|
170
|
+
<span v-if="v.status !== 'BOND_STATUS_BONDED'">x</span>
|
|
171
|
+
</option>
|
|
172
|
+
</select>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="form-control">
|
|
175
|
+
<label class="label">
|
|
176
|
+
<span class="label-text">Amount</span>
|
|
177
|
+
<span>
|
|
178
|
+
{{ available?.display.amount }} {{ available?.display.denom }}
|
|
179
|
+
</span>
|
|
180
|
+
</label>
|
|
181
|
+
<label class="join">
|
|
182
|
+
<input
|
|
183
|
+
v-model="amount"
|
|
184
|
+
type="number"
|
|
185
|
+
:placeholder="`Available: ${available?.display.amount}`"
|
|
186
|
+
class="input border border-gray-300 dark:border-gray-600 w-full join-item dark:text-white"
|
|
187
|
+
/>
|
|
188
|
+
<select v-model="amountDenom" class="select select-bordered join-item dark:text-white">
|
|
189
|
+
<option v-for="u in units">{{ u.denom }}</option>
|
|
190
|
+
</select>
|
|
191
|
+
</label>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</template>
|