diff --git a/.gitignore b/.gitignore index 9088eeb7e..070f9ccbe 100644 --- a/.gitignore +++ b/.gitignore @@ -53,4 +53,4 @@ astar-collator # Chopstick binaries db.sqlite* -.vercel \ No newline at end of file +.vercel diff --git a/package.json b/package.json index f05d3fc17..dd9ba2767 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,7 @@ "@types/validator": "^13.7.11", "@typescript-eslint/eslint-plugin": "^4.16.1", "@typescript-eslint/parser": "^4.16.1", + "dotenv": "^16.4.7", "eslint": "^7.14.0", "eslint-config-prettier": "^8.1.0", "eslint-plugin-jest": "^25.2.2", diff --git a/quasar.conf.js b/quasar.conf.js index 9cbbe728e..5e1996b36 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -12,6 +12,8 @@ const { configure } = require('quasar/wrappers'); const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); const ESLintPlugin = require('eslint-webpack-plugin'); const path = require('path'); +require('dotenv').config() + module.exports = configure(function (ctx) { return { @@ -53,7 +55,10 @@ module.exports = configure(function (ctx) { // Full list of options: https://v2.quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build build: { vueRouterMode: 'history', // available values: 'hash', 'history' - + // Memo: Create .env file in root directory to set environment variables + // env: { + // SONEIUM_CCIP_ROUTER:process.env.SONEIUM_CCIP_ROUTER, + // }, // transpile: false, // Add dependencies for transpiling with Babel (Array of string/regex) diff --git a/src/assets/img/chain/soneium-white.svg b/src/assets/img/chain/soneium-white.svg new file mode 100644 index 000000000..1e7334c62 --- /dev/null +++ b/src/assets/img/chain/soneium-white.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/assets/EvmNativeToken.vue b/src/components/assets/EvmNativeToken.vue index b3410c28f..efab32ca5 100644 --- a/src/components/assets/EvmNativeToken.vue +++ b/src/components/assets/EvmNativeToken.vue @@ -45,14 +45,30 @@
- + @@ -78,9 +94,9 @@ :is-disabled="!layerZeroBridgeEnabled" > - {{ $t('assets.bridge') }} + {{ $t('assets.bridgeToZkEvm') }} - {{ $t('assets.bridge') }} + {{ $t('assets.bridgeToZkEvm') }} @@ -144,7 +160,12 @@ import { cbridgeAppLink } from 'src/c-bridge'; import ModalFaucet from 'src/components/assets/modals/ModalFaucet.vue'; import Balloon from 'src/components/common/Balloon.vue'; import { LOCAL_STORAGE } from 'src/config/localStorage'; -import { ccipMinatoBridgeEnabled, layerZeroBridgeEnabled, nativeBridgeEnabled } from 'src/features'; +import { + ccipMinatoBridgeEnabled, + layerZeroBridgeEnabled, + nativeBridgeEnabled, + ccipSoneiumBridgeEnabled, +} from 'src/features'; import { useAccount, useBreakpoints, useFaucet, useNetworkInfo } from 'src/hooks'; import { faucetSethLink } from 'src/links'; import { getTokenImage } from 'src/modules/token'; @@ -156,6 +177,7 @@ import { } from 'src/router/routes'; import { useStore } from 'src/store'; import { computed, defineComponent, ref, watch, watchEffect } from 'vue'; + import CustomRouterLink from '../common/CustomRouterLink.vue'; export default defineComponent({ @@ -177,8 +199,9 @@ export default defineComponent({ const isCcipBalloon = ref(false); const isBalloonClosing = ref(false); + const isSoneiumButtonHover = ref(false); - const { currentNetworkName, nativeTokenSymbol, isZkEvm, isAstar, isShibuyaEvm } = + const { currentNetworkName, nativeTokenSymbol, isZkEvm, isAstar, isShibuyaEvm, isAstarEvm } = useNetworkInfo(); const closeCcipBalloon = () => { @@ -229,16 +252,34 @@ export default defineComponent({ const isTruncate = !nativeTokenSymbol.value.toUpperCase().includes('BTC'); + const isEnableCcipBridge = computed(() => { + return ( + (isShibuyaEvm.value && ccipMinatoBridgeEnabled) || + (isAstarEvm.value && ccipSoneiumBridgeEnabled) + ); + }); + // Memo: display the balloon animation watch( - [isShibuyaEvm], + [isShibuyaEvm, isAstarEvm], async () => { - const isBallonDisplayed = Boolean(localStorage.getItem(LOCAL_STORAGE.BALLOON_CCIP_SHIBUYA)); - if (isShibuyaEvm.value && !isBallonDisplayed) { + const isBallonShibuyaDisplayed = Boolean( + localStorage.getItem(LOCAL_STORAGE.BALLOON_CCIP_SHIBUYA) + ); + const isBallonAstarDisplayed = Boolean( + localStorage.getItem(LOCAL_STORAGE.BALLOON_CCIP_ASTAR) + ); + if (isShibuyaEvm.value && !isBallonShibuyaDisplayed) { await wait(1000); isCcipBalloon.value = true; localStorage.setItem(LOCAL_STORAGE.BALLOON_CCIP_SHIBUYA, 'true'); } + + if (isAstarEvm.value && !isBallonAstarDisplayed) { + await wait(1000); + isCcipBalloon.value = true; + localStorage.setItem(LOCAL_STORAGE.BALLOON_CCIP_ASTAR, 'true'); + } }, { immediate: true } ); @@ -261,9 +302,11 @@ export default defineComponent({ nativeBridgeEnabled, layerZeroBridgeEnabled, isShibuyaEvm, - ccipMinatoBridgeEnabled, + isEnableCcipBridge, isCcipBalloon, isBalloonClosing, + isAstarEvm, + isSoneiumButtonHover, closeCcipBalloon, buildCcipBridgePageLink, truncate, diff --git a/src/components/assets/styles/asset-list.scss b/src/components/assets/styles/asset-list.scss index 554d4e6c7..df54bf82d 100644 --- a/src/components/assets/styles/asset-list.scss +++ b/src/components/assets/styles/asset-list.scss @@ -441,6 +441,7 @@ display: block; text-align: center; margin-top: 10px; + width: 80px; @media (min-width: $sm) { display: none; } diff --git a/src/components/bridge/BridgeSelection.vue b/src/components/bridge/BridgeSelection.vue index c5d8a01d4..c518f7842 100644 --- a/src/components/bridge/BridgeSelection.vue +++ b/src/components/bridge/BridgeSelection.vue @@ -6,9 +6,9 @@
- -

- {{ $t('bridge.ccipMinatoBridge.remark') }} +

+ {{ $t('bridge.ccipSoneiumBridge.remark') }}

@@ -232,6 +250,7 @@ import { celerBridgeEnabled, layerSwapBridgeEnabled, layerZeroBridgeEnabled, + ccipSoneiumBridgeEnabled, nativeBridgeEnabled, } from 'src/features'; import { useAccount, useNetworkInfo } from 'src/hooks'; @@ -259,6 +278,7 @@ export default defineComponent({ isAstar, isH160, isShibuyaEvm, + isAstarEvm, nativeTokenSymbol, } = useNetworkInfo(); @@ -280,8 +300,11 @@ export default defineComponent({ return isH160.value && (isAstar.value || isAstarZkEvm.value); }); - const isEnableMinatoBridge = computed(() => { - return isShibuyaEvm.value && ccipMinatoBridgeEnabled; + const isEnableCcipBridge = computed(() => { + return ( + (isShibuyaEvm.value && ccipMinatoBridgeEnabled) || + (isAstarEvm.value && ccipSoneiumBridgeEnabled) + ); }); return { @@ -300,8 +323,9 @@ export default defineComponent({ layerSwapBridgeEnabled, nativeBridgeEnabled, layerZeroBridgeEnabled, - isEnableMinatoBridge, + isEnableCcipBridge, isShibuyaEvm, + isAstarEvm, buildEthereumBridgePageLink, buildLzBridgePageLink, navigateInNewTab, diff --git a/src/components/bridge/ccip/CcipBridge.vue b/src/components/bridge/ccip/CcipBridge.vue index 71bf04d34..1c6d2c616 100644 --- a/src/components/bridge/ccip/CcipBridge.vue +++ b/src/components/bridge/ccip/CcipBridge.vue @@ -162,7 +162,7 @@ import { EthBridgeNetworkName } from 'src/modules/zk-evm-bridge'; import { useStore } from 'src/store'; import { PropType, computed, defineComponent, ref, watch } from 'vue'; import Jazzicon from 'vue3-jazzicon/src/components'; -import { ccipMinatoBridgeEnabled } from 'src/features'; +import { ccipMinatoBridgeEnabled, ccipSoneiumBridgeEnabled } from 'src/features'; import { ccipBridgeIcon, CCIP_TOKEN, @@ -255,7 +255,7 @@ export default defineComponent({ }, setup(props) { const { currentAccount } = useAccount(); - const { nativeTokenSymbol } = useNetworkInfo(); + const { nativeTokenSymbol, isShibuyaEvm, isAstarEvm } = useNetworkInfo(); const store = useStore(); const isHandling = ref(false); const isLoading = computed(() => store.getters['general/isLoading']); @@ -318,9 +318,12 @@ export default defineComponent({ isHandling.value = false; }; - // Todo: update for Soneium const ccipBridgeEnabled = computed(() => { - return ccipMinatoBridgeEnabled; + return isShibuyaEvm.value + ? ccipMinatoBridgeEnabled + : isAstarEvm.value + ? ccipSoneiumBridgeEnabled + : false; }); watch( diff --git a/src/config/localStorage.ts b/src/config/localStorage.ts index a6cfcf43c..05d1256d6 100644 --- a/src/config/localStorage.ts +++ b/src/config/localStorage.ts @@ -18,6 +18,7 @@ export enum LOCAL_STORAGE { XVM_TX_HISTORIES = 'xvmTxHistories', BALLOON_NATIVE_TOKEN = 'balloonNativeToken', BALLOON_CCIP_SHIBUYA = 'balloonCcipShibuya', + BALLOON_CCIP_ASTAR = 'balloonCcipAstar', THEME_COLOR = 'themeColor', MULTISIG = 'multisig', CLOSE_DAPP_STAKING_V3_ONBOARDING = 'closeDappStakingV3Onboarding', diff --git a/src/config/web3/index.ts b/src/config/web3/index.ts index e39200628..f474b79da 100644 --- a/src/config/web3/index.ts +++ b/src/config/web3/index.ts @@ -49,8 +49,7 @@ export enum EVM { MOONRIVER = 1285, MOONBEAM = 1284, SONEIUM_MINATO_TESTNET = 1946, - // Todo: update - SONEIUM = 9999, + SONEIUM = 1868, } export const chainName = { @@ -166,8 +165,7 @@ export const rpcUrls = { [EVM.MOONRIVER]: ['https://rpc.api.moonriver.moonbeam.network'], [EVM.MOONBEAM]: ['https://rpc.api.moonbeam.network'], [EVM.SONEIUM_MINATO_TESTNET]: ['https://rpc.minato.soneium.org'], - // Todo: update - [EVM.SONEIUM]: ['https://rpc.minato.soneium.org'], + [EVM.SONEIUM]: ['https://rpc.soneium.org/'], }; export const blockExplorerUrls = { @@ -184,8 +182,7 @@ export const blockExplorerUrls = { [EVM.MOONRIVER]: ['https://moonriver.moonscan.io'], [EVM.MOONBEAM]: ['https://moonbeam.moonscan.io'], [EVM.SONEIUM_MINATO_TESTNET]: ['https://soneium-minato.blockscout.com'], - // Todo: update - [EVM.SONEIUM]: ['https://soneium-minato.blockscout.com'], + [EVM.SONEIUM]: ['https://soneium.blockscout.com'], }; export const CHAIN_INFORMATION = { diff --git a/src/features.ts b/src/features.ts index ff3e7d7f5..7f2fd0568 100644 --- a/src/features.ts +++ b/src/features.ts @@ -5,6 +5,7 @@ export const layerSwapBridgeEnabled = false; export const celerBridgeEnabled = true; export const omniBridgeEnabled = true; export const ccipMinatoBridgeEnabled = true; +export const ccipSoneiumBridgeEnabled = true; const stargateBridgeEnabled = true; const stakeStoneBridgeEnabled = true; const arthSwapBridgeEnabled = true; diff --git a/src/hooks/bridge/useCcipBridge.ts b/src/hooks/bridge/useCcipBridge.ts index 903077eea..99c92a1e4 100644 --- a/src/hooks/bridge/useCcipBridge.ts +++ b/src/hooks/bridge/useCcipBridge.ts @@ -8,6 +8,7 @@ import { CCIP_TOKEN, CCIP_SBY, ccipBridgeAddress, + CCIP_ASTR, } from 'src/modules/ccip-bridge'; import { showLoading } from 'src/modules/extrinsic/utils'; import { useStore } from 'src/store'; @@ -24,7 +25,7 @@ import { astarNativeTokenErcAddr } from 'src/modules/xcm'; export const useCcipBridge = () => { const { isShibuya, nativeTokenSymbol } = useNetworkInfo(); - const selectedToken = ref(CCIP_SBY); + const selectedToken = ref(isShibuya.value ? CCIP_SBY : CCIP_ASTR); const bridgeAmt = ref(null); const toBridgeBalance = ref(0); const fromBridgeBalance = ref(0); diff --git a/src/hooks/useNetworkInfo.ts b/src/hooks/useNetworkInfo.ts index b2a64d339..13e4061bb 100644 --- a/src/hooks/useNetworkInfo.ts +++ b/src/hooks/useNetworkInfo.ts @@ -38,6 +38,9 @@ export function useNetworkInfo() { const isShibuyaEvm = computed(() => { return isH160.value && isShibuya.value; }); + const isAstarEvm = computed(() => { + return isH160.value && isAstar.value; + }); const currentNetworkChain = computed(() => { if (isZkEvm.value) { @@ -133,5 +136,6 @@ export function useNetworkInfo() { nativeTokenImg, isShibuya, isShibuyaEvm, + isAstarEvm, }; } diff --git a/src/i18n/en-US/index.ts b/src/i18n/en-US/index.ts index a7e4dc441..55b8eb78e 100644 --- a/src/i18n/en-US/index.ts +++ b/src/i18n/en-US/index.ts @@ -494,6 +494,7 @@ export default { faucet: 'Faucet', bridge: 'Bridge', bridgeToSoneium: 'Bridge to Soneium', + bridgeToZkEvm: 'Bridge to zkEVM', swap: 'Swap', manage: 'Manage', xcm: 'XCM', @@ -1092,6 +1093,12 @@ export default { text: 'Transfer SBY between Soneium Minato and Shibuya EVM. Powered by CCIP.', remark: 'Available on Shibuya EVM. Switch the network to use it.', }, + ccipSoneiumBridge: { + title: 'Soneium Bridge', + tag: 'ASTR', + text: 'Transfer ASTR between Soneium and Astar EVM. Powered by CCIP.', + remark: 'Available on Astar EVM. Switch the network to use it.', + }, astarBridge: { title: 'LayerZero', tag: 'ASTR', diff --git a/src/modules/ccip-bridge/index.ts b/src/modules/ccip-bridge/index.ts index 2537d78e7..031fea992 100644 --- a/src/modules/ccip-bridge/index.ts +++ b/src/modules/ccip-bridge/index.ts @@ -11,8 +11,7 @@ export enum CcipChainId { 'ShibuyaEvm' = 81, 'AstarEvm' = 592, 'SoneiumMinato' = 1946, - // Todo: update - 'Soneium' = 9999, + 'Soneium' = 1868, } export const ccipChainId = { @@ -24,25 +23,21 @@ export const ccipChainId = { export const ccipChainSelector = { [CcipChainId.ShibuyaEvm]: '6955638871347136141', - // Todo: update - [CcipChainId.AstarEvm]: '999999999999999', + [CcipChainId.AstarEvm]: '6422105447186081193', [CcipChainId.SoneiumMinato]: '686603546605904534', - // Todo: update - [CcipChainId.Soneium]: '999999999999999', + [CcipChainId.Soneium]: '12505351618335765396', }; const routerSoneiumMinato = '0x443a1bce545d56E2c3f20ED32eA588395FFce0f4'; -const routerSoneium = '0x443a1bce545d56E2c3f20ED32eA588395FFce0f4'; +const routerSoneium = '0x8C8B88d827Fe14Df2bc6392947d513C86afD6977'; const etherSenderReceiverShibuya = '0x89cB78A4A3cAD4cA86D3e3fF565f63B4620CB6ea'; -const etherSenderReceiverAstar = '0x89cB78A4A3cAD4cA86D3e3fF565f63B4620CB6ea'; +const etherSenderReceiverAstar = '0x4036a6Ff8C1a29677108Aef299B560f6E4fA5e71'; export const ccipBridgeAddress = { [CcipChainId.ShibuyaEvm]: etherSenderReceiverShibuya, - // Todo: update [CcipChainId.AstarEvm]: etherSenderReceiverAstar, [CcipChainId.SoneiumMinato]: routerSoneiumMinato, - // Todo: update [CcipChainId.Soneium]: routerSoneium, }; @@ -50,7 +45,7 @@ export const ccipBridgeIcon = { [CcipNetworkName.ShibuyaEvm]: require('src/assets/img/chain/astar.png'), [CcipNetworkName.AstarEvm]: require('src/assets/img/chain/astar.png'), [CcipNetworkName.SoneiumMinato]: require('src/assets/img/chain/soneium-black.svg'), - [CcipNetworkName.Soneium]: require('src/assets/img/chain/astar.png'), + [CcipNetworkName.Soneium]: require('src/assets/img/chain/soneium-black.svg'), } as any; export interface CCIP_TOKEN { @@ -72,9 +67,20 @@ export const CCIP_SBY: CCIP_TOKEN = { image: require('/src/assets/img/token/astr.png'), }; +export const CCIP_ASTR: CCIP_TOKEN = { + symbol: 'ASTR', + name: 'Astar Token', + tokenAddress: { + [CcipChainId.AstarEvm]: astarNativeTokenErcAddr, + [CcipChainId.Soneium]: '0x2CAE934a1e84F693fbb78CA5ED3B0A6893259441', + }, + decimals: 18, + image: require('/src/assets/img/token/astr.png'), +}; + export const ccipBridgeTime = { [CcipNetworkName.ShibuyaEvm]: 3, [CcipNetworkName.AstarEvm]: 3, [CcipNetworkName.SoneiumMinato]: 30, - [CcipNetworkName.Soneium]: 30, + [CcipNetworkName.Soneium]: 120, }; diff --git a/src/modules/information/hot-topics/transfer/index.ts b/src/modules/information/hot-topics/transfer/index.ts index 5f94844e4..ea1ce22d9 100644 --- a/src/modules/information/hot-topics/transfer/index.ts +++ b/src/modules/information/hot-topics/transfer/index.ts @@ -2,6 +2,6 @@ import { Faq } from 'src/modules/information'; export const hotTopics: Faq[] = [ { title: 'Find latest news here', - url: 'https://medium.com/astar-network', + url: 'https://astar.network/blog', }, ]; diff --git a/src/v2/repositories/implementations/CcipBridgeRepository.ts b/src/v2/repositories/implementations/CcipBridgeRepository.ts index 61facde0d..eb223a1e8 100644 --- a/src/v2/repositories/implementations/CcipBridgeRepository.ts +++ b/src/v2/repositories/implementations/CcipBridgeRepository.ts @@ -86,8 +86,10 @@ export class CcipBridgeRepository implements ICcipBridgeRepository { const contract = new web3.eth.Contract(abi as AbiItem[], contractAddress); const { destinationChainSelector, message } = this.getMessageArgs(param); const fee = await contract.methods.getFee(destinationChainSelector, message).call(); + // Memo: Add 5% of fee for buffer + const feeWithBuffer = ethers.BigNumber.from(fee).mul(105).div(100).toString(); - return fee; + return feeWithBuffer; } public async getBridgeCcipAssetData({ diff --git a/yarn.lock b/yarn.lock index 24e4ab79e..3dad0ff07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6402,7 +6402,7 @@ mini-svg-data-uri "^1.0.3" traverse "^0.6.6" -"@tailwindcss/postcss7-compat@^2.0.3": +"@tailwindcss/postcss7-compat@^2.0.3", "tailwindcss@npm:@tailwindcss/postcss7-compat@^2.0.3": version "2.2.17" resolved "https://registry.yarnpkg.com/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.17.tgz#dc78f3880a2af84163150ff426a39e42b9ae8922" integrity sha512-3h2svqQAqYHxRZ1KjsJjZOVTQ04m29LjfrLjXyZZEJuvUuJN+BCIF9GI8vhE1s0plS0mogd6E6YLg6mu4Wv/Vw== @@ -10625,6 +10625,11 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" +dotenv@^16.4.7: + version "16.4.7" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26" + integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ== + duplexer2@~0.1.0: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -19357,47 +19362,6 @@ tailwindcss-theme-variants@^2.0.0-alpha.2: lodash "^4.17.21" postcss-selector-parser "^6.0.6" -"tailwindcss@npm:@tailwindcss/postcss7-compat@^2.0.3": - version "2.2.17" - resolved "https://registry.yarnpkg.com/@tailwindcss/postcss7-compat/-/postcss7-compat-2.2.17.tgz#dc78f3880a2af84163150ff426a39e42b9ae8922" - integrity sha512-3h2svqQAqYHxRZ1KjsJjZOVTQ04m29LjfrLjXyZZEJuvUuJN+BCIF9GI8vhE1s0plS0mogd6E6YLg6mu4Wv/Vw== - dependencies: - arg "^5.0.1" - autoprefixer "^9" - bytes "^3.0.0" - chalk "^4.1.2" - chokidar "^3.5.2" - color "^4.0.1" - cosmiconfig "^7.0.1" - detective "^5.2.0" - didyoumean "^1.2.2" - dlv "^1.1.3" - fast-glob "^3.2.7" - fs-extra "^10.0.0" - glob-parent "^6.0.1" - html-tags "^3.1.0" - is-color-stop "^1.1.0" - is-glob "^4.0.1" - lodash "^4.17.21" - lodash.topath "^4.5.2" - modern-normalize "^1.1.0" - node-emoji "^1.11.0" - normalize-path "^3.0.0" - object-hash "^2.2.0" - postcss "^7" - postcss-functions "^3" - postcss-js "^2" - postcss-load-config "^3.1.0" - postcss-nested "^4" - postcss-selector-parser "^6.0.6" - postcss-value-parser "^4.1.0" - pretty-hrtime "^1.0.3" - purgecss "^4.0.3" - quick-lru "^5.1.1" - reduce-css-calc "^2.1.8" - resolve "^1.20.0" - tmp "^0.2.1" - tapable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"