diff --git a/firebase.json b/firebase.json index 8ad7f2cac..a41553a5c 100644 --- a/firebase.json +++ b/firebase.json @@ -1,46 +1,9 @@ { - "hosting": [ { - "target": "production", - "public": "dist", - "ignore": [ - "firebase.json", - "**/.*", - "**/node_modules/**" - ], - "rewrites": [ - { - "source": "**", - "destination": "/index.html" - } - ], - "headers": [ - { - "source": "**/*", - "headers": [ - { - "key": "X-Content-Type-Options", - "value": "nosniff" - }, - { - "key": "X-Frame-Options", - "value": "DENY" - }, - { - "key": "X-XSS-Protection", - "value": "1; mode=block" - } - ] - } - ] - }, + "hosting": [ { - "target": "staging", + "target": "production", "public": "dist", - "ignore": [ - "firebase.json", - "**/.*", - "**/node_modules/**" - ], + "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], "rewrites": [ { "source": "**", @@ -52,9 +15,35 @@ "source": "**/*", "headers": [ { - "key": "Content-Security-Policy", - "value": "frame-ancestors 'self' https://bancor-v2-beta.web.app https://bancor.network https://*.bancor.network; default-src 'self'; connect-src https://bancor.network https://ropsten-ptdczarhfq-nw.a.run.app/welcome wss://*.blocknative.com https://*.bancor.network https://*.keeperdao.com https://*.sentry.io https://raw.githubusercontent.com/Velua/eth-tokens-registry/master/tokens.json wss://*.alchemyapi.io; font-src https://fonts.gstatic.com; img-src 'self' data: https://etherscan.io https://*.etherscan.io https://raw.githubusercontent.com https://storage.googleapis.com; script-src 'self' 'unsafe-eval' https://www.google-analytics.com/analytics.js https://www.googletagmanager.com/gtag/js; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com/;" + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "X-Frame-Options", + "value": "DENY" }, + { + "key": "X-XSS-Protection", + "value": "1; mode=block" + } + ] + } + ] + }, + { + "target": "staging", + "public": "dist", + "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ], + "headers": [ + { + "source": "**/*", + "headers": [ { "key": "X-Content-Type-Options", "value": "nosniff" @@ -72,4 +61,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/package.json b/package.json index 40d7f6839..3a4e6f8bd 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "vue-analytics": "^5.17.2", "vue-class-component": "^7.2.3", "vue-content-loader": "^2.0.0", - "vue-gtag": "^1.14.0", "vue-i18n": "8.24.4", "vue-property-decorator": "^9.1.2", "vue-router": "^3.5.1", diff --git a/src/App.vue b/src/App.vue index ce3eb76cf..3ec79c8c0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -163,12 +163,7 @@ export default class App extends BaseComponent { try { await vxm.bancor.init(initParams); - // @ts-ignore this.loading = false; - // @ts-ignore - this.$gtag.event("initBancor", { - event_category: this.$route.params.service - }); } catch (e) { await wait(1000); try { diff --git a/src/components/modals/ModalBase.vue b/src/components/modals/ModalBase.vue index b992aee66..ee2c67256 100644 --- a/src/components/modals/ModalBase.vue +++ b/src/components/modals/ModalBase.vue @@ -20,7 +20,7 @@ icon="chevron-left" :class="darkMode ? 'text-dark' : 'text-light'" class="cursor" - @click="onHide" + @click="hide" /> @@ -94,6 +94,10 @@ export default class ModalBase extends BaseComponent { ]; } + hide() { + this.show = false; + } + @Emit("onHide") onHide() { this.show = false; diff --git a/src/components/modals/ModalTxAction.vue b/src/components/modals/ModalTxAction.vue index 465b9b0e8..f598f2d33 100644 --- a/src/components/modals/ModalTxAction.vue +++ b/src/components/modals/ModalTxAction.vue @@ -1,5 +1,10 @@ @@ -44,16 +45,18 @@ import { Component } from "vue-property-decorator"; import ContentBlock from "@/components/common/ContentBlock.vue"; import BaseComponent from "@/components/BaseComponent.vue"; +import SwapMarket from "@/components/swap/SwapMarket.vue"; +import SwapLimit from "@/components/swap/SwapLimit.vue"; @Component({ components: { - ContentBlock + ContentBlock, + SwapLimit, + SwapMarket } }) export default class SwapHome extends BaseComponent { - get limit() { - return this.$route.name === "SwapLimit"; - } + limit = false; } diff --git a/src/components/swap/SwapLimit.vue b/src/components/swap/SwapLimit.vue index bff835111..5cf659fb4 100644 --- a/src/components/swap/SwapLimit.vue +++ b/src/components/swap/SwapLimit.vue @@ -438,7 +438,7 @@ export default class SwapLimit extends BaseTxAction { } this.$router.replace({ - name: "SwapLimit", + name: "Swap", query: { from: id, to: to @@ -455,7 +455,7 @@ export default class SwapLimit extends BaseTxAction { } this.$router.replace({ - name: "SwapLimit", + name: "Swap", query: { from: from, to: id @@ -465,7 +465,7 @@ export default class SwapLimit extends BaseTxAction { invertSelection() { this.$router.replace({ - name: "SwapLimit", + name: "Swap", query: { from: this.token2.id, to: this.token1.id @@ -746,7 +746,7 @@ export default class SwapLimit extends BaseTxAction { if (this.$route.query.from) defaultQuery.from = this.$route.query.from; // @ts-ignore if (this.$route.query.to) defaultQuery.to = this.$route.query.to; - await this.$router.replace({ name: "SwapLimit", query: defaultQuery }); + await this.$router.replace({ name: "Swap", query: defaultQuery }); } } } diff --git a/src/components/swap/SwapMarket.vue b/src/components/swap/SwapMarket.vue index 651b842d6..bbe9a73cb 100644 --- a/src/components/swap/SwapMarket.vue +++ b/src/components/swap/SwapMarket.vue @@ -86,6 +86,7 @@ :title="$t('confirm_token_swap')" icon="exchange-alt" :tx-meta.sync="txMeta" + :onHide="onHide" > { + if (window.dataLayer) return; + if (id && name) + window.dataLayer = [ + { + wallet: { + id: id, + name: name + } + } + ]; + init(window, document, "script", "dataLayer", "GTM-TCBKR7W"); + sendGTMPath(undefined, window.location.pathname); +}; + +const init = (w: any, d: any, s: any, l: any, i: any) => { + w[l] = w[l] || []; + w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" }); + var f = d.getElementsByTagName(s)[0], + j = d.createElement(s), + dl = l != "dataLayer" ? "&l=" + l : ""; + j.async = true; + j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl; + f.parentNode.insertBefore(j, f); +}; + +const sendGTM = (data: {}) => { + const dataLayer = window.dataLayer as {}[]; + if (dataLayer) dataLayer.push(data); +}; + +export const sendGTMEvent = ( + event: string, + ga_event_category: string, + event_properties: {} | undefined = undefined +) => + sendGTM({ + event: "CE " + event, + event_properties: event_properties, + user_properties: undefined, + ga_event: { + category: ga_event_category + } + }); + +export const sendWalletEvent = ( + event: string, + ga_event_category: string, + id: string, + name: string +) => + sendGTM({ + event: "CE " + event, + ga_event: { + category: ga_event_category + }, + user_properties: { + wallet_id: id, + wallet_name: name + }, + wallet: { + id, + name + } + }); + +export const sendGTMPath = ( + from: string | undefined, + to: string, + darkMode: boolean = false +) => + sendGTM({ + event: "VP " + to, + page: { + from_path: from, + to_path: to, + theme: darkMode ? "Dark" : "Light" + }, + user_properties: undefined, + ga_event: undefined + }); diff --git a/src/main.ts b/src/main.ts index 78ae5c207..9971eef21 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,7 +18,6 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { sync } from "vuex-router-sync"; import { firebase } from "@firebase/app"; import "@firebase/analytics"; -import VueGtag from "vue-gtag"; const appVersion = JSON.parse( unescape(escape(JSON.stringify(require("../package.json")))) @@ -59,14 +58,6 @@ const firebaseConfig = { firebase.initializeApp(firebaseConfig); -Vue.use( - VueGtag, - { - config: { id: "UA-174155472-1" } - }, - router -); - Vue.use(BootstrapVue); library.add(...fas, ...fab, ...fal, ...far); diff --git a/src/router/index.ts b/src/router/index.ts index b376d9f6a..5e06c2a3c 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -6,8 +6,6 @@ import Navigation from "@/components/layout/Navigation.vue"; import { services } from "@/api/helpers"; import PoolActions from "@/components/pool/PoolActions.vue"; import SwapHome from "@/components/swap/SwapHome.vue"; -import SwapMarket from "@/components/swap/SwapMarket.vue"; -import SwapLimit from "@/components/swap/SwapLimit.vue"; import CreateHome from "@/views/CreateHome.vue"; import DataSummary from "@/components/data/DataSummary.vue"; import Portfolio from "@/views/Portfolio.vue"; @@ -27,6 +25,8 @@ import RestakeRewards from "@/components/rewards/RestakeRewards.vue"; import WithdrawRewards from "@/components/rewards/WithdrawRewards.vue"; import LimitOrderTable from "@/components/swap/LimitOrderTable.vue"; import VoteLegacy from "@/components/vote/VoteLegacy.vue"; +import { sendGTMPath } from "@/gtm"; +import { vxm } from "@/store"; Vue.use(Router); @@ -224,18 +224,7 @@ export const router = new Router({ key: "swap", feature: "Trade" }, - children: [ - { - path: "", - name: "Swap", - component: SwapMarket - }, - { - path: "limit", - name: "SwapLimit", - component: SwapLimit - } - ] + name: "Swap" }, { path: "/:service/data", @@ -365,6 +354,9 @@ const setPreferredService = (service: string) => { }; router.beforeEach((to, from, next) => { + if (from.path !== to.path) { + sendGTMPath(from.path, to.path, vxm.general.darkMode); + } if (to.meta && to.meta.feature) { const service = services.find( service => service.namespace == to.params.service diff --git a/src/store/modules/swap/ethBancor.ts b/src/store/modules/swap/ethBancor.ts index b6e0708e2..06415519c 100644 --- a/src/store/modules/swap/ethBancor.ts +++ b/src/store/modules/swap/ethBancor.ts @@ -232,6 +232,7 @@ import { } from "@/api/observables/keeperDao"; import { createOrder } from "@/api/orderSigning"; import { tokens$ } from "@/api/observables/pools"; +import { sendGTMEvent } from "@/gtm"; keeperTokens$.subscribe(token => vxm.ethBancor.setDaoTokens(token.map(x => x.address)) @@ -2495,9 +2496,11 @@ export class EthBancorModule resolve(txHash); } }) - .on("confirmation", () => { - if (onConfirmation) onConfirmation(txHash); - resolve(txHash); + .on("confirmation", (confirmationNumber: number) => { + if (confirmationNumber === 1) { + if (onConfirmation) onConfirmation(txHash); + resolve(txHash); + } }) .on("error", (error: any) => reject(error)); }); @@ -6000,7 +6003,6 @@ export class EthBancorModule description: "Done!" } ]; - onUpdate!(0, steps); const fromTokenDecimals = await this.getDecimalsByTokenAddress( @@ -6022,6 +6024,23 @@ export class EthBancorModule toId: to.id }); + const conversion = { + conversion_type: "Market", + conversion_approve: "Unlimited", + conversion_blockchain: "ethereum", + conversion_blockchain_network: + vxm.ethBancor.currentNetwork === EthNetworks.Ropsten + ? "Ropsten" + : "MainNet", + conversion_settings: + vxm.bancor.slippageTolerance === 0.005 ? "Regular" : "Advanced", + conversion_token_pair: fromSymbol + "/" + toToken.symbol, + conversion_from_token: fromSymbol, + conversion_to_token: toToken.symbol, + conversion_from_amount: fromAmount, + conversion_to_amount: to.amount + }; + const ethPath = generateEthPath(fromSymbol, relays); onUpdate!(1, steps); @@ -6032,7 +6051,6 @@ export class EthBancorModule tokenAddress: fromTokenContract, onPrompt }); - onUpdate!(2, steps); const networkContract = buildNetworkContract(this.contracts.BancorNetwork); @@ -6040,6 +6058,12 @@ export class EthBancorModule const expectedReturn = to.amount; const expectedReturnWei = expandToken(expectedReturn, toTokenDecimals); + sendGTMEvent( + "Conversion Wallet Confirmation Request", + "Conversion", + conversion + ); + const confirmedHash = await this.resolveTxOnConfirmation({ tx: networkContract.methods.convertByPath( ethPath.path, @@ -6049,11 +6073,23 @@ export class EthBancorModule zeroAddress, 0 ), - onConfirmation: () => - this.spamBalances([fromTokenContract, toTokenContract]), + onConfirmation: () => { + sendGTMEvent("Conversion Success", "Conversion", { + ...conversion, + conversion_market_eth_usd_rate: this.stats.nativeTokenPrice.price, + conversion_market_token_rate: fromToken.price?.toFixed(10), + transaction_category: "Conversion", + transaction_id: confirmedHash, + transaction_revenue: "" + }); + return this.spamBalances([fromTokenContract, toTokenContract]); + }, resolveImmediately: true, ...(fromIsEth && { value: fromWei }), - onHash: () => onUpdate!(3, steps) + onHash: () => { + sendGTMEvent("Conversion Wallet Confirmed", "Conversion", conversion); + return onUpdate!(3, steps); + } }); onUpdate!(4, steps); diff --git a/src/store/modules/wallet/ethWallet.ts b/src/store/modules/wallet/ethWallet.ts index b35a948c1..d89022d87 100644 --- a/src/store/modules/wallet/ethWallet.ts +++ b/src/store/modules/wallet/ethWallet.ts @@ -6,6 +6,7 @@ import { fromWei, isAddress, toHex, toWei } from "web3-utils"; import { shrinkToken } from "@/api/eth/helpers"; import { vxm } from "@/store"; import { EthNetworks, getWeb3, Provider, web3 } from "@/api/web3"; +import { sendGTMEvent, googleTagManager, sendWalletEvent } from "@/gtm"; const tx = (data: any) => new Promise((resolve, reject) => { @@ -58,8 +59,19 @@ export class EthereumModule extends VuexModule.With({ @action async connect() { try { + sendGTMEvent("Wallet Connect Select Wallet Popup", "Wallet"); await onboard.walletSelect(); + const state = onboard.getState(); + sendGTMEvent("Wallet Connect Wallet Icon Click", "Wallet", { + wallet_name: state.wallet.name + }); await onboard.walletCheck(); + sendWalletEvent( + "Wallet Connect", + "Wallet", + state.address, + state.wallet.name ?? "" + ); } catch (e) { console.error(e, "was the error"); throw new Error(`error: ${e}`); @@ -67,9 +79,10 @@ export class EthereumModule extends VuexModule.With({ } @action async nativeBalanceChange(nativeBalance: string) { - vxm.ethBancor.updateUserBalances([ - { balance: fromWei(nativeBalance), id: ethReserveAddress } - ]); + if (nativeBalance) + vxm.ethBancor.updateUserBalances([ + { balance: fromWei(nativeBalance), id: ethReserveAddress } + ]); } @action async checkAlreadySignedIn() { @@ -78,6 +91,7 @@ export class EthereumModule extends VuexModule.With({ if (previouslySelectedWallet) { await onboard.walletSelect(previouslySelectedWallet); } + googleTagManager(this.loggedInAccount, previouslySelectedWallet); } @action async getBalance({ diff --git a/yarn.lock b/yarn.lock index dfc6200ea..fb767b01c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20589,11 +20589,6 @@ vue-functional-data-merge@^3.1.0: resolved "https://registry.yarnpkg.com/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz#08a7797583b7f35680587f8a1d51d729aa1dc657" integrity sha512-leT4kdJVQyeZNY1kmnS1xiUlQ9z1B/kdBFCILIjYYQDqZgLqCLa0UhjSSeRX6c3mUe6U5qYeM8LrEqkHJ1B4LA== -vue-gtag@^1.14.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/vue-gtag/-/vue-gtag-1.14.0.tgz#da835752fa26b459ef076db757a78b4e94a69976" - integrity sha512-jutSxDedzckzPxOV1dOfV3rWqlfw5uzXOGGxDiOiq5o1UiGerpCOP4uWlO7CfwmujJotMaxgo0xR/TXsc2TLqA== - vue-hot-reload-api@^2.3.0: version "2.3.4" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"