diff --git a/.gitignore b/.gitignore index feb46c8..1ab4a0a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ **/node_modules/ **/build/ **/yarn.lock +**/pnpm-lock.yaml **/.tsimp # Frontend diff --git a/frontend/package.json b/frontend/package.json index a02a1e5..392ed13 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,22 +12,27 @@ "lint": "next lint" }, "dependencies": { - "@near-wallet-selector/core": "^8.9.11", - "@near-wallet-selector/here-wallet": "^8.9.11", - "@near-wallet-selector/modal-ui": "^8.9.11", - "@near-wallet-selector/my-near-wallet": "^8.9.11", + "@near-js/providers": "^1.0.0", + "@near-wallet-selector/core": "^8.9.13", + "@near-wallet-selector/ethereum-wallets": "^8.9.13", + "@near-wallet-selector/here-wallet": "^8.9.13", + "@near-wallet-selector/modal-ui": "^8.9.13", + "@near-wallet-selector/my-near-wallet": "^8.9.13", + "@web3modal/wagmi": "^5.1.9", "bootstrap": "^5", "bootstrap-icons": "^1.11.3", - "near-api-js": "^4.0.3", - "next": "14.2.3", + "near-api-js": "^5", + "next": "14.2.13", "react": "^18", - "react-dom": "^18" + "wagmi": "^2.12.14" }, "resolutions": { "near-api-js": "4.0.3" }, "devDependencies": { - "eslint": "^8", - "eslint-config-next": "14.2.3" + "encoding": "^0.1.13", + "eslint": "^9", + "eslint-config-next": "14.2.13", + "pino-pretty": "^11.2.2" } } diff --git a/frontend/src/config.js b/frontend/src/config.js index c97f8cb..eb0ffd9 100644 --- a/frontend/src/config.js +++ b/frontend/src/config.js @@ -3,5 +3,22 @@ const contractPerNetwork = { testnet: 'hello.near-examples.testnet', }; +// Chains for EVM Wallets +const evmWalletChains = { + mainnet: { + chainId: 397, + name: "Near Mainnet", + explorer: "https://eth-explorer.near.org", + rpc: "https://eth-rpc.mainnet.near.org", + }, + testnet: { + chainId: 398, + name: "Near Testnet", + explorer: "https://eth-explorer-testnet.near.org", + rpc: "https://eth-rpc.testnet.near.org", + }, +} + export const NetworkId = 'testnet'; -export const HelloNearContract = contractPerNetwork[NetworkId]; \ No newline at end of file +export const HelloNearContract = contractPerNetwork[NetworkId]; +export const EVMWalletChain = evmWalletChains[NetworkId]; \ No newline at end of file diff --git a/frontend/src/pages/_app.js b/frontend/src/pages/_app.js index 234f7e9..6294258 100644 --- a/frontend/src/pages/_app.js +++ b/frontend/src/pages/_app.js @@ -5,9 +5,9 @@ import { NearContext } from '@/context'; import { Navigation } from '@/components/navigation'; import { Wallet } from '@/wallets/near'; -import { NetworkId, HelloNearContract } from '@/config'; +import { NetworkId } from '@/config'; -const wallet = new Wallet({ createAccessKeyFor: HelloNearContract, networkId: NetworkId }); +const wallet = new Wallet({ networkId: NetworkId }); export default function MyApp({ Component, pageProps }) { const [signedAccountId, setSignedAccountId] = useState(''); diff --git a/frontend/src/pages/hello-near/index.js b/frontend/src/pages/hello-near/index.js index 03bf9db..055d42e 100644 --- a/frontend/src/pages/hello-near/index.js +++ b/frontend/src/pages/hello-near/index.js @@ -30,9 +30,11 @@ export default function HelloNear() { const saveGreeting = async () => { setShowSpinner(true); - await wallet.callMethod({ contractId: CONTRACT, method: 'set_greeting', args: { greeting: newGreeting } }); - const greeting = await wallet.viewMethod({ contractId: CONTRACT, method: 'get_greeting' }); - setGreeting(greeting); + try { + await wallet.callMethod({ contractId: CONTRACT, method: 'set_greeting', args: { greeting: newGreeting } }); + const greeting = await wallet.viewMethod({ contractId: CONTRACT, method: 'get_greeting' }); + setGreeting(greeting); + } catch (e) { console.error(e); } setShowSpinner(false); }; diff --git a/frontend/src/wallets/near.js b/frontend/src/wallets/near.js index f620195..0db76bf 100644 --- a/frontend/src/wallets/near.js +++ b/frontend/src/wallets/near.js @@ -9,6 +9,9 @@ import { setupWalletSelector } from '@near-wallet-selector/core'; import { setupHereWallet } from '@near-wallet-selector/here-wallet'; import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet'; +import { wagmiConfig, web3Modal } from '@/wallets/web3modal'; +import { setupEthereumWallets } from "@near-wallet-selector/ethereum-wallets"; + const THIRTY_TGAS = '30000000000000'; const NO_DEPOSIT = '0'; @@ -17,9 +20,9 @@ export class Wallet { * @constructor * @param {Object} options - the options for the wallet * @param {string} options.networkId - the network id to connect to - * @param {string} options.createAccessKeyFor - the contract to create an access key for + * @param {string} options.createAccessKeyFor - (optional) create a function call key for a contract * @example - * const wallet = new Wallet({ networkId: 'testnet', createAccessKeyFor: 'contractId' }); + * const wallet = new Wallet({ networkId: 'testnet'}); * wallet.startUp((signedAccountId) => console.log(signedAccountId)); */ constructor({ networkId = 'testnet', createAccessKeyFor = undefined }) { @@ -33,9 +36,14 @@ export class Wallet { * @returns {Promise} - the accountId of the signed-in user */ startUp = async (accountChangeHook) => { + const alwaysOnboardDuringSignIn = true; this.selector = setupWalletSelector({ network: this.networkId, - modules: [setupMyNearWallet(), setupHereWallet()] + modules: [ + setupMyNearWallet(), + setupHereWallet(), + setupEthereumWallets({ wagmiConfig, web3Modal, alwaysOnboardDuringSignIn: true }), + ] }); const walletSelector = await this.selector; diff --git a/frontend/src/wallets/web3modal.js b/frontend/src/wallets/web3modal.js new file mode 100644 index 0000000..48fe8f3 --- /dev/null +++ b/frontend/src/wallets/web3modal.js @@ -0,0 +1,45 @@ +import { NetworkId, EVMWalletChain } from '@/config'; +import { reconnect, http, createConfig } from "@wagmi/core"; +import { walletConnect, injected } from "@wagmi/connectors"; +import { createWeb3Modal } from "@web3modal/wagmi"; + +// Config +const near = { + id: EVMWalletChain.chainId, + name: EVMWalletChain.name, + nativeCurrency: { + decimals: 18, + name: "NEAR", + symbol: "NEAR", + }, + rpcUrls: { + default: { http: [EVMWalletChain.rpc] }, + public: { http: [EVMWalletChain.rpc] }, + }, + blockExplorers: { + default: { + name: "NEAR Explorer", + url: EVMWalletChain.explorer, + }, + }, + testnet: NetworkId === "testnet", +}; + +// Get projectId from https://cloud.reown.com +// check https://docs.reown.com/cloud/relay for more details +const projectId = 'YOUR_PROJECT_ID'; + +export const wagmiConfig = createConfig({ + chains: [near], + transports: { [near.id]: http() }, + connectors: [ + walletConnect({ projectId, showQrModal: false }), + injected({ shimDisconnect: true }), + ], +}); + +// Preserve login state on page reload +reconnect(wagmiConfig); + +// Modal for login +export const web3Modal = createWeb3Modal({ wagmiConfig, projectId }); \ No newline at end of file