@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.
Files changed (70) hide show
  1. package/.github/FUNDING.yml +3 -0
  2. package/.github/workflows/npm-publish.yml +34 -0
  3. package/.prettierrc.json +9 -0
  4. package/.vscode/extensions.json +3 -0
  5. package/README.md +41 -0
  6. package/dist/main-172a6585.js +404361 -0
  7. package/dist/ping-widget.js +13 -0
  8. package/dist/ping-widget.umd.cjs +96 -0
  9. package/dist/query.lcd-2adf1c0c.js +129 -0
  10. package/dist/query.rpc.Query-b53908e7.js +2316 -0
  11. package/dist/tx.rpc.msg-d234ff40.js +37 -0
  12. package/dist/tx.rpc.msg-f7a80a78.js +21 -0
  13. package/example/.vscode/extensions.json +3 -0
  14. package/example/README.md +7 -0
  15. package/example/index.html +12 -0
  16. package/example/package.json +19 -0
  17. package/example/pnpm-lock.yaml +465 -0
  18. package/example/public/cdn.html +19 -0
  19. package/example/src/App.vue +73 -0
  20. package/example/src/main.js +4 -0
  21. package/example/vite.config.js +7 -0
  22. package/index.html +12 -0
  23. package/lib/components/ConnectWallet/index.vue +267 -0
  24. package/lib/components/TokenConvert/index.vue +1033 -0
  25. package/lib/components/TokenConvert/tokens.ts +37 -0
  26. package/lib/components/TxDialog/index.vue +432 -0
  27. package/lib/components/TxDialog/messages/Delegate.vue +194 -0
  28. package/lib/components/TxDialog/messages/Deposit.vue +97 -0
  29. package/lib/components/TxDialog/messages/MsgDelegate.ts +0 -0
  30. package/lib/components/TxDialog/messages/Redelegate.vue +189 -0
  31. package/lib/components/TxDialog/messages/Send.vue +142 -0
  32. package/lib/components/TxDialog/messages/Transfer.vue +248 -0
  33. package/lib/components/TxDialog/messages/Unbond.vue +137 -0
  34. package/lib/components/TxDialog/messages/Vote.vue +63 -0
  35. package/lib/components/TxDialog/messages/Withdraw.vue +56 -0
  36. package/lib/components/TxDialog/messages/WithdrawCommission.vue +75 -0
  37. package/lib/components/TxDialog/wasm/ClearAdmin.vue +56 -0
  38. package/lib/components/TxDialog/wasm/ExecuteContract.vue +99 -0
  39. package/lib/components/TxDialog/wasm/InstantiateContract.vue +109 -0
  40. package/lib/components/TxDialog/wasm/MigrateContract.vue +77 -0
  41. package/lib/components/TxDialog/wasm/MigrateContract2.vue +77 -0
  42. package/lib/components/TxDialog/wasm/StoreCode.vue +101 -0
  43. package/lib/components/TxDialog/wasm/UpdateAdmin.vue +75 -0
  44. package/lib/main.css +7 -0
  45. package/lib/main.ts +23 -0
  46. package/lib/utils/TokenUnitConverter.ts +45 -0
  47. package/lib/utils/format.ts +16 -0
  48. package/lib/utils/http.ts +223 -0
  49. package/lib/utils/type.ts +77 -0
  50. package/lib/wallet/EthermintMessageAdapter.ts +136 -0
  51. package/lib/wallet/UniClient.ts +152 -0
  52. package/lib/wallet/Wallet.ts +138 -0
  53. package/lib/wallet/wallets/InitiaWallet.ts +189 -0
  54. package/lib/wallet/wallets/KeplerWallet.ts +158 -0
  55. package/lib/wallet/wallets/LeapWallet.ts +142 -0
  56. package/lib/wallet/wallets/LedgerWallet.ts +203 -0
  57. package/lib/wallet/wallets/MetamaskSnapWallet.ts +105 -0
  58. package/lib/wallet/wallets/MetamaskWallet.ts +173 -0
  59. package/lib/wallet/wallets/OKXWallet.ts +202 -0
  60. package/lib/wallet/wallets/UnisatWallet.ts +198 -0
  61. package/package.json +102 -0
  62. package/postcss.config.js +6 -0
  63. package/src/App.vue +241 -0
  64. package/src/main.ts +4 -0
  65. package/src/vite-env.d.ts +1 -0
  66. package/tailwind.config.js +34 -0
  67. package/tsconfig.json +25 -0
  68. package/tsconfig.node.json +10 -0
  69. package/vite.config.ts +62 -0
  70. 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>