diff --git a/docs/developers.md b/docs/developers.md index 3fb259a..acb114f 100644 --- a/docs/developers.md +++ b/docs/developers.md @@ -12,7 +12,8 @@ The provider customizes few json-rpc methods like `eth_sendTransaction`, `eth_re ```typescript const oldProvider = window.ethereum; const provider = await import("provider"); -await provider.inject(oracleUrl, nodeUrl).setup(); +const injectedProvider = provider.inject(oracleUrl, nodeUrl); +await injectedProvider.setup(); ``` To switch back to the old provider, for example the MetaMask. @@ -21,6 +22,10 @@ To switch back to the old provider, for example the MetaMask. window.ethereum = oldProvider; ``` +Note that there is no guarantee another wallet doesn't come and replace the `window.ethereum` object, so you cannot assume that it always points to the WSC provider. Therefore, to keep a reference to the new WSC provider, you have two options: +1. Keep a reference to the new provider (`injectedProvider`) +2. Use [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) to query the provider at a later date + ## Actor Address Under the hood the protocol creates an account abstraction called `Actor` that is bound to the specific L1 address. The actor is deployed on the layer 2 and is used to execute transactions on behalf of the user. The actor address is derived from the l1 address using the `CREATE2` opcode. @@ -46,11 +51,12 @@ import { ethers } from "ethers"; import { Blockfrost, Lucid } from "lucid-cardano"; const milkomedaProvider = await import("provider"); -await milkomedaProvider.inject(oracleUrl, nodeUrl).setup(); +const injectedProvider = milkomedaProvider.inject(oracleUrl, nodeUrl); +await injectedProvider.setup(); const amount = 10; -const provider = new ethers.providers.Web3Provider(window.ethereum); +const provider = new ethers.providers.Web3Provider(injectedProvider); const signer = provider.getSigner(); const actorAddress = await signer.getAddress(); @@ -79,11 +85,12 @@ const txHash = await signedTx.submit(); import { ethers } from "ethers"; const milkomedaProvider = await import("provider"); -await milkomedaProvider.inject(oracleUrl, nodeUrl).setup(); +const injectedProvider = milkomedaProvider.inject(oracleUrl, nodeUrl); +await injectedProvider.setup(); const amount = 10; -const provider = new ethers.providers.Web3Provider(window.ethereum); +const provider = new ethers.providers.Web3Provider(injectedProvider); const signer = provider.getSigner(); @@ -103,11 +110,12 @@ const receipt = await tx.wait(); import { ethers } from "ethers"; const milkomedaProvider = await import("provider"); -await milkomedaProvider.inject(oracleUrl, nodeUrl).setup(); +const injectedProvider = milkomedaProvider.inject(oracleUrl, nodeUrl); +await injectedProvider.setup(); const amount = 10; -const provider = new ethers.providers.Web3Provider(window.ethereum); +const provider = new ethers.providers.Web3Provider(injectedProvider); const signer = provider.getSigner(); diff --git a/packages/dapp-example/src/App.tsx b/packages/dapp-example/src/App.tsx index 9d00667..1178e65 100644 --- a/packages/dapp-example/src/App.tsx +++ b/packages/dapp-example/src/App.tsx @@ -1,4 +1,5 @@ import { ethers } from "ethers"; +import type { MilkomedaProvider } from "milkomeda-wsc-provider"; import React from "react"; import { Link } from "react-router-dom"; @@ -6,26 +7,30 @@ import { Link } from "react-router-dom"; const COUNTER_ADDRESS = "0000000000000000000000000000000000222222"; const App = () => { + const [injectedProvider, setInjectedProvider] = React.useState(); const injectCardano = async () => { const provider = await import("milkomeda-wsc-provider"); - await provider - .injectCardano("http://localhost:8080", "https://rpc-devnet-cardano-evm.c1.milkomeda.com") - .setup(1); + const injectedProvider = provider + .injectCardano("http://localhost:8080", "https://rpc-devnet-cardano-evm.c1.milkomeda.com"); + setInjectedProvider(injectedProvider); + await injectedProvider.setup(1); alert("Injected"); }; const injectAlgorand = async () => { const provider = await import("milkomeda-wsc-provider"); - await provider - .injectAlgorand("http://localhost:8080", "https://rpc-devnet-cardano-evm.c1.milkomeda.com") - .setup(); + const injectedProvider = provider + .injectAlgorand("http://localhost:8080", "https://rpc-devnet-cardano-evm.c1.milkomeda.com"); + setInjectedProvider(injectedProvider); + await injectedProvider.setup(); alert("Injected"); }; const eth_requestAccounts = async () => { - const result = (await window.ethereum.request({ + if (injectedProvider == null) alert("Provider not injected"); + const result = (await injectedProvider.request({ method: "eth_requestAccounts", params: [], })) as string[]; @@ -34,26 +39,30 @@ const App = () => { }; const eth_accounts = async () => { - const provider = new ethers.providers.Web3Provider(window.ethereum); + if (injectedProvider == null) alert("Provider not injected"); + const provider = new ethers.providers.Web3Provider(injectedProvider); const accounts = await provider.listAccounts(); alert(accounts); }; const eth_blockNumber = async () => { - const provider = new ethers.providers.Web3Provider(window.ethereum); + if (injectedProvider == null) alert("Provider not injected"); + const provider = new ethers.providers.Web3Provider(injectedProvider); const blockNumber = await provider.getBlockNumber(); alert(blockNumber); }; const eth_getBalance = async () => { - const provider = new ethers.providers.Web3Provider(window.ethereum); + if (injectedProvider == null) alert("Provider not injected"); + const provider = new ethers.providers.Web3Provider(injectedProvider); const signer = provider.getSigner(); const balance = await provider.getBalance(signer.getAddress()); alert(ethers.utils.formatEther(balance)); }; const sendEther = async () => { - const provider = new ethers.providers.Web3Provider(window.ethereum); + if (injectedProvider == null) alert("Provider not injected"); + const provider = new ethers.providers.Web3Provider(injectedProvider); const signer = provider.getSigner(); const receiverAddress = prompt("Receiver address"); @@ -69,7 +78,8 @@ const App = () => { }; const getCounter = async () => { - const provider = new ethers.providers.Web3Provider(window.ethereum); + if (injectedProvider == null) alert("Provider not injected"); + const provider = new ethers.providers.Web3Provider(injectedProvider); const counter = new ethers.Contract( COUNTER_ADDRESS, ["function count() view returns (uint256)", "function increment(uint256)"], @@ -80,7 +90,8 @@ const App = () => { }; const incrementCounter = async () => { - const provider = new ethers.providers.Web3Provider(window.ethereum); + if (injectedProvider == null) alert("Provider not injected"); + const provider = new ethers.providers.Web3Provider(injectedProvider); const counter = new ethers.Contract( COUNTER_ADDRESS, ["function count() view returns (uint256)", "function increment(uint256)"], diff --git a/packages/dapp-example/src/Swap.tsx b/packages/dapp-example/src/Swap.tsx index 830ca7a..11b27c5 100644 --- a/packages/dapp-example/src/Swap.tsx +++ b/packages/dapp-example/src/Swap.tsx @@ -4,6 +4,7 @@ import React, { useEffect } from "react"; import { Link } from "react-router-dom"; import { default as bridgeArtifact } from "./contracts/bridge_abi_v1.json"; import { bech32ToHexAddress, hexToBytes } from "./utils"; +import type { MilkomedaProvider } from "milkomeda-wsc-provider"; let cml: typeof import("@dcspark/cardano-multiplatform-lib-browser"); const JSON_RPC_URL = "https://rpc-devnet-cardano-evm.c1.milkomeda.com"; @@ -14,18 +15,21 @@ const BLOCKFROST_API_KEY = process.env.BLOCKFROST_KEY; const BRIDGE_ADDRESS = "0x319f10d19e21188ecF58b9a146Ab0b2bfC894648"; -const inject = async () => { +const inject = async (): Promise => { const provider = await import("milkomeda-wsc-provider"); - await provider.injectCardano(ORACLE_URL, JSON_RPC_URL).setup(); + const cardanoProvider = provider.injectCardano(ORACLE_URL, JSON_RPC_URL); + await cardanoProvider.setup(); cml = await import("@dcspark/cardano-multiplatform-lib-browser"); + + return cardanoProvider; }; export const getLucid = async (key: string) => Lucid.new(new Blockfrost("https://cardano-preprod.blockfrost.io/api/v0", key), "Preprod"); -const wrap = async () => { - const provider = new ethers.providers.Web3Provider(window.ethereum); +const wrap = async (injectedProvider: MilkomedaProvider) => { + const provider = new ethers.providers.Web3Provider(injectedProvider); const lucid = await getLucid(BLOCKFROST_API_KEY); @@ -52,8 +56,8 @@ const wrap = async () => { alert("Wrapping submitted"); }; -const swap = async () => { - const provider = new ethers.providers.Web3Provider(window.ethereum); +const swap = async (injectedProvider: MilkomedaProvider) => { + const provider = new ethers.providers.Web3Provider(injectedProvider); const swapContract = new ethers.Contract( "0xAE84Ee320C66F2E1f984e28cACa37492f853FC6a", @@ -90,8 +94,8 @@ const swap = async () => { console.log("Swapped"); }; -const unwrap = async () => { - const provider = new ethers.providers.Web3Provider(window.ethereum); +const unwrap = async (injectedProvider: MilkomedaProvider) => { + const provider = new ethers.providers.Web3Provider(injectedProvider); const tokenContract = new ethers.Contract( "0x5fA38625dbd065B3e336e7ef627B06a8e6090e8F", @@ -135,8 +139,9 @@ const unwrap = async () => { }; const App = () => { + const [injectedProvider, setInjectedProvider] = React.useState(); useEffect(() => { - inject().catch(console.error); + inject().then(provider => { setInjectedProvider(provider); }).catch(console.error); }, []); return ( @@ -146,13 +151,13 @@ const App = () => {

Swap

- +
- +
- +
); diff --git a/packages/milkomeda-wsc/src/wscLib.ts b/packages/milkomeda-wsc/src/wscLib.ts index aa804ab..93bc168 100644 --- a/packages/milkomeda-wsc/src/wscLib.ts +++ b/packages/milkomeda-wsc/src/wscLib.ts @@ -174,8 +174,8 @@ export class WSCLib { } async eth_requestAccounts(): Promise { - if (window.ethereum == null) throw new Error("Provider not loaded"); - const result = (await window.ethereum.request({ + if (this.wscProvider == null) throw new Error("Provider not loaded"); + const result = (await this.wscProvider.request({ method: "eth_requestAccounts", params: [], })) as string[]; @@ -196,8 +196,8 @@ export class WSCLib { } async getEthersProvider(): Promise { - if (!window.ethereum) throw "Provider not loaded"; - const provider = new ethers.providers.Web3Provider(window.ethereum); + if (!this.wscProvider) throw "Provider not loaded"; + const provider = new ethers.providers.Web3Provider(this.wscProvider); if (this.isAlgorand()) { // eslint-disable-next-line @typescript-eslint/no-explicit-any (provider.provider as any).peraWallet.chainId = this.peraWallet?.chainId;