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 View KEEP DashboardKEEP 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({