@multiplatform.one/web3 7.31.1 → 7.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -0
- package/dist/cjs/clients/evm.cjs +52 -1
- package/dist/cjs/clients/evm.native.js +56 -1
- package/dist/cjs/clients/evm.native.js.map +1 -1
- package/dist/cjs/clients/hooks.cjs +5 -2
- package/dist/cjs/clients/hooks.native.js +7 -2
- package/dist/cjs/clients/hooks.native.js.map +1 -1
- package/dist/cjs/clients/index.cjs +4 -0
- package/dist/cjs/clients/index.native.js +4 -0
- package/dist/cjs/clients/index.native.js.map +1 -1
- package/dist/cjs/clients/preparedSend.cjs +96 -0
- package/dist/cjs/clients/preparedSend.native.js +225 -0
- package/dist/cjs/clients/preparedSend.native.js.map +1 -0
- package/dist/cjs/connectkit/connectors/metaMaskConnectConnector.cjs +108 -27
- package/dist/cjs/connectkit/connectors/metaMaskConnectConnector.native.js +105 -28
- package/dist/cjs/connectkit/connectors/metaMaskConnectConnector.native.js.map +1 -1
- package/dist/cjs/connectkit/index.cjs +2 -0
- package/dist/cjs/connectkit/index.native.js +2 -0
- package/dist/cjs/connectkit/index.native.js.map +1 -1
- package/dist/cjs/connectkit/openConnector/context.cjs +16 -3
- package/dist/cjs/connectkit/openConnector/context.native.js +19 -4
- package/dist/cjs/connectkit/openConnector/context.native.js.map +1 -1
- package/dist/cjs/connectkit/openConnector/launcher.cjs +2 -2
- package/dist/cjs/connectkit/openConnector/launcher.native.js +2 -2
- package/dist/cjs/connectkit/openConnector/launcher.native.js.map +1 -1
- package/dist/cjs/phantom/evm.cjs +133 -0
- package/dist/cjs/phantom/evm.native.js +269 -0
- package/dist/cjs/phantom/evm.native.js.map +1 -0
- package/dist/cjs/phantom/index.cjs +36 -0
- package/dist/cjs/phantom/index.native.js +39 -0
- package/dist/cjs/phantom/index.native.js.map +1 -0
- package/dist/cjs/phantom/urls.cjs +33 -0
- package/dist/cjs/phantom/urls.native.js +38 -0
- package/dist/cjs/phantom/urls.native.js.map +1 -0
- package/dist/cjs/wagmi/driver.cjs +13 -9
- package/dist/cjs/wagmi/driver.native.js +17 -9
- package/dist/cjs/wagmi/driver.native.js.map +1 -1
- package/dist/esm/clients/evm.mjs +53 -2
- package/dist/esm/clients/evm.mjs.map +1 -1
- package/dist/esm/clients/evm.native.js +57 -2
- package/dist/esm/clients/evm.native.js.map +1 -1
- package/dist/esm/clients/hooks.mjs +5 -2
- package/dist/esm/clients/hooks.mjs.map +1 -1
- package/dist/esm/clients/hooks.native.js +7 -2
- package/dist/esm/clients/hooks.native.js.map +1 -1
- package/dist/esm/clients/index.mjs +2 -1
- package/dist/esm/clients/index.mjs.map +1 -1
- package/dist/esm/clients/index.native.js +2 -1
- package/dist/esm/clients/index.native.js.map +1 -1
- package/dist/esm/clients/preparedSend.mjs +69 -0
- package/dist/esm/clients/preparedSend.mjs.map +1 -0
- package/dist/esm/clients/preparedSend.native.js +195 -0
- package/dist/esm/clients/preparedSend.native.js.map +1 -0
- package/dist/esm/connectkit/connectors/metaMaskConnectConnector.mjs +107 -28
- package/dist/esm/connectkit/connectors/metaMaskConnectConnector.mjs.map +1 -1
- package/dist/esm/connectkit/connectors/metaMaskConnectConnector.native.js +104 -29
- package/dist/esm/connectkit/connectors/metaMaskConnectConnector.native.js.map +1 -1
- package/dist/esm/connectkit/index.mjs +2 -2
- package/dist/esm/connectkit/index.mjs.map +1 -1
- package/dist/esm/connectkit/index.native.js +2 -2
- package/dist/esm/connectkit/index.native.js.map +1 -1
- package/dist/esm/connectkit/openConnector/context.mjs +17 -4
- package/dist/esm/connectkit/openConnector/context.mjs.map +1 -1
- package/dist/esm/connectkit/openConnector/context.native.js +20 -5
- package/dist/esm/connectkit/openConnector/context.native.js.map +1 -1
- package/dist/esm/connectkit/openConnector/launcher.mjs +2 -2
- package/dist/esm/connectkit/openConnector/launcher.mjs.map +1 -1
- package/dist/esm/connectkit/openConnector/launcher.native.js +2 -2
- package/dist/esm/connectkit/openConnector/launcher.native.js.map +1 -1
- package/dist/esm/phantom/evm.mjs +103 -0
- package/dist/esm/phantom/evm.mjs.map +1 -0
- package/dist/esm/phantom/evm.native.js +236 -0
- package/dist/esm/phantom/evm.native.js.map +1 -0
- package/dist/esm/phantom/index.mjs +4 -0
- package/dist/esm/phantom/index.mjs.map +1 -0
- package/dist/esm/phantom/index.native.js +4 -0
- package/dist/esm/phantom/index.native.js.map +1 -0
- package/dist/esm/phantom/urls.mjs +7 -0
- package/dist/esm/phantom/urls.mjs.map +1 -0
- package/dist/esm/phantom/urls.native.js +9 -0
- package/dist/esm/phantom/urls.native.js.map +1 -0
- package/dist/esm/wagmi/driver.mjs +14 -10
- package/dist/esm/wagmi/driver.mjs.map +1 -1
- package/dist/esm/wagmi/driver.native.js +18 -10
- package/dist/esm/wagmi/driver.native.js.map +1 -1
- package/dist/jsx/clients/evm.mjs +53 -2
- package/dist/jsx/clients/evm.mjs.map +1 -1
- package/dist/jsx/clients/evm.native.js +56 -1
- package/dist/jsx/clients/evm.native.js.map +1 -1
- package/dist/jsx/clients/hooks.mjs +5 -2
- package/dist/jsx/clients/hooks.mjs.map +1 -1
- package/dist/jsx/clients/hooks.native.js +7 -2
- package/dist/jsx/clients/hooks.native.js.map +1 -1
- package/dist/jsx/clients/index.mjs +2 -1
- package/dist/jsx/clients/index.mjs.map +1 -1
- package/dist/jsx/clients/index.native.js +4 -0
- package/dist/jsx/clients/index.native.js.map +1 -1
- package/dist/jsx/clients/preparedSend.mjs +69 -0
- package/dist/jsx/clients/preparedSend.mjs.map +1 -0
- package/dist/jsx/clients/preparedSend.native.js +225 -0
- package/dist/jsx/clients/preparedSend.native.js.map +1 -0
- package/dist/jsx/connectkit/connectors/metaMaskConnectConnector.mjs +107 -28
- package/dist/jsx/connectkit/connectors/metaMaskConnectConnector.mjs.map +1 -1
- package/dist/jsx/connectkit/connectors/metaMaskConnectConnector.native.js +105 -28
- package/dist/jsx/connectkit/connectors/metaMaskConnectConnector.native.js.map +1 -1
- package/dist/jsx/connectkit/index.mjs +2 -2
- package/dist/jsx/connectkit/index.mjs.map +1 -1
- package/dist/jsx/connectkit/index.native.js +2 -0
- package/dist/jsx/connectkit/index.native.js.map +1 -1
- package/dist/jsx/connectkit/openConnector/context.mjs +17 -4
- package/dist/jsx/connectkit/openConnector/context.mjs.map +1 -1
- package/dist/jsx/connectkit/openConnector/context.native.js +19 -4
- package/dist/jsx/connectkit/openConnector/context.native.js.map +1 -1
- package/dist/jsx/connectkit/openConnector/launcher.mjs +2 -2
- package/dist/jsx/connectkit/openConnector/launcher.mjs.map +1 -1
- package/dist/jsx/connectkit/openConnector/launcher.native.js +2 -2
- package/dist/jsx/connectkit/openConnector/launcher.native.js.map +1 -1
- package/dist/jsx/phantom/evm.mjs +103 -0
- package/dist/jsx/phantom/evm.mjs.map +1 -0
- package/dist/jsx/phantom/evm.native.js +269 -0
- package/dist/jsx/phantom/evm.native.js.map +1 -0
- package/dist/jsx/phantom/index.mjs +4 -0
- package/dist/jsx/phantom/index.mjs.map +1 -0
- package/dist/jsx/phantom/index.native.js +39 -0
- package/dist/jsx/phantom/index.native.js.map +1 -0
- package/dist/jsx/phantom/urls.mjs +7 -0
- package/dist/jsx/phantom/urls.mjs.map +1 -0
- package/dist/jsx/phantom/urls.native.js +38 -0
- package/dist/jsx/phantom/urls.native.js.map +1 -0
- package/dist/jsx/wagmi/driver.mjs +14 -10
- package/dist/jsx/wagmi/driver.mjs.map +1 -1
- package/dist/jsx/wagmi/driver.native.js +17 -9
- package/dist/jsx/wagmi/driver.native.js.map +1 -1
- package/dist/keycloak/login.iife.js +382 -382
- package/package.json +12 -6
- package/src/clients/evm.ts +86 -0
- package/src/clients/hooks.spec.tsx +1 -0
- package/src/clients/hooks.ts +4 -1
- package/src/clients/index.ts +12 -0
- package/src/clients/preparedSend.spec.ts +214 -0
- package/src/clients/preparedSend.ts +136 -0
- package/src/connectkit/connectors/metaMaskConnectConnector.spec.ts +256 -4
- package/src/connectkit/connectors/metaMaskConnectConnector.ts +145 -33
- package/src/connectkit/index.ts +2 -0
- package/src/connectkit/openConnector/context.tsx +10 -5
- package/src/connectkit/openConnector/launcher.spec.ts +24 -2
- package/src/connectkit/openConnector/launcher.ts +10 -3
- package/src/core/types.ts +7 -0
- package/src/phantom/evm.spec.ts +195 -0
- package/src/phantom/evm.ts +163 -0
- package/src/phantom/index.ts +16 -0
- package/src/phantom/urls.spec.ts +29 -0
- package/src/phantom/urls.ts +16 -0
- package/src/wagmi/driver.spec.ts +31 -0
- package/src/wagmi/driver.ts +11 -4
- package/types/clients/evm.d.ts +32 -0
- package/types/clients/evm.d.ts.map +1 -1
- package/types/clients/hooks.d.ts.map +1 -1
- package/types/clients/index.d.ts +2 -1
- package/types/clients/index.d.ts.map +1 -1
- package/types/clients/preparedSend.d.ts +71 -0
- package/types/clients/preparedSend.d.ts.map +1 -0
- package/types/connectkit/connectors/metaMaskConnectConnector.d.ts +27 -0
- package/types/connectkit/connectors/metaMaskConnectConnector.d.ts.map +1 -1
- package/types/connectkit/index.d.ts +1 -1
- package/types/connectkit/index.d.ts.map +1 -1
- package/types/connectkit/openConnector/context.d.ts.map +1 -1
- package/types/connectkit/openConnector/launcher.d.ts +8 -1
- package/types/connectkit/openConnector/launcher.d.ts.map +1 -1
- package/types/core/types.d.ts +7 -0
- package/types/core/types.d.ts.map +1 -1
- package/types/phantom/evm.d.ts +76 -0
- package/types/phantom/evm.d.ts.map +1 -0
- package/types/phantom/index.d.ts +3 -0
- package/types/phantom/index.d.ts.map +1 -0
- package/types/phantom/urls.d.ts +11 -0
- package/types/phantom/urls.d.ts.map +1 -0
- package/types/wagmi/driver.d.ts +1 -1
- package/types/wagmi/driver.d.ts.map +1 -1
|
@@ -32,6 +32,13 @@ export type OpenConnectorOptions = {
|
|
|
32
32
|
* to settle however long that takes.
|
|
33
33
|
*/
|
|
34
34
|
fallbackMs?: number;
|
|
35
|
+
/**
|
|
36
|
+
* The chain to connect on. MetaMask asks the wallet for it first, so a page
|
|
37
|
+
* that knows its purchase chain connects there and needs no switch after.
|
|
38
|
+
* Must be one of the config's chains; a wallet that does not grant it
|
|
39
|
+
* connects on another, which the connect result reports.
|
|
40
|
+
*/
|
|
41
|
+
chainId?: number;
|
|
35
42
|
};
|
|
36
43
|
|
|
37
44
|
export type PendingConnect = {
|
|
@@ -45,7 +52,7 @@ export type PendingConnect = {
|
|
|
45
52
|
type Listener = (event: Event) => void;
|
|
46
53
|
|
|
47
54
|
export type LauncherDeps = {
|
|
48
|
-
connect: (connector: Connector) => Promise<unknown>;
|
|
55
|
+
connect: (connector: Connector, chainId?: number) => Promise<unknown>;
|
|
49
56
|
/** Show the connecting state for this wallet. */
|
|
50
57
|
showConnecting: (walletId: string) => void;
|
|
51
58
|
/** Show the wallet list. */
|
|
@@ -103,7 +110,7 @@ export function createConnectorLauncher(getDeps: () => LauncherDeps): ConnectorL
|
|
|
103
110
|
|
|
104
111
|
const current = session;
|
|
105
112
|
if (current && current.pending.connector.uid === connector.uid) {
|
|
106
|
-
deps.connect(connector).then(
|
|
113
|
+
deps.connect(connector, options.chainId).then(
|
|
107
114
|
() => end(current),
|
|
108
115
|
() => undefined,
|
|
109
116
|
);
|
|
@@ -173,7 +180,7 @@ export function createConnectorLauncher(getDeps: () => LauncherDeps): ConnectorL
|
|
|
173
180
|
};
|
|
174
181
|
|
|
175
182
|
connector.emitter?.on?.("message", onMessage);
|
|
176
|
-
deps.connect(connector).then(
|
|
183
|
+
deps.connect(connector, options.chainId).then(
|
|
177
184
|
() => end(s),
|
|
178
185
|
(error: unknown) => fallBack(error ?? new Error("connect failed")),
|
|
179
186
|
);
|
package/src/core/types.ts
CHANGED
|
@@ -29,6 +29,13 @@ export interface DriverState {
|
|
|
29
29
|
export interface DriverConnectOptions {
|
|
30
30
|
/** Called with a pairing URI when the connector hands off to another app or device. */
|
|
31
31
|
onDisplayUri?: (uri: string) => void;
|
|
32
|
+
/**
|
|
33
|
+
* EVM: the chain to connect on, asked of the wallet first so no switch is
|
|
34
|
+
* needed after. A chain the config does not list rejects before the wallet
|
|
35
|
+
* is asked. Ignored by drivers without EVM chains, and by a connector the
|
|
36
|
+
* driver already holds open.
|
|
37
|
+
*/
|
|
38
|
+
chainId?: number;
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
/** What the connection store's `connect` takes. */
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { baseSepolia } from "viem/chains";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
connectPhantomEvm,
|
|
5
|
+
discoverPhantomEthereumProvider,
|
|
6
|
+
openInPhantom,
|
|
7
|
+
phantomEthereumProvider,
|
|
8
|
+
phantomEvmRoute,
|
|
9
|
+
PhantomNotInjectedError,
|
|
10
|
+
type PhantomEthereumProvider,
|
|
11
|
+
} from "./evm";
|
|
12
|
+
|
|
13
|
+
const account = "0x1111111111111111111111111111111111111111";
|
|
14
|
+
|
|
15
|
+
function fakeProvider(chainId: number, opts: { unknownChain?: boolean } = {}) {
|
|
16
|
+
let current = chainId;
|
|
17
|
+
let known = !opts.unknownChain;
|
|
18
|
+
const calls: { method: string; params?: unknown }[] = [];
|
|
19
|
+
const request = vi.fn(async ({ method, params }: { method: string; params?: unknown }) => {
|
|
20
|
+
calls.push({ method, params });
|
|
21
|
+
switch (method) {
|
|
22
|
+
case "eth_requestAccounts":
|
|
23
|
+
case "eth_accounts":
|
|
24
|
+
return [account];
|
|
25
|
+
case "eth_chainId":
|
|
26
|
+
return `0x${current.toString(16)}`;
|
|
27
|
+
case "wallet_switchEthereumChain": {
|
|
28
|
+
if (!known) throw Object.assign(new Error("Unrecognized chain"), { code: 4902 });
|
|
29
|
+
current = Number((params as [{ chainId: string }])[0].chainId);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
case "wallet_addEthereumChain":
|
|
33
|
+
known = true;
|
|
34
|
+
return null;
|
|
35
|
+
default:
|
|
36
|
+
throw new Error(`unexpected ${method}`);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const provider = { isPhantom: true, request, on: vi.fn(), removeListener: vi.fn() };
|
|
40
|
+
return { provider: provider as unknown as PhantomEthereumProvider, calls };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("phantom evm", () => {
|
|
44
|
+
it("finds window.phantom.ethereum, then a Phantom window.ethereum, and nothing else", () => {
|
|
45
|
+
const own = { isPhantom: true } as unknown as PhantomEthereumProvider;
|
|
46
|
+
const other = { isMetaMask: true } as unknown as PhantomEthereumProvider;
|
|
47
|
+
expect(phantomEthereumProvider({ phantom: { ethereum: own }, ethereum: other })).toBe(own);
|
|
48
|
+
expect(phantomEthereumProvider({ ethereum: own })).toBe(own);
|
|
49
|
+
expect(phantomEthereumProvider({ ethereum: other })).toBeUndefined();
|
|
50
|
+
expect(phantomEvmRoute({ ethereum: other })).toBe("browse");
|
|
51
|
+
expect(phantomEvmRoute({ phantom: { ethereum: own } })).toBe("injected");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("takes Phantom's EIP-6963 announcement", async () => {
|
|
55
|
+
const provider = { isPhantom: true } as unknown as PhantomEthereumProvider;
|
|
56
|
+
const target = new EventTarget();
|
|
57
|
+
target.addEventListener("eip6963:requestProvider", () => {
|
|
58
|
+
target.dispatchEvent(
|
|
59
|
+
Object.assign(new Event("eip6963:announceProvider"), {
|
|
60
|
+
detail: { info: { rdns: "io.metamask" }, provider: {} },
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
target.dispatchEvent(
|
|
64
|
+
Object.assign(new Event("eip6963:announceProvider"), {
|
|
65
|
+
detail: { info: { rdns: "app.phantom" }, provider },
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
const win = {
|
|
70
|
+
addEventListener: target.addEventListener.bind(target),
|
|
71
|
+
removeEventListener: target.removeEventListener.bind(target),
|
|
72
|
+
dispatchEvent: target.dispatchEvent.bind(target),
|
|
73
|
+
};
|
|
74
|
+
await expect(discoverPhantomEthereumProvider({ window: win as never })).resolves.toBe(provider);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("resolves undefined when nothing announces", async () => {
|
|
78
|
+
const target = new EventTarget();
|
|
79
|
+
const win = {
|
|
80
|
+
addEventListener: target.addEventListener.bind(target),
|
|
81
|
+
removeEventListener: target.removeEventListener.bind(target),
|
|
82
|
+
dispatchEvent: target.dispatchEvent.bind(target),
|
|
83
|
+
};
|
|
84
|
+
await expect(
|
|
85
|
+
discoverPhantomEthereumProvider({ window: win as never, timeout: 5 }),
|
|
86
|
+
).resolves.toBeUndefined();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("opens this page in Phantom's browser synchronously", () => {
|
|
90
|
+
const assign = vi.fn();
|
|
91
|
+
const link = openInPhantom({
|
|
92
|
+
window: {
|
|
93
|
+
location: {
|
|
94
|
+
href: "https://dev.promanager.vip/u/buy?mobile=1",
|
|
95
|
+
origin: "https://dev.promanager.vip",
|
|
96
|
+
assign,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
expect(link).toBe(
|
|
101
|
+
"https://phantom.com/ul/browse/https%3A%2F%2Fdev.promanager.vip%2Fu%2Fbuy%3Fmobile%3D1?ref=https%3A%2F%2Fdev.promanager.vip",
|
|
102
|
+
);
|
|
103
|
+
expect(assign).toHaveBeenCalledWith(link);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("connects, switches to Base Sepolia, and returns a client on it", async () => {
|
|
107
|
+
const { provider, calls } = fakeProvider(1);
|
|
108
|
+
const client = await connectPhantomEvm({ chain: baseSepolia, provider });
|
|
109
|
+
expect(client.address.toLowerCase()).toBe(account);
|
|
110
|
+
expect(client.chainId).toBe(84532);
|
|
111
|
+
expect(calls.map((c) => c.method)).toEqual([
|
|
112
|
+
"eth_requestAccounts",
|
|
113
|
+
"eth_chainId",
|
|
114
|
+
"wallet_switchEthereumChain",
|
|
115
|
+
]);
|
|
116
|
+
expect(calls[2].params).toEqual([{ chainId: "0x14a34" }]);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("adds the chain when Phantom does not know it", async () => {
|
|
120
|
+
const { provider, calls } = fakeProvider(1, { unknownChain: true });
|
|
121
|
+
await connectPhantomEvm({ chain: baseSepolia, provider });
|
|
122
|
+
expect(calls.map((c) => c.method)).toEqual([
|
|
123
|
+
"eth_requestAccounts",
|
|
124
|
+
"eth_chainId",
|
|
125
|
+
"wallet_switchEthereumChain",
|
|
126
|
+
"wallet_addEthereumChain",
|
|
127
|
+
"wallet_switchEthereumChain",
|
|
128
|
+
]);
|
|
129
|
+
expect((calls[3].params as [{ chainId: string; rpcUrls: string[] }])[0]).toMatchObject({
|
|
130
|
+
chainId: "0x14a34",
|
|
131
|
+
rpcUrls: [...baseSepolia.rpcUrls.default.http],
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("skips the switch when already on the chain", async () => {
|
|
136
|
+
const { provider, calls } = fakeProvider(84532);
|
|
137
|
+
await connectPhantomEvm({ chain: baseSepolia, provider });
|
|
138
|
+
expect(calls.map((c) => c.method)).toEqual(["eth_requestAccounts", "eth_chainId"]);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("sends an ERC-20 transfer to Phantom as eth_sendTransaction", async () => {
|
|
142
|
+
const { provider } = fakeProvider(84532);
|
|
143
|
+
const sent: unknown[] = [];
|
|
144
|
+
const request = provider.request as unknown as ReturnType<typeof vi.fn>;
|
|
145
|
+
const base = request.getMockImplementation() as (a: {
|
|
146
|
+
method: string;
|
|
147
|
+
params?: unknown;
|
|
148
|
+
}) => Promise<unknown>;
|
|
149
|
+
request.mockImplementation(async (args: { method: string; params?: unknown }) => {
|
|
150
|
+
if (args.method === "eth_sendTransaction") {
|
|
151
|
+
sent.push(args.params);
|
|
152
|
+
return `0x${"ab".repeat(32)}`;
|
|
153
|
+
}
|
|
154
|
+
return base(args);
|
|
155
|
+
});
|
|
156
|
+
const client = await connectPhantomEvm({ chain: baseSepolia, provider });
|
|
157
|
+
const hash = await client.walletClient.writeContract({
|
|
158
|
+
address: "0xc6fe3521644e0f5439fe345f64d372cd6ed37491",
|
|
159
|
+
abi: [
|
|
160
|
+
{
|
|
161
|
+
type: "function",
|
|
162
|
+
name: "transfer",
|
|
163
|
+
stateMutability: "nonpayable",
|
|
164
|
+
inputs: [
|
|
165
|
+
{ name: "to", type: "address" },
|
|
166
|
+
{ name: "amount", type: "uint256" },
|
|
167
|
+
],
|
|
168
|
+
outputs: [{ type: "bool" }],
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
functionName: "transfer",
|
|
172
|
+
args: ["0x2222222222222222222222222222222222222222", 10n ** 18n],
|
|
173
|
+
gas: 100000n,
|
|
174
|
+
});
|
|
175
|
+
expect(hash).toBe(`0x${"ab".repeat(32)}`);
|
|
176
|
+
expect(sent[0]).toEqual([
|
|
177
|
+
expect.objectContaining({
|
|
178
|
+
from: account,
|
|
179
|
+
to: "0xc6fe3521644e0f5439fe345f64d372cd6ed37491",
|
|
180
|
+
data: expect.stringMatching(/^0xa9059cbb0{24}2{40}/),
|
|
181
|
+
}),
|
|
182
|
+
]);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("says to reopen in Phantom when nothing is injected", async () => {
|
|
186
|
+
vi.stubGlobal("phantom", undefined);
|
|
187
|
+
try {
|
|
188
|
+
await expect(connectPhantomEvm({ chain: baseSepolia })).rejects.toBeInstanceOf(
|
|
189
|
+
PhantomNotInjectedError,
|
|
190
|
+
);
|
|
191
|
+
} finally {
|
|
192
|
+
vi.unstubAllGlobals();
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { numberToHex, type Chain, type EIP1193Provider, type PublicClient } from "viem";
|
|
2
|
+
import { evmClient, type EvmClient } from "../clients/evm";
|
|
3
|
+
import { phantomBrowseUrl } from "./urls";
|
|
4
|
+
|
|
5
|
+
export type PhantomEthereumProvider = EIP1193Provider & { isPhantom?: boolean };
|
|
6
|
+
|
|
7
|
+
interface PhantomWindow {
|
|
8
|
+
phantom?: { ethereum?: PhantomEthereumProvider };
|
|
9
|
+
ethereum?: PhantomEthereumProvider;
|
|
10
|
+
location?: { href: string; origin: string; assign(url: string): void };
|
|
11
|
+
addEventListener?: (type: string, listener: (event: Event) => void) => void;
|
|
12
|
+
removeEventListener?: (type: string, listener: (event: Event) => void) => void;
|
|
13
|
+
dispatchEvent?: (event: Event) => boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const phantomRdns = "app.phantom";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Phantom's injected EVM provider: `window.phantom.ethereum`, else a
|
|
20
|
+
* `window.ethereum` that says it is Phantom. Present in Phantom's desktop
|
|
21
|
+
* extension and its in-app browser, never in Chrome or Safari on a phone.
|
|
22
|
+
*/
|
|
23
|
+
export function phantomEthereumProvider(
|
|
24
|
+
win: PhantomWindow = globalThis as PhantomWindow,
|
|
25
|
+
): PhantomEthereumProvider | undefined {
|
|
26
|
+
const own = win.phantom?.ethereum;
|
|
27
|
+
if (own?.isPhantom) return own;
|
|
28
|
+
if (win.ethereum?.isPhantom) return win.ethereum;
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* phantomEthereumProvider, or Phantom's EIP-6963 announcement (rdns
|
|
34
|
+
* `app.phantom`) when the provider is not on `window` yet. Resolves undefined
|
|
35
|
+
* after `timeout` ms.
|
|
36
|
+
*/
|
|
37
|
+
export function discoverPhantomEthereumProvider(
|
|
38
|
+
options: { timeout?: number; window?: PhantomWindow } = {},
|
|
39
|
+
): Promise<PhantomEthereumProvider | undefined> {
|
|
40
|
+
const win = options.window ?? (globalThis as PhantomWindow);
|
|
41
|
+
const found = phantomEthereumProvider(win);
|
|
42
|
+
if (found || !win.addEventListener || !win.dispatchEvent) return Promise.resolve(found);
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
const onAnnounce = (event: Event) => {
|
|
45
|
+
const detail = (
|
|
46
|
+
event as CustomEvent<{ info?: { rdns?: string }; provider?: PhantomEthereumProvider }>
|
|
47
|
+
).detail;
|
|
48
|
+
if (detail?.info?.rdns !== phantomRdns || !detail.provider) return;
|
|
49
|
+
done(detail.provider);
|
|
50
|
+
};
|
|
51
|
+
const timer = setTimeout(() => done(phantomEthereumProvider(win)), options.timeout ?? 500);
|
|
52
|
+
const done = (provider: PhantomEthereumProvider | undefined) => {
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
win.removeEventListener?.("eip6963:announceProvider", onAnnounce);
|
|
55
|
+
resolve(provider);
|
|
56
|
+
};
|
|
57
|
+
win.addEventListener?.("eip6963:announceProvider", onAnnounce);
|
|
58
|
+
win.dispatchEvent?.(new Event("eip6963:requestProvider"));
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* How this page reaches Phantom's EVM wallet: `injected` when Phantom's
|
|
64
|
+
* provider is here (extension or Phantom's browser), `browse` when the page
|
|
65
|
+
* must first reopen inside Phantom with openInPhantom.
|
|
66
|
+
*/
|
|
67
|
+
export function phantomEvmRoute(win: PhantomWindow = globalThis as PhantomWindow) {
|
|
68
|
+
return phantomEthereumProvider(win) ? ("injected" as const) : ("browse" as const);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface OpenInPhantomOptions {
|
|
72
|
+
/** The page to open in Phantom's browser. Defaults to this page. */
|
|
73
|
+
url?: string;
|
|
74
|
+
/** The requesting app. Defaults to this page's origin. */
|
|
75
|
+
ref?: string;
|
|
76
|
+
/** Defaults to `location.assign`. */
|
|
77
|
+
navigate?: (url: string) => void;
|
|
78
|
+
base?: string;
|
|
79
|
+
window?: PhantomWindow;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Reopens the page inside Phantom's in-app browser. Call it straight from the
|
|
84
|
+
* tap handler with nothing awaited first: mobile browsers only follow a link
|
|
85
|
+
* into another app without a prompt inside the tap's user activation.
|
|
86
|
+
* Returns the link, for an `<a href>` instead.
|
|
87
|
+
*/
|
|
88
|
+
export function openInPhantom(options: OpenInPhantomOptions = {}): string {
|
|
89
|
+
const win = options.window ?? (globalThis as PhantomWindow);
|
|
90
|
+
const url = options.url ?? win.location?.href;
|
|
91
|
+
const ref = options.ref ?? win.location?.origin ?? url;
|
|
92
|
+
if (!url || !ref) throw new Error("openInPhantom: no page URL here; pass `url` and `ref`");
|
|
93
|
+
const link = phantomBrowseUrl(url, ref, { base: options.base });
|
|
94
|
+
(options.navigate ?? ((target: string) => win.location?.assign(target)))(link);
|
|
95
|
+
return link;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface ConnectPhantomEvmOptions {
|
|
99
|
+
chain: Chain;
|
|
100
|
+
/** Defaults to phantomEthereumProvider(). */
|
|
101
|
+
provider?: PhantomEthereumProvider;
|
|
102
|
+
publicClient?: PublicClient;
|
|
103
|
+
rpcUrl?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export class PhantomNotInjectedError extends Error {
|
|
107
|
+
constructor() {
|
|
108
|
+
super(
|
|
109
|
+
"Phantom's EVM provider is not on this page: on a phone, reopen it in Phantom with openInPhantom()",
|
|
110
|
+
);
|
|
111
|
+
this.name = "PhantomNotInjectedError";
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const errorCode = (error: unknown) =>
|
|
116
|
+
(error as { code?: unknown; cause?: { code?: unknown } } | undefined)?.code ??
|
|
117
|
+
(error as { cause?: { code?: unknown } } | undefined)?.cause?.code;
|
|
118
|
+
|
|
119
|
+
async function ensureChain(provider: PhantomEthereumProvider, chain: Chain) {
|
|
120
|
+
const current = await provider.request({ method: "eth_chainId" });
|
|
121
|
+
if (Number(current) === chain.id) return;
|
|
122
|
+
const chainId = numberToHex(chain.id);
|
|
123
|
+
try {
|
|
124
|
+
await provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId }] });
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (errorCode(error) !== 4902) throw error;
|
|
127
|
+
await provider.request({
|
|
128
|
+
method: "wallet_addEthereumChain",
|
|
129
|
+
params: [
|
|
130
|
+
{
|
|
131
|
+
chainId,
|
|
132
|
+
chainName: chain.name,
|
|
133
|
+
nativeCurrency: chain.nativeCurrency,
|
|
134
|
+
rpcUrls: [...chain.rpcUrls.default.http],
|
|
135
|
+
blockExplorerUrls: chain.blockExplorers ? [chain.blockExplorers.default.url] : undefined,
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
});
|
|
139
|
+
await provider.request({ method: "wallet_switchEthereumChain", params: [{ chainId }] });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Connects Phantom's injected EVM wallet, moves it to `chain`, and returns
|
|
145
|
+
* the mpo EVM client on it, so `erc20.transfer` and `writeContract` go to
|
|
146
|
+
* Phantom for approval. Testnets such as Base Sepolia need Phantom's Testnet
|
|
147
|
+
* Mode on.
|
|
148
|
+
*/
|
|
149
|
+
export async function connectPhantomEvm(options: ConnectPhantomEvmOptions): Promise<EvmClient> {
|
|
150
|
+
const provider = options.provider ?? phantomEthereumProvider();
|
|
151
|
+
if (!provider) throw new PhantomNotInjectedError();
|
|
152
|
+
const accounts = await provider.request({ method: "eth_requestAccounts" });
|
|
153
|
+
const account = accounts[0];
|
|
154
|
+
if (!account) throw new Error("Phantom returned no EVM account");
|
|
155
|
+
await ensureChain(provider, options.chain);
|
|
156
|
+
return evmClient({
|
|
157
|
+
provider,
|
|
158
|
+
account,
|
|
159
|
+
chain: options.chain,
|
|
160
|
+
publicClient: options.publicClient,
|
|
161
|
+
rpcUrl: options.rpcUrl,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// `@multiplatform.one/web3/phantom`: Phantom's EVM wallet from a phone's own
|
|
2
|
+
// browser. Phantom's deeplinks carry no EVM methods, so the page reopens in
|
|
3
|
+
// Phantom's in-app browser (openInPhantom) and connects the provider Phantom
|
|
4
|
+
// injects there (connectPhantomEvm).
|
|
5
|
+
export {
|
|
6
|
+
connectPhantomEvm,
|
|
7
|
+
discoverPhantomEthereumProvider,
|
|
8
|
+
openInPhantom,
|
|
9
|
+
phantomEthereumProvider,
|
|
10
|
+
phantomEvmRoute,
|
|
11
|
+
PhantomNotInjectedError,
|
|
12
|
+
type ConnectPhantomEvmOptions,
|
|
13
|
+
type OpenInPhantomOptions,
|
|
14
|
+
type PhantomEthereumProvider,
|
|
15
|
+
} from "./evm";
|
|
16
|
+
export { phantomBrowseUrl, phantomUniversalLinkBase } from "./urls";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { phantomBrowseUrl } from "./urls";
|
|
3
|
+
|
|
4
|
+
describe("phantomBrowseUrl", () => {
|
|
5
|
+
it("builds the browse link Phantom documents", () => {
|
|
6
|
+
expect(
|
|
7
|
+
phantomBrowseUrl(
|
|
8
|
+
"https://magiceden.io/item-details/8yjN8iRuoiYiKW487cnW9vn6mLBR5E8aCNKsBRmTP9vN",
|
|
9
|
+
"https://magiceden.io",
|
|
10
|
+
),
|
|
11
|
+
).toBe(
|
|
12
|
+
"https://phantom.com/ul/browse/https%3A%2F%2Fmagiceden.io%2Fitem-details%2F8yjN8iRuoiYiKW487cnW9vn6mLBR5E8aCNKsBRmTP9vN?ref=https%3A%2F%2Fmagiceden.io",
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("keeps a page's own query inside the browse link", () => {
|
|
17
|
+
expect(
|
|
18
|
+
phantomBrowseUrl(
|
|
19
|
+
"https://dev.promanager.vip/u/buy?mobile=1&x=a b",
|
|
20
|
+
"https://dev.promanager.vip",
|
|
21
|
+
{
|
|
22
|
+
base: "https://phantom.app/ul/",
|
|
23
|
+
},
|
|
24
|
+
),
|
|
25
|
+
).toBe(
|
|
26
|
+
"https://phantom.app/ul/browse/https%3A%2F%2Fdev.promanager.vip%2Fu%2Fbuy%3Fmobile%3D1%26x%3Da%20b?ref=https%3A%2F%2Fdev.promanager.vip",
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Phantom's universal-link host. `https://phantom.app/ul` still answers; Phantom asks new code to use this one. */
|
|
2
|
+
export const phantomUniversalLinkBase = "https://phantom.com/ul";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Opens `url` inside Phantom's in-app browser, where Phantom injects
|
|
6
|
+
* `window.phantom.ethereum`. Phantom's deeplinks carry no EVM methods, so
|
|
7
|
+
* this is how a phone's browser reaches its EVM wallet.
|
|
8
|
+
*/
|
|
9
|
+
export function phantomBrowseUrl(
|
|
10
|
+
url: string,
|
|
11
|
+
ref: string,
|
|
12
|
+
options: { base?: string } = {},
|
|
13
|
+
): string {
|
|
14
|
+
const base = (options.base ?? phantomUniversalLinkBase).replace(/\/+$/, "");
|
|
15
|
+
return `${base}/browse/${encodeURIComponent(url)}?ref=${encodeURIComponent(ref)}`;
|
|
16
|
+
}
|
package/src/wagmi/driver.spec.ts
CHANGED
|
@@ -71,6 +71,37 @@ function setup(options: { displayUri?: string } = {}) {
|
|
|
71
71
|
describeConnectionDriver("wagmiDriver", setup);
|
|
72
72
|
|
|
73
73
|
describe("wagmiDriver", () => {
|
|
74
|
+
it("connects on the chain the caller names", async () => {
|
|
75
|
+
const { driver, connector } = setup();
|
|
76
|
+
const spy = vi.spyOn(connector("wallet.one"), "connect");
|
|
77
|
+
const chainId = 8453;
|
|
78
|
+
|
|
79
|
+
await driver.connect("wallet.one", { chainId });
|
|
80
|
+
|
|
81
|
+
expect(spy).toHaveBeenCalledWith(expect.objectContaining({ chainId }));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("refuses a chain the config does not list before asking the wallet", async () => {
|
|
85
|
+
const { driver, connector, wallets } = setup();
|
|
86
|
+
const spy = vi.spyOn(connector("wallet.one"), "connect");
|
|
87
|
+
|
|
88
|
+
await expect(driver.connect("wallet.one", { chainId: 999_999 })).rejects.toThrow(
|
|
89
|
+
/Chain not configured/,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
expect(spy).not.toHaveBeenCalled();
|
|
93
|
+
expect(wallets.get("wallet.one").open).toBeFalsy();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("connects as before when no chain is named", async () => {
|
|
97
|
+
const { driver, connector } = setup();
|
|
98
|
+
const spy = vi.spyOn(connector("wallet.one"), "connect");
|
|
99
|
+
|
|
100
|
+
await driver.connect("wallet.one");
|
|
101
|
+
|
|
102
|
+
expect(spy.mock.calls[0]![0]).not.toHaveProperty("chainId", expect.anything());
|
|
103
|
+
});
|
|
104
|
+
|
|
74
105
|
it("hands a pairing URI to onDisplayUri while connecting, and stops after", async () => {
|
|
75
106
|
const { driver, config, connector } = setup({ displayUri: "metamask://wc?uri=pair" });
|
|
76
107
|
const onDisplayUri = vi.fn();
|
package/src/wagmi/driver.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { ChainNotConfiguredError, type Config, type Connection, type Connector } from "wagmi";
|
|
2
2
|
import {
|
|
3
3
|
connect,
|
|
4
|
+
getAccount,
|
|
4
5
|
getPublicClient,
|
|
5
6
|
getWalletClient,
|
|
6
7
|
switchConnection,
|
|
@@ -231,17 +232,22 @@ export function wagmiDriver(config: Config, options: WagmiDriverOptions = {}): C
|
|
|
231
232
|
},
|
|
232
233
|
async connect(connectorId, connectOptions: DriverConnectOptions = {}) {
|
|
233
234
|
const connector = connectorFor(connectorId);
|
|
235
|
+
const { onDisplayUri, chainId } = connectOptions;
|
|
236
|
+
if (chainId !== undefined && !config.chains.some((chain) => chain.id === chainId))
|
|
237
|
+
throw new ChainNotConfiguredError();
|
|
234
238
|
const open = await reuse(connector);
|
|
235
239
|
if (open) return open;
|
|
236
240
|
if (empty(connector)) forget(connector);
|
|
237
|
-
const { onDisplayUri } = connectOptions;
|
|
238
241
|
const onMessage = ({ type, data }: { type: string; data?: unknown }) => {
|
|
239
242
|
if (type === "display_uri" && typeof data === "string") onDisplayUri?.(data);
|
|
240
243
|
};
|
|
241
244
|
if (onDisplayUri) connector.emitter.on("message", onMessage);
|
|
242
245
|
try {
|
|
243
|
-
const
|
|
244
|
-
|
|
246
|
+
const connection = await connect(
|
|
247
|
+
config,
|
|
248
|
+
chainId === undefined ? { connector } : { connector, chainId },
|
|
249
|
+
);
|
|
250
|
+
return opened(connector, connection.accounts, connection.chainId);
|
|
245
251
|
} finally {
|
|
246
252
|
if (onDisplayUri) connector.emitter.off("message", onMessage);
|
|
247
253
|
}
|
|
@@ -296,6 +302,7 @@ export function wagmiDriver(config: Config, options: WagmiDriverOptions = {}): C
|
|
|
296
302
|
return evmClient({
|
|
297
303
|
walletClient: walletClient as unknown as EvmWalletClient,
|
|
298
304
|
...(publicClient ? { publicClient: publicClient as unknown as PublicClient } : {}),
|
|
305
|
+
current: () => getAccount(config),
|
|
299
306
|
});
|
|
300
307
|
}
|
|
301
308
|
}
|
package/types/clients/evm.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Abi, type Account, type Address, type Chain, type ContractFunctionArgs, type ContractFunctionName, type EIP1193Provider, type Hash, type Hex, type PublicClient, type SendTransactionParameters, type TransactionReceipt, type Transport, type WalletClient, type WriteContractParameters } from "viem";
|
|
2
|
+
import { type CurrentWalletState, type PreparedEvmSend } from "./preparedSend";
|
|
2
3
|
import type { ChainClient } from "./types";
|
|
3
4
|
/** A viem wallet client with its account and chain hoisted, the shape wagmi's getWalletClient returns. */
|
|
4
5
|
export type EvmWalletClient = WalletClient<Transport, Chain, Account>;
|
|
@@ -158,6 +159,27 @@ export interface Erc20Actions {
|
|
|
158
159
|
signal?: AbortSignal;
|
|
159
160
|
}): Promise<Hash | null>;
|
|
160
161
|
}
|
|
162
|
+
/** What `prepareTransaction` takes: the transaction, and optionally its gas, nonce and lifetime. */
|
|
163
|
+
export interface EvmPrepareTransactionParameters {
|
|
164
|
+
to: Address;
|
|
165
|
+
data?: Hex;
|
|
166
|
+
value?: bigint;
|
|
167
|
+
gas?: bigint;
|
|
168
|
+
nonce?: number;
|
|
169
|
+
/** How long the prepared send stays good. 60 s by default. */
|
|
170
|
+
maxAgeMs?: number;
|
|
171
|
+
}
|
|
172
|
+
/** What `prepareWriteContract` takes: viem's contract call, simulated before the tap. */
|
|
173
|
+
export interface EvmPrepareWriteContractParameters<abi extends Abi | readonly unknown[] = Abi, functionName extends WritableFunctionName<abi> = WritableFunctionName<abi>> {
|
|
174
|
+
address: Address;
|
|
175
|
+
abi: abi;
|
|
176
|
+
functionName: functionName;
|
|
177
|
+
args?: ContractFunctionArgs<abi, "nonpayable" | "payable", functionName>;
|
|
178
|
+
value?: bigint;
|
|
179
|
+
gas?: bigint;
|
|
180
|
+
nonce?: number;
|
|
181
|
+
maxAgeMs?: number;
|
|
182
|
+
}
|
|
161
183
|
export interface EvmClient extends ChainClient<"evm"> {
|
|
162
184
|
readonly address: Address;
|
|
163
185
|
readonly chainId: number;
|
|
@@ -189,6 +211,14 @@ export interface EvmClient extends ChainClient<"evm"> {
|
|
|
189
211
|
confirmTransaction(hash: Hash, options?: EvmWaitOptions): Promise<TransactionReceipt>;
|
|
190
212
|
/** The chain explorer's page for `hash`, when the chain names an explorer. */
|
|
191
213
|
explorerTxUrl(hash: Hash): string | undefined;
|
|
214
|
+
/**
|
|
215
|
+
* Does the network work for a send ahead of the user's tap (nonce, gas,
|
|
216
|
+
* fees), so the tap's handler calls `send()` with nothing awaited and the
|
|
217
|
+
* wallet app opens without "Continue to MetaMask?". See PreparedEvmSend.
|
|
218
|
+
*/
|
|
219
|
+
prepareTransaction(parameters: EvmPrepareTransactionParameters): Promise<PreparedEvmSend>;
|
|
220
|
+
/** prepareTransaction for a contract call: simulates it, then encodes it. */
|
|
221
|
+
prepareWriteContract<const abi extends Abi | readonly unknown[], functionName extends WritableFunctionName<abi>>(parameters: EvmPrepareWriteContractParameters<abi, functionName>): Promise<PreparedEvmSend>;
|
|
192
222
|
readonly erc20: Erc20Actions;
|
|
193
223
|
}
|
|
194
224
|
export type EvmClientConfig = ({
|
|
@@ -203,6 +233,8 @@ export type EvmClientConfig = ({
|
|
|
203
233
|
/** Reads go here. Defaults to an http client on `rpcUrl`, else the chain's default RPC. */
|
|
204
234
|
publicClient?: PublicClient;
|
|
205
235
|
rpcUrl?: string;
|
|
236
|
+
/** The wallet's account and chain now, which a prepared send checks before it sends. */
|
|
237
|
+
current?: CurrentWalletState;
|
|
206
238
|
};
|
|
207
239
|
/** Bind a viem WalletClient and PublicClient into the mpo EVM client. */
|
|
208
240
|
export declare function evmClient(config: EvmClientConfig): EvmClient;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evm.d.ts","sourceRoot":"","sources":["../../src/clients/evm.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"evm.d.ts","sourceRoot":"","sources":["../../src/clients/evm.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/clients/hooks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/clients/hooks.ts"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAE9D,OAAO,EAAa,KAAK,SAAS,EAAwB,MAAM,OAAO,CAAC;AACxE,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGlD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,UAAU,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,SAAS,GAAG,SAAS,CA6BzF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,aAAa,GAAG,SAAS,CAkBjG;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,SAAS,GAAG,SAAS,CAYpD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,GAAG,SAAS,CAY5D"}
|
package/types/clients/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { ReadOnlyClientError, UnsupportedClientMethodError, type ChainClient, type ClientCapability, type ClientFamily, } from "./types";
|
|
2
|
-
export { classifyTxError, evmClient, ReceiptPendingError, ReceiptWaitAbortedError, TransactionReplacedError, TransactionRevertedError, type Erc20Actions, type EvmClient, type EvmClientConfig, type EvmReplacementReason, type EvmSendTransactionParameters, type EvmWaitOptions, type EvmWalletClient, type EvmWriteContractParameters, type TxErrorKind, } from "./evm";
|
|
2
|
+
export { classifyTxError, evmClient, ReceiptPendingError, ReceiptWaitAbortedError, TransactionReplacedError, TransactionRevertedError, type Erc20Actions, type EvmClient, type EvmClientConfig, type EvmPrepareTransactionParameters, type EvmPrepareWriteContractParameters, type EvmReplacementReason, type EvmSendTransactionParameters, type EvmWaitOptions, type EvmWalletClient, type EvmWriteContractParameters, type TxErrorKind, } from "./evm";
|
|
3
|
+
export { DEFAULT_PREPARED_SEND_MAX_AGE_MS, prepareEvmSend, PreparedSendStaleError, type CurrentWalletState, type Eip1193Request, type PrepareEvmSendParameters, type PreparedEvmSend, type PreparedSendStaleReason, } from "./preparedSend";
|
|
3
4
|
export { evmReadClient, evmReadClients, type Erc20ReadActions, type EvmReadClient, type EvmReadClientConfig, type EvmReadClients, } from "./evmRead";
|
|
4
5
|
export type { Web3ChainConfig } from "../providers/walletConfig";
|
|
5
6
|
export { suiClient, type SuiClient } from "./sui";
|