diff --git a/src/components/pages/network-view/network-view.html b/src/components/pages/network-view/network-view.html
index 1ccf77e..4cae8ec 100644
--- a/src/components/pages/network-view/network-view.html
+++ b/src/components/pages/network-view/network-view.html
@@ -17,7 +17,7 @@
size="small"
type="link"
>
- Mint or Redeem tBTC
+ Mint/Redeem tBTC
KEEP Dashboard
diff --git a/src/lib/abis/index.ts b/src/lib/abis/index.ts
new file mode 100644
index 0000000..2278811
--- /dev/null
+++ b/src/lib/abis/index.ts
@@ -0,0 +1 @@
+export * from './keep-random-beacon-operator.abi';
diff --git a/src/lib/abis/keep-random-beacon-operator.abi.ts b/src/lib/abis/keep-random-beacon-operator.abi.ts
new file mode 100644
index 0000000..9c3b283
--- /dev/null
+++ b/src/lib/abis/keep-random-beacon-operator.abi.ts
@@ -0,0 +1,903 @@
+export const KeepRandomBeaconOperatorABI = [
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: '_serviceContract',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_tokenStaking',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_keepRegistry',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: '_gasPriceOracle',
+ type: 'address',
+ },
+ ],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'constructor',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'memberIndex',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes',
+ name: 'groupPubKey',
+ type: 'bytes',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes',
+ name: 'misbehaved',
+ type: 'bytes',
+ },
+ ],
+ name: 'DkgResultSubmittedEvent',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'beneficiary',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'address',
+ name: 'operator',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'groupIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'GroupMemberRewardsWithdrawn',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'newEntry',
+ type: 'uint256',
+ },
+ ],
+ name: 'GroupSelectionStarted',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'bytes',
+ name: 'groupPubKey',
+ type: 'bytes',
+ },
+ ],
+ name: 'OnGroupRegistered',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: false,
+ internalType: 'bytes',
+ name: 'previousEntry',
+ type: 'bytes',
+ },
+ {
+ indexed: false,
+ internalType: 'bytes',
+ name: 'groupPublicKey',
+ type: 'bytes',
+ },
+ ],
+ name: 'RelayEntryRequested',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [],
+ name: 'RelayEntrySubmitted',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'groupIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'RelayEntryTimeoutReported',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'uint256',
+ name: 'groupIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'UnauthorizedSigningReported',
+ type: 'event',
+ },
+ {
+ constant: false,
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'serviceContract',
+ type: 'address',
+ },
+ ],
+ name: 'addServiceContract',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ constant: false,
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: '_newEntry',
+ type: 'uint256',
+ },
+ {
+ internalType: 'address payable',
+ name: 'submitter',
+ type: 'address',
+ },
+ ],
+ name: 'createGroup',
+ outputs: [],
+ payable: true,
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'currentRequestGroupIndex',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'currentRequestPreviousEntry',
+ outputs: [
+ {
+ internalType: 'bytes',
+ name: '',
+ type: 'bytes',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'currentRequestStartBlock',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'dkgGasEstimate',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'dkgSubmitterReimbursementFee',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'entryVerificationFee',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'entryVerificationGasEstimate',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'gasPriceCeiling',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: false,
+ inputs: [],
+ name: 'genesis',
+ outputs: [],
+ payable: true,
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'getFirstActiveGroupIndex',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [
+ {
+ internalType: 'bytes',
+ name: 'groupPubKey',
+ type: 'bytes',
+ },
+ ],
+ name: 'getGroupMemberRewards',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [
+ {
+ internalType: 'bytes',
+ name: 'groupPubKey',
+ type: 'bytes',
+ },
+ ],
+ name: 'getGroupMembers',
+ outputs: [
+ {
+ internalType: 'address[]',
+ name: 'members',
+ type: 'address[]',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: 'groupIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'getGroupPublicKey',
+ outputs: [
+ {
+ internalType: 'bytes',
+ name: '',
+ type: 'bytes',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: 'groupIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'getGroupRegistrationTime',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'getNumberOfCreatedGroups',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'groupCreationFee',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'groupMemberBaseReward',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'groupProfitFee',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'groupSelectionGasEstimate',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'groupSize',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'groupThreshold',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'staker',
+ type: 'address',
+ },
+ ],
+ name: 'hasMinimumStake',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'operator',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'groupIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'hasWithdrawnRewards',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'isEntryInProgress',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [
+ {
+ internalType: 'bytes',
+ name: 'groupPubKey',
+ type: 'bytes',
+ },
+ ],
+ name: 'isGroupRegistered',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'isGroupSelectionPossible',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: 'groupIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'isGroupTerminated',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [
+ {
+ internalType: 'bytes',
+ name: 'groupPubKey',
+ type: 'bytes',
+ },
+ ],
+ name: 'isStaleGroup',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'numberOfGroups',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: false,
+ inputs: [],
+ name: 'refreshGasPrice',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ constant: false,
+ inputs: [
+ {
+ internalType: 'bytes',
+ name: '_groupSignature',
+ type: 'bytes',
+ },
+ ],
+ name: 'relayEntry',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'relayEntryTimeout',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: false,
+ inputs: [],
+ name: 'reportRelayEntryTimeout',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ constant: false,
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: 'groupIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'signedMsgSender',
+ type: 'bytes',
+ },
+ ],
+ name: 'reportUnauthorizedSigning',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'resultPublicationBlockStep',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'selectedParticipants',
+ outputs: [
+ {
+ internalType: 'address[]',
+ name: '',
+ type: 'address[]',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: false,
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: 'requestId',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'previousEntry',
+ type: 'bytes',
+ },
+ ],
+ name: 'sign',
+ outputs: [],
+ payable: true,
+ stateMutability: 'payable',
+ type: 'function',
+ },
+ {
+ constant: false,
+ inputs: [
+ {
+ internalType: 'uint256',
+ name: 'submitterMemberIndex',
+ type: 'uint256',
+ },
+ {
+ internalType: 'bytes',
+ name: 'groupPubKey',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes',
+ name: 'misbehaved',
+ type: 'bytes',
+ },
+ {
+ internalType: 'bytes',
+ name: 'signatures',
+ type: 'bytes',
+ },
+ {
+ internalType: 'uint256[]',
+ name: 'signingMembersIndexes',
+ type: 'uint256[]',
+ },
+ ],
+ name: 'submitDkgResult',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ constant: false,
+ inputs: [
+ {
+ internalType: 'bytes32',
+ name: 'ticket',
+ type: 'bytes32',
+ },
+ ],
+ name: 'submitTicket',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'submittedTickets',
+ outputs: [
+ {
+ internalType: 'uint64[]',
+ name: '',
+ type: 'uint64[]',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: true,
+ inputs: [],
+ name: 'ticketSubmissionTimeout',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ payable: false,
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ constant: false,
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'operator',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'groupIndex',
+ type: 'uint256',
+ },
+ ],
+ name: 'withdrawGroupMemberRewards',
+ outputs: [],
+ payable: false,
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+];
diff --git a/src/lib/utils/ethereum-service.ts b/src/lib/utils/ethereum-service.ts
index 3e2e011..6a30c9d 100644
--- a/src/lib/utils/ethereum-service.ts
+++ b/src/lib/utils/ethereum-service.ts
@@ -1,7 +1,10 @@
-import { providers } from 'ethers';
+import { providers, Contract } from 'ethers';
import { EthereumNode } from '@/types';
import { waitFor } from './async';
import { range } from './numbers';
+import { ETHEREUM_ACCOUNTS, CONTRACTS } from '../constants';
+import { KeepRandomBeaconOperatorABI } from '../abis';
+import { info } from 'electron-log';
class EthereumService {
createClient(node: EthereumNode) {
@@ -16,6 +19,26 @@ class EthereumService {
return this.createClient(node).getBlockNumber();
}
+ async triggerKeepBeaconGenesis(node: EthereumNode) {
+ try {
+ const [owner] = ETHEREUM_ACCOUNTS;
+ const signer = this.createClient(node).getSigner(owner);
+ const contract = new Contract(
+ CONTRACTS.KEEP.CORE.KEEP_RANDOM_BEACON_OPERATOR.ADDRESS,
+ KeepRandomBeaconOperatorABI,
+ signer,
+ );
+ const dkgGas = await contract.dkgGasEstimate();
+ const gasPrice = await contract.gasPriceCeiling();
+ const dkgFee = dkgGas.mul(gasPrice);
+
+ await contract.genesis({ value: dkgFee });
+ info('Beacon genesis successful');
+ } catch (error) {
+ error('Beacon genesis failed with error', error);
+ }
+ }
+
/**
* Helper function to continually query the ganache node until a successful
* response is received or it times out
diff --git a/src/localization/languages/en.json b/src/localization/languages/en.json
index 5545fee..4f1472d 100644
--- a/src/localization/languages/en.json
+++ b/src/localization/languages/en.json
@@ -12,6 +12,7 @@
"NETWORK_NAME": "Network Name",
"NETWORK_NAME_REQUIRED": "A network name is required",
+ "NETWORK_NAME_MAX_LENGTH": "Network name must be 16 characters or less",
"HOW_MANY_NODES": "How many nodes?",
"RANDOM_BEACON": "Random Beacon",
"RANDOM_BEACON_NODE_REQUIRED": "At least one random beacon node is required",
diff --git a/src/store/modules/ethereum.ts b/src/store/modules/ethereum.ts
index 4acf733..dff15b5 100644
--- a/src/store/modules/ethereum.ts
+++ b/src/store/modules/ethereum.ts
@@ -5,7 +5,6 @@ import { getNetworkById, delay, ethereumService } from '@/lib/utils';
import { system, network } from '..';
import { languageLibrary } from '@/localization';
import Vue from 'vue';
-import { info } from 'electron-log';
@Module({ store, name: 'ethereum', dynamic: true, namespaced: true })
export class EthereumModule extends VuexModule {
@@ -36,7 +35,6 @@ export class EthereumModule extends VuexModule {
if (!this._nodes[node.name]) {
Vue.set(this._nodes, node.name, {});
}
- info(this._nodes[node.name], 'blockHeight', blockHeight);
Vue.set(this._nodes[node.name], 'blockHeight', blockHeight);
}
diff --git a/src/store/modules/network.ts b/src/store/modules/network.ts
index 24088e8..03937dd 100644
--- a/src/store/modules/network.ts
+++ b/src/store/modules/network.ts
@@ -270,6 +270,8 @@ export class NetworkModule extends VuexModule {
.then(async () => {
this.setStatus({ id, status: Status.Started, only: eth.name });
await ethereum.getInfo(eth);
+ await ethereumService.triggerKeepBeaconGenesis(eth);
+ await ethereum.mine({ blocks: 5, node: eth }); // tBTC dapp fails unless blocks are mined on startup
})
.catch((error) =>
this.setStatus({