diff --git a/src/containers/NavBar/index.js b/src/containers/NavBar/index.js
index e35391b..18c4d97 100644
--- a/src/containers/NavBar/index.js
+++ b/src/containers/NavBar/index.js
@@ -9,7 +9,7 @@ import { connect } from 'react-redux';
import ClassNames from 'classnames';
import { hideSideBar, showConnectDialog } from '../../actions/navBar';
import Icon from '../../components/Icon';
-import { initializeChain, initializeCosmoStation } from '../../helper';
+import { initializeChain, initializeCosmoStation, initializeMetaMask } from '../../helper';
import { decode, encode } from 'js-base64';
import { config } from '../../config';
import { showMessage } from '../../actions/snackbar';
@@ -49,6 +49,7 @@ class NavBar extends Component {
this.getValidatorImage = this.getValidatorImage.bind(this);
this.getProposalDetails = this.getProposalDetails.bind(this);
this.handleCosmoStation = this.handleCosmoStation.bind(this);
+ this.handleMetaMask = this.handleMetaMask.bind(this);
}
componentDidMount () {
@@ -56,6 +57,10 @@ class NavBar extends Component {
setTimeout(() => {
this.handleCosmoStation(true);
}, 600);
+ } else if (localStorage.getItem('of_co_address') && (localStorage.getItem('of_co_wallet') === 'metamask')) {
+ setTimeout(() => {
+ this.handleMetaMask(true);
+ }, 600);
} else if (localStorage.getItem('of_co_address')) {
setTimeout(() => {
this.initKeplr();
@@ -70,7 +75,7 @@ class NavBar extends Component {
const array = [];
result.map((val) => {
const filter = this.props.proposalDetails && Object.keys(this.props.proposalDetails).length &&
- Object.keys(this.props.proposalDetails).find((key) => key === val.proposal_id);
+ Object.keys(this.props.proposalDetails).find((key) => key === val.id);
if (!filter) {
if (this.props.home && (val.status !== 'PROPOSAL_STATUS_VOTING_PERIOD')) {
return null;
@@ -79,7 +84,7 @@ class NavBar extends Component {
array.push(val.proposal_id);
}
if (val.status === 2 || val.status === 'PROPOSAL_STATUS_VOTING_PERIOD') {
- this.props.fetchProposalTally(val.proposal_id);
+ this.props.fetchProposalTally(val.id);
}
return null;
@@ -93,7 +98,7 @@ class NavBar extends Component {
const array = [];
this.props.proposals.map((val) => {
const filter = this.props.proposalDetails && Object.keys(this.props.proposalDetails).length &&
- Object.keys(this.props.proposalDetails).find((key) => key === val.proposal_id);
+ Object.keys(this.props.proposalDetails).find((key) => key === val.id);
if (!filter) {
if (this.props.home && (val.status !== 'PROPOSAL_STATUS_VOTING_PERIOD')) {
return null;
@@ -102,7 +107,7 @@ class NavBar extends Component {
array.push(val.proposal_id);
}
if (val.status === 2 || val.status === 'PROPOSAL_STATUS_VOTING_PERIOD') {
- this.props.fetchProposalTally(val.proposal_id);
+ this.props.fetchProposalTally(val.id);
}
return null;
@@ -142,6 +147,17 @@ class NavBar extends Component {
});
}
};
+
+ if (window.ethereum) {
+ window.ethereum && window.ethereum.on('accountsChanged', (accounts) => {
+ if (accounts.length === 0) {
+ showMessage('Please connect to MetaMask.');
+ return;
+ }
+
+ this.handleMetaMask();
+ });
+ }
}
componentDidUpdate (pp, ps, ss) {
@@ -149,12 +165,12 @@ class NavBar extends Component {
this.props.proposals && this.props.proposals.length) ||
((pp.address !== this.props.address) && (pp.address === '') && (this.props.address !== ''))) {
this.props.proposals.map((val) => {
- const votedOption = this.props.voteDetails && this.props.voteDetails.length && val && val.proposal_id &&
- this.props.voteDetails.filter((vote) => vote.proposal_id === val.proposal_id)[0];
+ const votedOption = this.props.voteDetails && this.props.voteDetails.length && val && val.id &&
+ this.props.voteDetails.filter((vote) => vote && vote.id === val.id)[0];
if ((val.status === 2 || val.status === 'PROPOSAL_STATUS_VOTING_PERIOD') &&
!votedOption && this.props.address) {
- this.props.fetchVoteDetails(val.proposal_id, this.props.address);
+ this.props.fetchVoteDetails(val.id, this.props.address);
}
return null;
@@ -168,7 +184,7 @@ class NavBar extends Component {
const array = [];
result.map((val) => {
const filter = this.props.proposalDetails && Object.keys(this.props.proposalDetails).length &&
- Object.keys(this.props.proposalDetails).find((key) => key === val.proposal_id);
+ Object.keys(this.props.proposalDetails).find((key) => key === val.id);
if (!filter) {
if (this.props.home && (val.status !== 'PROPOSAL_STATUS_VOTING_PERIOD')) {
return null;
@@ -177,8 +193,8 @@ class NavBar extends Component {
array.push(val.proposal_id);
}
if (val.status === 2 || val.status === 'PROPOSAL_STATUS_VOTING_PERIOD') {
- this.props.fetchProposalTally(val.proposal_id);
- this.props.fetchVoteDetails(val.proposal_id, this.props.address);
+ this.props.fetchProposalTally(val.id);
+ this.props.fetchVoteDetails(val.id, this.props.address);
}
return null;
@@ -324,6 +340,24 @@ class NavBar extends Component {
});
}
+ handleMetaMask (fetch) {
+ initializeMetaMask((error, account) => {
+ if (error) {
+ this.props.showMessage(error);
+ localStorage.removeItem('of_co_address');
+
+ return;
+ }
+
+ this.props.setAccountAddress(account && account.address && account.address.address);
+ if (fetch) {
+ this.handleFetch(account && account.address && account.address.address);
+ }
+ localStorage.setItem('of_co_address', encode(account && account.address && account.address.address));
+ localStorage.setItem('of_co_wallet', 'metamask');
+ });
+ }
+
render () {
return (
{
const VoteCalculation = (proposal, val) => {
if (proposal.status === 2 || proposal.status === 'PROPOSAL_STATUS_VOTING_PERIOD') {
const value = props.tallyDetails && props.tallyDetails[proposal.id];
- const sum = value && value.yes_count && value.no_count && value.no_with_veto_count && value.abstain_count &&
- (parseInt(value.yes_count) + parseInt(value.no_count) + parseInt(value.no_with_veto_count) + parseInt(value.abstain_count));
-
- return (props.tallyDetails && props.tallyDetails[proposal.id] && props.tallyDetails[proposal.id][val]
- ? tally(props.tallyDetails[proposal.id][val], sum) : '0%');
+ const sum = value && value.yes && value.no && value.no_with_veto && value.abstain &&
+ (parseInt(value.yes) + parseInt(value.no) + parseInt(value.no_with_veto) + parseInt(value.abstain));
+ let val1 = null;
+ if (val === 'yes_count') {
+ val1 = 'yes';
+ } else if (val === 'no_count') {
+ val1 = 'no';
+ } else if (val === 'no_with_veto_count') {
+ val1 = 'no_with_veto';
+ } else if (val === 'abstain_count') {
+ val1 = 'abstain';
+ }
+
+ return (props.tallyDetails && props.tallyDetails[proposal.id] && props.tallyDetails[proposal.id][val1]
+ ? tally(props.tallyDetails[proposal.id][val1], sum) : '0%');
} else {
const sum = proposal.final_tally_result && proposal.final_tally_result.yes_count &&
proposal.final_tally_result.no_count && proposal.final_tally_result.no_with_veto_count &&
@@ -63,7 +73,7 @@ const Cards = (props) => {
if (index < (page * rowsPerPage) && index >= (page - 1) * rowsPerPage) {
const votedOption = props.voteDetails && props.voteDetails.length &&
proposal && proposal.id &&
- props.voteDetails.filter((vote) => vote.id === proposal.id)[0];
+ props.voteDetails.filter((vote) => vote && vote.id === proposal.id)[0];
let proposer = proposal.proposer;
props.proposalDetails && Object.keys(props.proposalDetails).length &&
Object.keys(props.proposalDetails).filter((key) => {
diff --git a/src/containers/Proposals/ProposalDialog/Voting.js b/src/containers/Proposals/ProposalDialog/Voting.js
index 0b44444..46150ee 100644
--- a/src/containers/Proposals/ProposalDialog/Voting.js
+++ b/src/containers/Proposals/ProposalDialog/Voting.js
@@ -4,7 +4,7 @@ import { fetchProposalTally, fetchVoteDetails, hideProposalDialog } from '../../
import { connect } from 'react-redux';
import { Button, FormControlLabel, Radio, RadioGroup } from '@material-ui/core';
import CircularProgress from '../../../components/CircularProgress';
-import { cosmoStationSign, signTxAndBroadcast } from '../../../helper';
+import { cosmoStationSign, metaMaskSign, signTxAndBroadcast } from '../../../helper';
import { config } from '../../../config';
import variables from '../../../utils/variables';
import { showMessage } from '../../../actions/snackbar';
@@ -68,6 +68,11 @@ const Voting = (props) => {
return;
}
+ if (localStorage.getItem('of_co_wallet') === 'metamask') {
+ metaMaskSign(tx, props.address, handleFetch);
+ return;
+ }
+
signTxAndBroadcast(tx, props.address, handleFetch);
};
diff --git a/src/containers/Proposals/ProposalDialog/index.js b/src/containers/Proposals/ProposalDialog/index.js
index 095fbd4..be97fa6 100644
--- a/src/containers/Proposals/ProposalDialog/index.js
+++ b/src/containers/Proposals/ProposalDialog/index.js
@@ -38,7 +38,7 @@ class ProposalDialog extends Component {
componentDidMount () {
const votedOption = this.props.voteDetails && this.props.voteDetails.length && this.props.proposal && this.props.proposal.id &&
- this.props.voteDetails.filter((vote) => vote.id === this.props.proposal.id)[0];
+ this.props.voteDetails.filter((vote) => vote && vote.id === this.props.proposal.id)[0];
if (!votedOption && this.props.proposal && this.props.proposal.id && this.props.address) {
this.props.fetchVoteDetails(this.props.proposal.id, this.props.address);
@@ -74,8 +74,8 @@ class ProposalDialog extends Component {
if (proposal && (proposal.status === 2 || proposal.status === 'PROPOSAL_STATUS_VOTING_PERIOD')) {
const value = this.props.tallyDetails && this.props.tallyDetails[proposal.id];
- const sum = value && value.yes_count && value.no_count && value.no_with_veto_count && value.abstain_count &&
- (parseInt(value.yes_count) + parseInt(value.no_count) + parseInt(value.no_with_veto_count) + parseInt(value.abstain_count));
+ const sum = value && value.yes && value.no && value.no_with_veto && value.abstain &&
+ (parseInt(value.yes) + parseInt(value.no) + parseInt(value.no_with_veto) + parseInt(value.abstain));
return (this.props.tallyDetails && this.props.tallyDetails[proposal.id] && this.props.tallyDetails[proposal.id][val]
? tally(this.props.tallyDetails[proposal.id][val], sum) : '0%');
@@ -100,7 +100,7 @@ class ProposalDialog extends Component {
render () {
let votedOption = this.props.voteDetails && this.props.voteDetails.length &&
this.props.proposal && this.props.proposal.id &&
- this.props.voteDetails.filter((vote) => vote.id === this.props.proposal.id)[0];
+ this.props.voteDetails.filter((vote) => vote && vote.id === this.props.proposal.id)[0];
let proposer = this.props.proposal && this.props.proposal.proposer;
this.props.proposalDetails && Object.keys(this.props.proposalDetails).length &&
diff --git a/src/containers/Stake/DelegateDialog/index.js b/src/containers/Stake/DelegateDialog/index.js
index b9646ae..adb839c 100644
--- a/src/containers/Stake/DelegateDialog/index.js
+++ b/src/containers/Stake/DelegateDialog/index.js
@@ -13,7 +13,7 @@ import {
import ValidatorSelectField from './ValidatorSelectField';
import TokensTextField from './TokensTextField';
import ToValidatorSelectField from './ToValidatorSelectField';
-import { cosmoStationSign, signTxAndBroadcast } from '../../../helper';
+import { cosmoStationSign, metaMaskSign, signTxAndBroadcast } from '../../../helper';
import {
fetchRewards,
fetchVestingBalance,
@@ -61,6 +61,11 @@ const DelegateDialog = (props) => {
return;
}
+ if (localStorage.getItem('of_co_wallet') === 'metamask') {
+ metaMaskSign(updatedTx, props.address, handleFetch);
+ return;
+ }
+
signTxAndBroadcast(updatedTx, props.address, handleFetch);
};
diff --git a/src/helper.js b/src/helper.js
index edae05d..cb40095 100644
--- a/src/helper.js
+++ b/src/helper.js
@@ -119,6 +119,93 @@ export const initializeCosmoStation = (cb) => {
})();
};
+export const initializeMetaMask = (cb) => {
+ (async () => {
+ if (!window.ethereum) {
+ const error = 'Download the MetaMask Extension';
+ cb(error);
+ }
+
+ if (window.ethereum) {
+ const result = await window.ethereum.request({ method: 'wallet_getSnaps' });
+ const installed = Object.keys(result).includes('npm:@cosmsnap/snap');
+
+ // Install Snap
+ if (!installed) {
+ window.ethereum.request({
+ method: 'wallet_requestSnaps',
+ params: {
+ 'npm:@cosmsnap/snap': {
+ version: '^0.1.0',
+ },
+ },
+ }).then((result) => {
+ metamaskInitialize(cb);
+ }).catch((error) => {
+ cb((error && error.message) || error);
+ });
+ }
+
+ metamaskInitialize(cb);
+ } else {
+ return null;
+ }
+ })();
+};
+
+const metamaskInitialize = (cb) => {
+ (async () => {
+ try {
+ const initialized = await window.ethereum.request({
+ method: 'wallet_invokeSnap',
+ params: {
+ snapId: 'npm:@cosmsnap/snap',
+ request: {
+ method: 'initialized',
+ },
+ },
+ });
+
+ if (!initialized) {
+ // Initialize the Snap with default chains
+ window.ethereum.request({
+ method: 'wallet_invokeSnap',
+ params: {
+ snapId: 'npm:@cosmsnap/snap',
+ request: {
+ method: 'initialize',
+ },
+ },
+ }).then((result) => {
+ }).catch((error) => {
+ cb((error && error.message) || error);
+ });
+ }
+
+ window.ethereum.request({
+ method: 'wallet_invokeSnap',
+ params: {
+ snapId: 'npm:@cosmsnap/snap',
+ request: {
+ method: 'getChainAddress',
+ params: {
+ chain_id: chainId,
+ },
+ },
+ },
+ }).then((result) => {
+ if (result && result.data) {
+ cb(null, result && result.data);
+ }
+ }).catch((error) => {
+ cb((error && error.message) || error);
+ });
+ } catch (error) {
+ cb((error && error.message) || error);
+ }
+ })();
+};
+
export const signTxAndBroadcast = (tx, address, cb) => {
(async () => {
await window.keplr && window.keplr.enable(chainId);
@@ -178,3 +265,57 @@ export const cosmoStationSign = (tx, address, cb) => {
});
})();
};
+
+export const metaMaskSign = (tx, address, cb) => {
+ (async () => {
+ const msgs = tx.msgs ? tx.msgs : [tx.msg];
+ const fees = tx.fee;
+ if (fees && fees.amount && fees.amount.length) {
+ const array = [];
+ fees.amount.map((val) => {
+ let obj = {};
+ if (val && val.amount) {
+ obj = {
+ amount: String(Number(val.amount) * 100),
+ denom: config.COIN_MINIMAL_DENOM,
+ };
+ }
+
+ array.push(obj);
+ });
+
+ fees.amount = array;
+ }
+
+ window.ethereum.request({
+ method: 'wallet_invokeSnap',
+ params: {
+ snapId: 'npm:@cosmsnap/snap',
+ request: {
+ method: 'transact',
+ params: {
+ chain_id: chainId,
+ msgs: JSON.stringify(msgs),
+ fees: JSON.stringify(fees),
+ },
+ },
+ },
+ }).then((result) => {
+ if (result && result.data && result.data.code !== undefined && result.data.code !== 0) {
+ cb(result.data.log || result.data.rawLog);
+ } else if (result && !result.success) {
+ const message = 'unsuccess';
+ cb(message);
+ } else {
+ cb(null, result && result.data);
+ }
+ }).catch((error) => {
+ const message = 'success';
+ if (error && error.message === 'Invalid string. Length must be a multiple of 4') {
+ cb(null, message);
+ } else {
+ cb(error && error.message);
+ }
+ });
+ })();
+};
diff --git a/src/utils/variables.js b/src/utils/variables.js
index 4c0d669..7c076c7 100644
--- a/src/utils/variables.js
+++ b/src/utils/variables.js
@@ -63,6 +63,7 @@ const variables = {
connect_account: 'Account not connected. Please connect to wallet',
connecting: 'connecting',
cosmostation: 'Cosmostation',
+ metamask: 'MetaMask',
},
};