Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00b9eea9ec | ||
|
|
bf27b88728 | ||
|
|
17fe41d42b | ||
|
|
36600decef | ||
|
|
e09221f1e0 | ||
|
|
62cc785c4a | ||
|
|
7c45941b57 | ||
|
|
7a2e0edab4 | ||
|
|
fb0cb676b9 | ||
|
|
12d85dec10 | ||
|
|
1094a03a04 | ||
|
|
6cc38b0680 | ||
|
|
7abb36a7ec | ||
|
|
6832b19fc5 | ||
|
|
1c4cbedb6a | ||
|
|
8ff87be631 | ||
|
|
e43beec36a |
@@ -1,5 +1,21 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 4.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1973](https://github.com/thirdweb-dev/js/pull/1973) [`36600dec`](https://github.com/thirdweb-dev/js/commit/36600decefca7b9ebb1924757b4186b559e439f6) Thanks [@assimovt](https://github.com/assimovt)! - add logging to authenticate
|
||||
|
||||
- Updated dependencies [[`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1)]:
|
||||
- @thirdweb-dev/wallets@2.1.8
|
||||
|
||||
## 4.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96)]:
|
||||
- @thirdweb-dev/wallets@2.1.7
|
||||
|
||||
## 4.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "4.1.9",
|
||||
"version": "4.1.11",
|
||||
"main": "dist/thirdweb-dev-auth.cjs.js",
|
||||
"module": "dist/thirdweb-dev-auth.esm.js",
|
||||
"browser": {
|
||||
|
||||
@@ -62,6 +62,7 @@ export async function getUser<
|
||||
const token = getToken(req);
|
||||
|
||||
if (!token) {
|
||||
console.error("Error: No auth token found");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -75,6 +76,7 @@ export async function getUser<
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(`Authenticate Error: ${(err as Error)?.message}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.13.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`36600dec`](https://github.com/thirdweb-dev/js/commit/36600decefca7b9ebb1924757b4186b559e439f6), [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1), [`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4)]:
|
||||
- @thirdweb-dev/auth@4.1.11
|
||||
- @thirdweb-dev/wallets@2.1.8
|
||||
- @thirdweb-dev/sdk@4.0.16
|
||||
|
||||
## 0.13.18
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96), [`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec)]:
|
||||
- @thirdweb-dev/wallets@2.1.7
|
||||
- @thirdweb-dev/sdk@4.0.15
|
||||
- @thirdweb-dev/auth@4.1.10
|
||||
|
||||
## 0.13.17
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.13.17",
|
||||
"version": "0.13.19",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,45 @@
|
||||
# @thirdweb-dev/react-core
|
||||
|
||||
## 4.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1952](https://github.com/thirdweb-dev/js/pull/1952) [`fb0cb676`](https://github.com/thirdweb-dev/js/commit/fb0cb676b996fe386dcc53b5a6dab03f88e76175) Thanks [@MananTank](https://github.com/MananTank)! - - Add `ConnectEmbed`` component to embed the ConnectWallet's Modal UI directly in page
|
||||
|
||||
- Fix `ConnectWallet` component setting personal wallet as "connected" wallet when connecting a Safe / Smart Wallet
|
||||
|
||||
- [#1921](https://github.com/thirdweb-dev/js/pull/1921) [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Add Coin98 wallet
|
||||
|
||||
- [#1971](https://github.com/thirdweb-dev/js/pull/1971) [`12d85dec`](https://github.com/thirdweb-dev/js/commit/12d85dec10f47d6520b737a23576e4f832abb47f) Thanks [@iketw](https://github.com/iketw)! - Adds usePersonalWalletAddress hook to React/RN
|
||||
|
||||
```typescript
|
||||
// React
|
||||
import { usePersonalWalletAddress } from "@thirdweb-dev/react";
|
||||
|
||||
const personalWalletAddress = usePersonalWalletAddress();
|
||||
```
|
||||
|
||||
```typescript
|
||||
// React-Native
|
||||
import { usePersonalWalletAddress } from "@thirdweb-dev/react-native";
|
||||
|
||||
const personalWalletAddress = usePersonalWalletAddress();
|
||||
```
|
||||
|
||||
- Updated dependencies [[`36600dec`](https://github.com/thirdweb-dev/js/commit/36600decefca7b9ebb1924757b4186b559e439f6), [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1), [`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4)]:
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
|
||||
## 4.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96), [`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec)]:
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
|
||||
## 4.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-core",
|
||||
"version": "4.1.7",
|
||||
"version": "4.1.9",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useWalletContext } from "../providers/thirdweb-wallet-provider";
|
||||
import invariant from "tiny-invariant";
|
||||
import type {
|
||||
BloctoWallet,
|
||||
Coin98Wallet,
|
||||
CoinbaseWallet,
|
||||
CoreWallet,
|
||||
EmbeddedWallet,
|
||||
@@ -26,6 +27,7 @@ export type WalletId = (typeof walletIds)[keyof typeof walletIds];
|
||||
|
||||
type WalletIdToWalletTypeMap = {
|
||||
metamask: MetaMaskWallet;
|
||||
coin98: Coin98Wallet;
|
||||
coinbase: CoinbaseWallet;
|
||||
coreWallet: CoreWallet;
|
||||
rainbowWallet: RainbowWallet;
|
||||
|
||||
@@ -65,6 +65,16 @@ const walletInstanceToConfig: Map<
|
||||
WalletConfig<any>
|
||||
> = new Map();
|
||||
|
||||
type PersonalWalletInfo = {
|
||||
signer?: Signer;
|
||||
wallet?: WalletInstance;
|
||||
walletConfig?: WalletConfig;
|
||||
connectionStatus: ConnectionStatus;
|
||||
chainId?: number;
|
||||
disconnect: () => Promise<void>;
|
||||
switchChain: (chainId: number) => Promise<void>;
|
||||
};
|
||||
|
||||
type ThirdwebWalletContextData = {
|
||||
wallets: WalletConfig[];
|
||||
signer?: Signer;
|
||||
@@ -92,6 +102,20 @@ type ThirdwebWalletContextData = {
|
||||
getWalletConfig: (walletInstance: WalletInstance) => WalletConfig | undefined;
|
||||
activeChainSetExplicitly: boolean;
|
||||
clientId?: string;
|
||||
/**
|
||||
* flag indicating whether to treat any wallet that gets connected after setting this flag as a "personal" wallet
|
||||
* If a wallet is treated as a "personal" wallet, It's details like signer, connectionStatus, wallet, walletConfig will be saved in `personalWalletInfo` object instead of the main context
|
||||
*/
|
||||
isConnectingToPersonalWallet: boolean;
|
||||
/**
|
||||
* Set value of `isConnectingToPersonalWallet` flag
|
||||
*/
|
||||
setIsConnectingToPersonalWallet: (value: boolean) => void;
|
||||
/**
|
||||
* object containing info about the connected personal wallet
|
||||
* When `isConnectingToPersonalWallet` is set to true, and a wallet is connected after that, all the info about the connected wallet will be stored in this object
|
||||
*/
|
||||
personalWalletInfo: PersonalWalletInfo;
|
||||
};
|
||||
|
||||
const ThirdwebWalletContext = /* @__PURE__ */ createContext<
|
||||
@@ -113,12 +137,32 @@ export function ThirdwebWalletProvider(
|
||||
signerWallet?: WalletConfig<SignerWallet>;
|
||||
}>,
|
||||
) {
|
||||
const [signer, setSigner] = useState<Signer | undefined>(undefined);
|
||||
const [connectionStatus, setConnectionStatus] =
|
||||
useState<ConnectionStatus>("unknown");
|
||||
|
||||
const autoConnectTimeout = props.autoConnectTimeout || 15000;
|
||||
|
||||
if (!lastConnectedWalletStorage) {
|
||||
lastConnectedWalletStorage =
|
||||
props.createWalletStorage("coordinatorStorage");
|
||||
}
|
||||
|
||||
// if autoSwitch is enabled - enforce connection to activeChain
|
||||
const chainToConnect = props.autoSwitch ? props.activeChain : undefined;
|
||||
|
||||
const [isConnectingToPersonalWallet, setIsConnectingToPersonalWallet] =
|
||||
useState(false);
|
||||
|
||||
const personalWalletConnection = usePersonalWalletConnection();
|
||||
|
||||
const {
|
||||
wallet: personalWallet,
|
||||
setWallet: setPersonalWallet,
|
||||
setSigner: setPersonalWalletSigner,
|
||||
setStatus: setPersonalWalletStatus,
|
||||
setWalletConfig: setPersonalWalletConfig,
|
||||
} = personalWalletConnection;
|
||||
|
||||
const [signer, setSigner] = useState<Signer | undefined>(undefined);
|
||||
const [status, setStatus] = useState<ConnectionStatus>("unknown");
|
||||
|
||||
const [activeWallet, setActiveWallet] = useState<
|
||||
WalletInstance | undefined
|
||||
>();
|
||||
@@ -131,14 +175,6 @@ export function ThirdwebWalletProvider(
|
||||
WalletConfig | undefined
|
||||
>();
|
||||
|
||||
if (!lastConnectedWalletStorage) {
|
||||
lastConnectedWalletStorage =
|
||||
props.createWalletStorage("coordinatorStorage");
|
||||
}
|
||||
|
||||
// if autoSwitch is enabled - enforce connection to activeChain
|
||||
const chainToConnect = props.autoSwitch ? props.activeChain : undefined;
|
||||
|
||||
const walletParams: WalletOptions = useMemo(() => {
|
||||
return {
|
||||
chains: props.chains,
|
||||
@@ -165,12 +201,25 @@ export function ThirdwebWalletProvider(
|
||||
[walletParams],
|
||||
);
|
||||
|
||||
// if props.chains is updated, update the active wallet's chains
|
||||
// if props.chains is updated, update the wallet's chains
|
||||
useEffect(() => {
|
||||
if (activeWallet) {
|
||||
activeWallet.updateChains(props.chains);
|
||||
const _personalWallet = activeWallet.getPersonalWallet();
|
||||
if (_personalWallet instanceof AbstractClientWallet) {
|
||||
_personalWallet.updateChains(props.chains);
|
||||
}
|
||||
}
|
||||
}, [activeWallet, props.chains]);
|
||||
|
||||
if (isConnectingToPersonalWallet && personalWallet) {
|
||||
personalWallet.updateChains(props.chains);
|
||||
}
|
||||
}, [
|
||||
activeWallet,
|
||||
props.chains,
|
||||
personalWallet,
|
||||
isConnectingToPersonalWallet,
|
||||
]);
|
||||
|
||||
const setConnectedWallet = useCallback(
|
||||
async (
|
||||
@@ -178,23 +227,45 @@ export function ThirdwebWalletProvider(
|
||||
connectParams?: ConnectParams<Record<string, any>>,
|
||||
isAutoConnect = false,
|
||||
) => {
|
||||
setActiveWallet(wallet);
|
||||
if (isConnectingToPersonalWallet) {
|
||||
setPersonalWallet(wallet);
|
||||
} else {
|
||||
setActiveWallet(wallet);
|
||||
}
|
||||
|
||||
const walletConfig = walletInstanceToConfig.get(wallet);
|
||||
if (!walletConfig) {
|
||||
throw new Error(
|
||||
"Wallet config not found for given wallet instance. Do not create a wallet instance manually - use the useCreateWalletInstance() hook instead",
|
||||
);
|
||||
}
|
||||
setActiveWalletConfig(walletConfig);
|
||||
setConnectionStatus("connected");
|
||||
const _signer = await wallet.getSigner();
|
||||
setSigner(_signer);
|
||||
|
||||
// it autoconnected, then the details is already saved in storage, no need to store again
|
||||
if (isConnectingToPersonalWallet) {
|
||||
setPersonalWalletConfig(walletConfig);
|
||||
setPersonalWalletStatus("connected");
|
||||
} else {
|
||||
setActiveWalletConfig(walletConfig);
|
||||
setStatus("connected");
|
||||
}
|
||||
|
||||
const _signer = await wallet.getSigner();
|
||||
|
||||
if (isConnectingToPersonalWallet) {
|
||||
setPersonalWalletSigner(_signer);
|
||||
} else {
|
||||
setSigner(_signer);
|
||||
}
|
||||
|
||||
// it auto-connected, then the details is already saved in storage, no need to store again
|
||||
if (isAutoConnect) {
|
||||
return;
|
||||
}
|
||||
|
||||
// do not save connection details for personal wallet
|
||||
if (isConnectingToPersonalWallet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// save to storage
|
||||
|
||||
const walletInfo: LastConnectedWalletInfo = {
|
||||
@@ -202,16 +273,18 @@ export function ThirdwebWalletProvider(
|
||||
connectParams: connectParams || wallet.getConnectParams(),
|
||||
};
|
||||
|
||||
// if personal wallet exists, we need to replace the connectParams.personalWallet to a stringifiable version
|
||||
const personalWallet = wallet.getPersonalWallet() as AbstractClientWallet;
|
||||
const personalWalletConfig = walletInstanceToConfig.get(personalWallet);
|
||||
// if personal wallet exists, we need to replace the connectParams.personalWallet to a serializable version
|
||||
const _personalWallet =
|
||||
wallet.getPersonalWallet() as AbstractClientWallet;
|
||||
|
||||
if (personalWallet && personalWalletConfig) {
|
||||
const _personalWalletConfig = walletInstanceToConfig.get(_personalWallet);
|
||||
|
||||
if (_personalWallet && _personalWalletConfig) {
|
||||
walletInfo.connectParams = {
|
||||
...walletInfo.connectParams,
|
||||
personalWallet: {
|
||||
walletId: personalWalletConfig.id,
|
||||
connectParams: personalWallet.getConnectParams(),
|
||||
walletId: _personalWalletConfig.id,
|
||||
connectParams: _personalWallet.getConnectParams(),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -220,7 +293,13 @@ export function ThirdwebWalletProvider(
|
||||
saveLastConnectedWalletInfo(walletInfo);
|
||||
}
|
||||
},
|
||||
[],
|
||||
[
|
||||
isConnectingToPersonalWallet,
|
||||
setPersonalWallet,
|
||||
setPersonalWalletConfig,
|
||||
setPersonalWalletStatus,
|
||||
setPersonalWalletSigner,
|
||||
],
|
||||
);
|
||||
|
||||
const storeLastActiveChainId = useCallback(async (chainId: number) => {
|
||||
@@ -250,22 +329,35 @@ export function ThirdwebWalletProvider(
|
||||
|
||||
const switchChain = useCallback(
|
||||
async (chainId: number) => {
|
||||
if (!activeWallet) {
|
||||
const wallet = isConnectingToPersonalWallet
|
||||
? personalWallet
|
||||
: activeWallet;
|
||||
|
||||
if (!wallet) {
|
||||
throw new Error("No active wallet");
|
||||
}
|
||||
|
||||
await activeWallet.switchChain(chainId);
|
||||
const _signer = await activeWallet.getSigner();
|
||||
await storeLastActiveChainId(chainId);
|
||||
await wallet.switchChain(chainId);
|
||||
const _signer = await wallet.getSigner();
|
||||
|
||||
setSigner(_signer);
|
||||
if (!isConnectingToPersonalWallet) {
|
||||
await storeLastActiveChainId(chainId);
|
||||
setSigner(_signer);
|
||||
} else {
|
||||
setPersonalWalletSigner(_signer);
|
||||
}
|
||||
},
|
||||
[activeWallet, storeLastActiveChainId],
|
||||
[
|
||||
isConnectingToPersonalWallet,
|
||||
personalWallet,
|
||||
activeWallet,
|
||||
storeLastActiveChainId,
|
||||
setPersonalWalletSigner,
|
||||
],
|
||||
);
|
||||
|
||||
const autoConnectTriggered = useRef(false);
|
||||
|
||||
// Auto Connect
|
||||
const autoConnectTriggered = useRef(false);
|
||||
useEffect(() => {
|
||||
// do not auto connect if signerWallet is given
|
||||
if (props.signerWallet) {
|
||||
@@ -278,7 +370,7 @@ export function ThirdwebWalletProvider(
|
||||
// if explicitly set to false, don't auto connect
|
||||
// by default, auto connect
|
||||
if (props.shouldAutoConnect === false) {
|
||||
setConnectionStatus("disconnected");
|
||||
setStatus("disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -287,18 +379,18 @@ export function ThirdwebWalletProvider(
|
||||
return;
|
||||
}
|
||||
|
||||
if (connectionStatus !== "unknown") {
|
||||
if (status !== "unknown") {
|
||||
// only try to auto connect if we're in the unknown state
|
||||
return;
|
||||
}
|
||||
|
||||
autoConnectTriggered.current = true;
|
||||
|
||||
async function autoconnect() {
|
||||
async function autoConnect() {
|
||||
const walletInfo = await getLastConnectedWalletInfo();
|
||||
|
||||
if (!walletInfo) {
|
||||
setConnectionStatus("disconnected");
|
||||
setStatus("disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -308,7 +400,7 @@ export function ThirdwebWalletProvider(
|
||||
|
||||
if (!walletObj) {
|
||||
// last connected wallet is no longer present in the supported wallets
|
||||
setConnectionStatus("disconnected");
|
||||
setStatus("disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -317,12 +409,13 @@ export function ThirdwebWalletProvider(
|
||||
if (personalWalletInfo) {
|
||||
const personalWallets = walletObj.personalWallets || [];
|
||||
|
||||
const personalWalleObj = personalWallets.find(
|
||||
const personalWalletObj = personalWallets.find(
|
||||
(W) => W.id === personalWalletInfo.walletId,
|
||||
);
|
||||
if (personalWalleObj) {
|
||||
if (personalWalletObj) {
|
||||
// create a personal wallet instance and auto connect it
|
||||
const personalWalletInstance = createWalletInstance(personalWalleObj);
|
||||
const personalWalletInstance =
|
||||
createWalletInstance(personalWalletObj);
|
||||
|
||||
try {
|
||||
await timeoutPromise(
|
||||
@@ -337,7 +430,7 @@ export function ThirdwebWalletProvider(
|
||||
} catch (e) {
|
||||
console.error("Failed to auto connect personal wallet");
|
||||
console.error(e);
|
||||
setConnectionStatus("disconnected");
|
||||
setStatus("disconnected");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -348,7 +441,7 @@ export function ThirdwebWalletProvider(
|
||||
};
|
||||
} else {
|
||||
// last used personal wallet is no longer present in the supported wallets
|
||||
setConnectionStatus("disconnected");
|
||||
setStatus("disconnected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -357,7 +450,7 @@ export function ThirdwebWalletProvider(
|
||||
const wallet = createWalletInstance(walletObj);
|
||||
|
||||
try {
|
||||
setConnectionStatus("connecting");
|
||||
setStatus("connecting");
|
||||
await timeoutPromise(wallet.autoConnect(walletInfo.connectParams), {
|
||||
ms: autoConnectTimeout,
|
||||
message: autoConnectTimeoutErrorMessage,
|
||||
@@ -374,20 +467,21 @@ export function ThirdwebWalletProvider(
|
||||
LAST_CONNECTED_WALLET_STORAGE_KEY,
|
||||
);
|
||||
}
|
||||
setConnectionStatus("disconnected");
|
||||
setStatus("disconnected");
|
||||
}
|
||||
}
|
||||
|
||||
autoconnect();
|
||||
autoConnect();
|
||||
}, [
|
||||
createWalletInstance,
|
||||
props.supportedWallets,
|
||||
setConnectedWallet,
|
||||
props.shouldAutoConnect,
|
||||
activeWallet,
|
||||
connectionStatus,
|
||||
status,
|
||||
autoConnectTimeout,
|
||||
props.signerWallet,
|
||||
setStatus,
|
||||
]);
|
||||
|
||||
const connectWallet = useCallback(
|
||||
@@ -400,7 +494,12 @@ export function ThirdwebWalletProvider(
|
||||
};
|
||||
|
||||
const wallet = createWalletInstance(WalletObj);
|
||||
setConnectionStatus("connecting");
|
||||
if (isConnectingToPersonalWallet) {
|
||||
setPersonalWalletStatus("connecting");
|
||||
} else {
|
||||
setStatus("connecting");
|
||||
}
|
||||
|
||||
try {
|
||||
// if magic is using social login - it will redirect the page - so need to save walletInfo before connecting
|
||||
// TODO: find a better way to handle this
|
||||
@@ -414,41 +513,65 @@ export function ThirdwebWalletProvider(
|
||||
setConnectedWallet(wallet, _connectedParams);
|
||||
} catch (e: any) {
|
||||
console.error(`Error connecting to wallet: ${e}`);
|
||||
setConnectionStatus("disconnected");
|
||||
if (isConnectingToPersonalWallet) {
|
||||
setPersonalWalletStatus("disconnected");
|
||||
} else {
|
||||
setStatus("disconnected");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return wallet;
|
||||
},
|
||||
[createWalletInstance, setConnectedWallet, chainToConnect],
|
||||
[
|
||||
chainToConnect?.chainId,
|
||||
createWalletInstance,
|
||||
isConnectingToPersonalWallet,
|
||||
setPersonalWalletStatus,
|
||||
setConnectedWallet,
|
||||
],
|
||||
);
|
||||
|
||||
const onWalletDisconnect = useCallback(async () => {
|
||||
await lastConnectedWalletStorage.removeItem(
|
||||
LAST_CONNECTED_WALLET_STORAGE_KEY,
|
||||
);
|
||||
setConnectionStatus("disconnected");
|
||||
setSigner(undefined);
|
||||
setActiveWallet(undefined);
|
||||
setActiveWalletConfig(undefined);
|
||||
}, []);
|
||||
if (isConnectingToPersonalWallet) {
|
||||
setPersonalWalletStatus("disconnected");
|
||||
setPersonalWalletSigner(undefined);
|
||||
setPersonalWallet(undefined);
|
||||
setPersonalWalletConfig(undefined);
|
||||
} else {
|
||||
setStatus("disconnected");
|
||||
setSigner(undefined);
|
||||
setActiveWallet(undefined);
|
||||
setActiveWalletConfig(undefined);
|
||||
}
|
||||
}, [
|
||||
isConnectingToPersonalWallet,
|
||||
setPersonalWallet,
|
||||
setPersonalWalletStatus,
|
||||
setPersonalWalletSigner,
|
||||
setPersonalWalletConfig,
|
||||
]);
|
||||
|
||||
const disconnectWallet = useCallback(async () => {
|
||||
// if disconnect is called before the wallet is connected
|
||||
if (!activeWallet) {
|
||||
onWalletDisconnect();
|
||||
return;
|
||||
const wallet = isConnectingToPersonalWallet ? personalWallet : activeWallet;
|
||||
|
||||
if (wallet) {
|
||||
const _personalWallet = wallet.getPersonalWallet();
|
||||
await wallet.disconnect();
|
||||
if (_personalWallet instanceof AbstractClientWallet) {
|
||||
await _personalWallet.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
const personalWallet = activeWallet.getPersonalWallet();
|
||||
await activeWallet.disconnect();
|
||||
|
||||
if (personalWallet) {
|
||||
await (personalWallet as AbstractClientWallet)?.disconnect();
|
||||
}
|
||||
|
||||
onWalletDisconnect();
|
||||
}, [activeWallet, onWalletDisconnect]);
|
||||
}, [
|
||||
activeWallet,
|
||||
isConnectingToPersonalWallet,
|
||||
onWalletDisconnect,
|
||||
personalWallet,
|
||||
]);
|
||||
|
||||
// when wallet's network or account is changed using the extension, update UI
|
||||
useEffect(() => {
|
||||
@@ -461,19 +584,20 @@ export function ThirdwebWalletProvider(
|
||||
setSigner(_signer);
|
||||
};
|
||||
|
||||
activeWallet.addListener("change", () => {
|
||||
update();
|
||||
});
|
||||
|
||||
activeWallet.addListener("disconnect", () => {
|
||||
onWalletDisconnect();
|
||||
});
|
||||
|
||||
activeWallet.addListener("change", update);
|
||||
activeWallet.addListener("disconnect", onWalletDisconnect);
|
||||
return () => {
|
||||
activeWallet.removeListener("change");
|
||||
activeWallet.removeListener("disconnect");
|
||||
activeWallet.removeListener("change", update);
|
||||
activeWallet.removeListener("disconnect", onWalletDisconnect);
|
||||
};
|
||||
}, [activeWallet, onWalletDisconnect]);
|
||||
}, [
|
||||
activeWallet,
|
||||
isConnectingToPersonalWallet,
|
||||
onWalletDisconnect,
|
||||
personalWallet,
|
||||
setPersonalWalletSigner,
|
||||
setSigner,
|
||||
]);
|
||||
|
||||
// connect signerWallet immediately if it's passed
|
||||
// and disconnect it if it's not passed
|
||||
@@ -502,6 +626,17 @@ export function ThirdwebWalletProvider(
|
||||
disconnectWallet,
|
||||
]);
|
||||
|
||||
const setConnectionStatus = useCallback(
|
||||
(value: ConnectionStatus) => {
|
||||
if (isConnectingToPersonalWallet) {
|
||||
setPersonalWalletStatus(value);
|
||||
} else {
|
||||
setStatus(value);
|
||||
}
|
||||
},
|
||||
[isConnectingToPersonalWallet, setPersonalWalletStatus],
|
||||
);
|
||||
|
||||
return (
|
||||
<ThirdwebWalletContext.Provider
|
||||
value={{
|
||||
@@ -511,8 +646,8 @@ export function ThirdwebWalletProvider(
|
||||
signer,
|
||||
activeWallet,
|
||||
activeWalletConfig,
|
||||
connectionStatus,
|
||||
setConnectionStatus,
|
||||
connectionStatus: status,
|
||||
setConnectionStatus: setConnectionStatus,
|
||||
createWalletInstance: createWalletInstance,
|
||||
createdWalletInstance: createdWalletInstance,
|
||||
createWalletStorage: props.createWalletStorage,
|
||||
@@ -525,6 +660,17 @@ export function ThirdwebWalletProvider(
|
||||
},
|
||||
activeChainSetExplicitly: props.activeChainSetExplicitly,
|
||||
clientId: props.clientId,
|
||||
isConnectingToPersonalWallet,
|
||||
setIsConnectingToPersonalWallet,
|
||||
personalWalletInfo: {
|
||||
wallet: personalWalletConnection.wallet,
|
||||
signer: personalWalletConnection.signer,
|
||||
walletConfig: personalWalletConnection.walletConfig,
|
||||
connectionStatus: personalWalletConnection.status,
|
||||
chainId: personalWalletConnection.chainId,
|
||||
disconnect: personalWalletConnection.disconnect,
|
||||
switchChain: personalWalletConnection.switchChain,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
@@ -603,3 +749,79 @@ function timeoutPromise<T>(
|
||||
}
|
||||
|
||||
const autoConnectTimeoutErrorMessage = `Failed to Auto connect. Auto connect timed out. You can increase the timeout duration using the autoConnectTimeout prop on <ThirdwebProvider />`;
|
||||
|
||||
function usePersonalWalletConnection() {
|
||||
const [signer, setSigner] = useState<Signer | undefined>(undefined);
|
||||
const [status, setStatus] = useState<ConnectionStatus>("unknown");
|
||||
const [wallet, setWallet] = useState<WalletInstance | undefined>();
|
||||
const [walletConfig, setWalletConfig] = useState<WalletConfig | undefined>();
|
||||
const [chainId, setChainId] = useState<number | undefined>(undefined);
|
||||
|
||||
const handleDisconnect = useCallback(async () => {
|
||||
setStatus("disconnected");
|
||||
setSigner(undefined);
|
||||
setWallet(undefined);
|
||||
setWalletConfig(undefined);
|
||||
}, []);
|
||||
|
||||
const disconnect = useCallback(async () => {
|
||||
if (wallet) {
|
||||
await wallet.disconnect();
|
||||
}
|
||||
handleDisconnect();
|
||||
}, [wallet, handleDisconnect]);
|
||||
|
||||
useEffect(() => {
|
||||
if (wallet) {
|
||||
const updateChainId = () => {
|
||||
wallet?.getChainId().then((_chainId) => {
|
||||
setChainId(_chainId);
|
||||
});
|
||||
};
|
||||
|
||||
const update = async () => {
|
||||
updateChainId();
|
||||
const _signer = await wallet.getSigner();
|
||||
setSigner(_signer);
|
||||
};
|
||||
|
||||
updateChainId();
|
||||
wallet.addListener("change", update);
|
||||
wallet.addListener("disconnect", handleDisconnect);
|
||||
|
||||
return () => {
|
||||
wallet.removeListener("change", update);
|
||||
wallet.removeListener("disconnect", handleDisconnect);
|
||||
};
|
||||
} else {
|
||||
setChainId(undefined);
|
||||
}
|
||||
}, [disconnect, handleDisconnect, wallet]);
|
||||
|
||||
const switchChain = useCallback(
|
||||
async (_chainId: number) => {
|
||||
if (!wallet) {
|
||||
throw new Error("No active wallet");
|
||||
}
|
||||
|
||||
await wallet.switchChain(_chainId);
|
||||
const _signer = await wallet.getSigner();
|
||||
setSigner(_signer);
|
||||
},
|
||||
[wallet],
|
||||
);
|
||||
|
||||
return {
|
||||
signer: signer,
|
||||
setSigner: setSigner,
|
||||
wallet: wallet,
|
||||
setWallet: setWallet,
|
||||
walletConfig: walletConfig,
|
||||
setWalletConfig: setWalletConfig,
|
||||
status: status,
|
||||
setStatus: setStatus,
|
||||
chainId: chainId,
|
||||
disconnect,
|
||||
switchChain,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useWallet } from "../../core/hooks/wallet-hooks";
|
||||
|
||||
export const usePersonalWalletAddress = () => {
|
||||
const [personalWalletAddress, setPersonalWalletAddress] = useState<
|
||||
string | undefined
|
||||
>();
|
||||
const activeWallet = useWallet();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const possiblePersonalWallet = activeWallet?.getPersonalWallet();
|
||||
|
||||
if (!possiblePersonalWallet) {
|
||||
setPersonalWalletAddress(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
const address = await (
|
||||
await possiblePersonalWallet?.getSigner()
|
||||
)?.getAddress();
|
||||
setPersonalWalletAddress(address);
|
||||
})();
|
||||
}, [activeWallet]);
|
||||
|
||||
return personalWalletAddress;
|
||||
};
|
||||
@@ -22,6 +22,7 @@ export {
|
||||
} from "../core/hooks/wallet-hooks";
|
||||
export { useNetwork } from "../core/hooks/useNetwork";
|
||||
export { useWalletContext } from "../core/providers/thirdweb-wallet-provider";
|
||||
export { usePersonalWalletAddress } from "./hooks/usePersonalWalletAddress";
|
||||
|
||||
// connected wallet hooks
|
||||
export {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @thirdweb-dev/react-native-compat
|
||||
|
||||
## 0.4.9
|
||||
|
||||
## 0.4.8
|
||||
|
||||
## 0.4.7
|
||||
|
||||
## 0.4.6
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-native-compat",
|
||||
"description": "Shims for Thirdweb in React Native Projects",
|
||||
"version": "0.4.7",
|
||||
"version": "0.4.9",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
# @thirdweb-dev/react-native
|
||||
|
||||
## 0.4.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1971](https://github.com/thirdweb-dev/js/pull/1971) [`12d85dec`](https://github.com/thirdweb-dev/js/commit/12d85dec10f47d6520b737a23576e4f832abb47f) Thanks [@iketw](https://github.com/iketw)! - Adds usePersonalWalletAddress hook to React/RN
|
||||
|
||||
```typescript
|
||||
// React
|
||||
import { usePersonalWalletAddress } from "@thirdweb-dev/react";
|
||||
|
||||
const personalWalletAddress = usePersonalWalletAddress();
|
||||
```
|
||||
|
||||
```typescript
|
||||
// React-Native
|
||||
import { usePersonalWalletAddress } from "@thirdweb-dev/react-native";
|
||||
|
||||
const personalWalletAddress = usePersonalWalletAddress();
|
||||
```
|
||||
|
||||
- [#1989](https://github.com/thirdweb-dev/js/pull/1989) [`bf27b887`](https://github.com/thirdweb-dev/js/commit/bf27b8872870c08bb19934d87e574e0477c9550f) Thanks [@iketw](https://github.com/iketw)! - Improves EmbeddedWallet error messages in React Native
|
||||
|
||||
- Updated dependencies [[`fb0cb676`](https://github.com/thirdweb-dev/js/commit/fb0cb676b996fe386dcc53b5a6dab03f88e76175), [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1), [`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4), [`12d85dec`](https://github.com/thirdweb-dev/js/commit/12d85dec10f47d6520b737a23576e4f832abb47f)]:
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
|
||||
## 0.4.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96), [`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec)]:
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
|
||||
## 0.4.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-native",
|
||||
"version": "0.4.7",
|
||||
"version": "0.4.9",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
+1
-1
@@ -13,12 +13,12 @@ import { ModalHeaderTextClose } from "../base/modal/ModalHeaderTextClose";
|
||||
import {
|
||||
shortenAddress,
|
||||
useAddress,
|
||||
usePersonalWalletAddress,
|
||||
useWallet,
|
||||
} from "@thirdweb-dev/react-core";
|
||||
import { PasswordInput } from "../PasswordInput";
|
||||
import * as FileSystem from "expo-file-system";
|
||||
import { SmartWallet } from "@thirdweb-dev/wallets";
|
||||
import { usePersonalWalletAddress } from "../../wallets/hooks/usePersonalWalletAddress";
|
||||
import { LocalWallet } from "../../wallets/wallets/LocalWallet";
|
||||
import { TWModal } from "../base/modal/TWModal";
|
||||
import { useLocale } from "../../providers/ui-context-provider";
|
||||
|
||||
@@ -25,11 +25,13 @@ import { getCognitoUser, setCognitoUser } from "./helpers/storage/state";
|
||||
import { isDeviceSharePresentForUser } from "./helpers/storage/local";
|
||||
import { Auth } from "aws-amplify";
|
||||
import {
|
||||
DOMAIN_URL_2023,
|
||||
ROUTE_AUTH_JWT_CALLBACK,
|
||||
ROUTE_HEADLESS_OAUTH_LOGIN,
|
||||
} from "./helpers/constants";
|
||||
import { AuthOptions, OauthOption, VerifiedTokenResponse } from "../types";
|
||||
import { InAppBrowser } from "react-native-inappbrowser-reborn";
|
||||
import { createErrorMessage } from "./helpers/errors";
|
||||
|
||||
export async function sendVerificationEmail(options: {
|
||||
email: string;
|
||||
@@ -50,7 +52,7 @@ export async function sendVerificationEmail(options: {
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Malformed response from the send email OTP API: ${JSON.stringify(e)}`,
|
||||
createErrorMessage("Malformed response from the send email OTP API", e),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,7 +104,7 @@ export async function validateEmailOTP(options: {
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Malformed response from the send email OTP API: ${JSON.stringify(e)}`,
|
||||
createErrorMessage("Malformed response validating the OTP", e),
|
||||
);
|
||||
}
|
||||
let verifiedTokenResponse: VerifiedTokenResponse;
|
||||
@@ -163,9 +165,10 @@ export async function validateEmailOTP(options: {
|
||||
return { storedToken };
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Malformed response from the verify one time password: ${
|
||||
(e as Error).message
|
||||
}}`,
|
||||
createErrorMessage(
|
||||
"Malformed response from the verify one time password",
|
||||
e,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -173,7 +176,7 @@ export async function validateEmailOTP(options: {
|
||||
export async function socialLogin(oauthOptions: OauthOption, clientId: string) {
|
||||
const encodedProvider = encodeURIComponent(oauthOptions.provider);
|
||||
const headlessLoginLinkWithParams = `${ROUTE_HEADLESS_OAUTH_LOGIN}?authProvider=${encodedProvider}&baseUrl=${encodeURIComponent(
|
||||
`https://embedded-wallet.thirdweb.com`,
|
||||
DOMAIN_URL_2023,
|
||||
)}&platform=${encodeURIComponent("mobile")}`;
|
||||
|
||||
const resp = await fetch(headlessLoginLinkWithParams);
|
||||
@@ -188,7 +191,7 @@ export async function socialLogin(oauthOptions: OauthOption, clientId: string) {
|
||||
const { platformLoginLink } = json;
|
||||
|
||||
const completeLoginUrl = `${platformLoginLink}?developerClientId=${encodeURIComponent(
|
||||
clientId || "",
|
||||
clientId,
|
||||
)}&platform=${encodeURIComponent("mobile")}&redirectUrl=${encodeURIComponent(
|
||||
oauthOptions.redirectUrl,
|
||||
)}&authOption=${encodedProvider}`;
|
||||
@@ -243,7 +246,7 @@ export async function socialLogin(oauthOptions: OauthOption, clientId: string) {
|
||||
return { storedToken, email: storedToken.authDetails.email };
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Malformed response from post authentication: ${JSON.stringify(e)}`,
|
||||
createErrorMessage("Malformed response from post authentication", e),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -285,7 +288,7 @@ export async function customJwt(authOptions: AuthOptions, clientId: string) {
|
||||
return { verifiedToken, email: verifiedToken.authDetails.email };
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Malformed response from post authentication: ${JSON.stringify(e)}`,
|
||||
createErrorMessage("Malformed response from post authentication", e),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@ import {
|
||||
IsValidUserManagedEmailOTPResponse,
|
||||
VerifiedTokenResponse,
|
||||
} from "../../../types";
|
||||
import { createErrorMessage } from "../errors";
|
||||
|
||||
const EMBEDDED_WALLET_TOKEN_HEADER = "embedded-wallet-token";
|
||||
const PAPER_CLIENT_ID_HEADER = "x-thirdweb-client-id";
|
||||
@@ -281,7 +282,7 @@ export async function getUserShares(clientId: string, getShareUrl: URL) {
|
||||
};
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Malformed response from the ews user wallet API: ${JSON.stringify(e)}`,
|
||||
createErrorMessage("Malformed response from the ews user wallet API", e),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -19,7 +19,8 @@ export const COGNITO_IDENTITY_POOL_ID =
|
||||
export const GENERATE_RECOVERY_PASSWORD_LAMBDA_FUNCTION =
|
||||
"arn:aws:lambda:us-west-2:324457261097:function:recovery-share-password-GenerateRecoverySharePassw-bbE5ZbVAToil";
|
||||
|
||||
const BASE_URL_2023 = "https://embedded-wallet.thirdweb.com/";
|
||||
export const DOMAIN_URL_2023 = "https://embedded-wallet.thirdweb.com";
|
||||
const BASE_URL_2023 = `${DOMAIN_URL_2023}/`;
|
||||
const ROUTE_2023_10_20_API_BASE_PATH = `${BASE_URL_2023}api/2023-10-20`;
|
||||
|
||||
export const ROUTE_GET_EMBEDDED_WALLET_DETAILS = `${ROUTE_2023_10_20_API_BASE_PATH}/embedded-wallet/embedded-wallet-user-details`;
|
||||
|
||||
+8
@@ -3,3 +3,11 @@ export const ErrorMessages = {
|
||||
"Your OTP code is invalid or expired. Please request a new code or try again.",
|
||||
missingRecoveryCode: "Missing encryption key for user",
|
||||
};
|
||||
|
||||
export const createErrorMessage = (message: string, error: any): string => {
|
||||
if (error instanceof Error) {
|
||||
return `${message}: ${error.message}`;
|
||||
} else {
|
||||
return `${message}: ${JSON.stringify(error)}`;
|
||||
}
|
||||
};
|
||||
|
||||
+4
-2
@@ -12,6 +12,7 @@ import {
|
||||
} from "../constants";
|
||||
import { setDeviceShare } from "../storage/local";
|
||||
import { encryptShareWeb } from "./encryption";
|
||||
import { createErrorMessage } from "../errors";
|
||||
|
||||
export async function setUpNewUserWallet(
|
||||
recoveryCode: string,
|
||||
@@ -145,9 +146,10 @@ export async function storeShares<R extends string | undefined>({
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Malformed response from the ews store user share API: ${JSON.stringify(
|
||||
createErrorMessage(
|
||||
"Malformed response from the ews store user share API",
|
||||
e,
|
||||
)}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { useWallet } from "@thirdweb-dev/react-core";
|
||||
import { AbstractClientWallet, SmartWallet } from "@thirdweb-dev/wallets";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export const usePersonalWalletAddress = () => {
|
||||
const [personalWalletAddress, setPersonalWalletAddress] = useState<
|
||||
string | undefined
|
||||
>();
|
||||
const activeWallet = useWallet();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (activeWallet?.walletId === SmartWallet.id) {
|
||||
const address = await (
|
||||
activeWallet.getPersonalWallet() as AbstractClientWallet
|
||||
).getAddress();
|
||||
setPersonalWalletAddress(address);
|
||||
}
|
||||
})();
|
||||
}, [activeWallet]);
|
||||
|
||||
return personalWalletAddress;
|
||||
};
|
||||
@@ -1,5 +1,29 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 4.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1952](https://github.com/thirdweb-dev/js/pull/1952) [`fb0cb676`](https://github.com/thirdweb-dev/js/commit/fb0cb676b996fe386dcc53b5a6dab03f88e76175) Thanks [@MananTank](https://github.com/MananTank)! - - Add `ConnectEmbed`` component to embed the ConnectWallet's Modal UI directly in page
|
||||
|
||||
- Fix `ConnectWallet` component setting personal wallet as "connected" wallet when connecting a Safe / Smart Wallet
|
||||
|
||||
- [#1921](https://github.com/thirdweb-dev/js/pull/1921) [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Add Coin98 wallet
|
||||
|
||||
- Updated dependencies [[`fb0cb676`](https://github.com/thirdweb-dev/js/commit/fb0cb676b996fe386dcc53b5a6dab03f88e76175), [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1), [`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4), [`12d85dec`](https://github.com/thirdweb-dev/js/commit/12d85dec10f47d6520b737a23576e4f832abb47f)]:
|
||||
- @thirdweb-dev/react-core@4.1.9
|
||||
- @thirdweb-dev/wallets@2.1.8
|
||||
- @thirdweb-dev/sdk@4.0.16
|
||||
|
||||
## 4.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96), [`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec)]:
|
||||
- @thirdweb-dev/wallets@2.1.7
|
||||
- @thirdweb-dev/sdk@4.0.15
|
||||
- @thirdweb-dev/react-core@4.1.8
|
||||
|
||||
## 4.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "4.1.7",
|
||||
"version": "4.1.9",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export {
|
||||
ConnectEmbed,
|
||||
type ConnectEmbedProps,
|
||||
} from "../wallet/ConnectWallet/Modal/ConnectEmbed";
|
||||
|
||||
export {
|
||||
useIsWalletModalOpen,
|
||||
useSetIsWalletModalOpen,
|
||||
@@ -10,7 +15,10 @@ export {
|
||||
ConnectWallet,
|
||||
type ConnectWalletProps,
|
||||
} from "../wallet/ConnectWallet/ConnectWallet";
|
||||
export { ConnectModalInline } from "../wallet/ConnectWallet/Modal/ConnectModalInline";
|
||||
export {
|
||||
ConnectModalInline,
|
||||
type ConnectModalInlineProps,
|
||||
} from "../wallet/ConnectWallet/Modal/ConnectModalInline";
|
||||
|
||||
export { NetworkSelector } from "../wallet/ConnectWallet/NetworkSelector";
|
||||
export type { NetworkSelectorProps } from "../wallet/ConnectWallet/NetworkSelector";
|
||||
@@ -73,4 +81,5 @@ export {
|
||||
InjectedWallet,
|
||||
setWalletAnalyticsEnabled,
|
||||
CoreWallet,
|
||||
Coin98Wallet,
|
||||
} from "@thirdweb-dev/wallets";
|
||||
|
||||
@@ -52,6 +52,7 @@ export function enDefault() {
|
||||
personalWallet: "Personal Wallet",
|
||||
smartWallet: "Smart Wallet",
|
||||
or: "OR",
|
||||
goBackButton: "Back",
|
||||
download: {
|
||||
chrome: "Download Chrome Extension",
|
||||
android: "Download on Google Play",
|
||||
@@ -168,6 +169,7 @@ export function enDefault() {
|
||||
trustWallet: extensionAndQRScanScreens("Trust"),
|
||||
zerionWallet: extensionAndQRScanScreens("Zerion"),
|
||||
coreWallet: extensionAndQRScanScreens("Core"),
|
||||
coin98Wallet: extensionAndQRScanScreens("Coin98"),
|
||||
paperWallet: {
|
||||
signIn: "Sign in",
|
||||
signInWithGoogle: "Sign in with Google",
|
||||
|
||||
@@ -54,6 +54,7 @@ export function esDefault(): ThirdwebLocale {
|
||||
personalWallet: "Cartera personal",
|
||||
smartWallet: "Cartera inteligente",
|
||||
or: "O",
|
||||
goBackButton: "Atras",
|
||||
download: {
|
||||
chrome: "Descargar extensión para Chrome",
|
||||
android: "Descargar en Google Play",
|
||||
@@ -168,6 +169,7 @@ export function esDefault(): ThirdwebLocale {
|
||||
metamaskWallet: extensionAndQRScanScreens("MetaMask"),
|
||||
okxWallet: extensionAndQRScanScreens("OKX"),
|
||||
coreWallet: extensionAndQRScanScreens("Core"),
|
||||
coin98Wallet: extensionAndQRScanScreens("Coin98"),
|
||||
phantomWallet: extensionAndQRScanScreens("Phantom"),
|
||||
rainbowWallet: extensionAndQRScanScreens("Rainbow"),
|
||||
trustWallet: extensionAndQRScanScreens("Trust"),
|
||||
|
||||
@@ -53,6 +53,7 @@ export function jaDefault(): ThirdwebLocale {
|
||||
personalWallet: "パーソナルウォレット",
|
||||
smartWallet: "スマートウォレット",
|
||||
or: "または",
|
||||
goBackButton: "戻る", // TODO - check translation
|
||||
download: {
|
||||
chrome: "Chrome拡張をダウンロード",
|
||||
android: "Google Playでダウンロード",
|
||||
@@ -168,6 +169,7 @@ export function jaDefault(): ThirdwebLocale {
|
||||
metamaskWallet: extensionAndQRScanScreens("MetaMask"),
|
||||
okxWallet: extensionAndQRScanScreens("OKX"),
|
||||
coreWallet: extensionAndQRScanScreens("Core"),
|
||||
coin98Wallet: extensionAndQRScanScreens("Coin98"),
|
||||
phantomWallet: extensionAndQRScanScreens("Phantom"),
|
||||
rainbowWallet: extensionAndQRScanScreens("Rainbow"),
|
||||
trustWallet: extensionAndQRScanScreens("Trust"),
|
||||
|
||||
@@ -20,6 +20,7 @@ export type ModalConfig = {
|
||||
onLogin?: (token: string) => void;
|
||||
onLogout?: () => void;
|
||||
};
|
||||
isEmbed?: boolean;
|
||||
};
|
||||
|
||||
const WalletModalOpen = /* @__PURE__ */ createContext(false);
|
||||
@@ -47,6 +48,7 @@ export const WalletUIStatesProvider = (
|
||||
termsOfServiceUrl?: string;
|
||||
privacyPolicyUrl?: string;
|
||||
welcomeScreen?: WelcomeScreen;
|
||||
isEmbed?: boolean;
|
||||
}>,
|
||||
) => {
|
||||
const [isWalletModalOpen, setIsWalletModalOpen] = useState(false);
|
||||
@@ -62,6 +64,7 @@ export const WalletUIStatesProvider = (
|
||||
privacyPolicyUrl: props.privacyPolicyUrl,
|
||||
welcomeScreen: props.welcomeScreen,
|
||||
titleIconUrl: props.titleIconUrl,
|
||||
isEmbed: props.isEmbed,
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -20,6 +20,7 @@ export { walletConnectV1 } from "./wallet/wallets/walletConnectV1";
|
||||
export { zerionWallet } from "./wallet/wallets/zerion/zerionWallet";
|
||||
export { okxWallet } from "./wallet/wallets/okx/okxWallet";
|
||||
export { coreWallet } from "./wallet/wallets/coreWallet/coreWallet";
|
||||
export { coin98Wallet } from "./wallet/wallets/coin98/coin98Wallet";
|
||||
export { darkTheme, lightTheme } from "./design-system/index";
|
||||
export type { Theme, ThemeOverrides } from "./design-system/index";
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
import {
|
||||
SetModalConfigCtx,
|
||||
WalletUIStatesProvider,
|
||||
} from "../../../evm/providers/wallet-ui-states-provider";
|
||||
import { modalMaxWidthCompact, defaultTheme } from "../constants";
|
||||
import { ConnectModalContent } from "./ConnectModal";
|
||||
import { useScreen } from "./screen";
|
||||
import { isMobile } from "../../../evm/utils/isMobile";
|
||||
import { useConnectionStatus } from "@thirdweb-dev/react-core";
|
||||
import { DynamicHeight } from "../../../components/DynamicHeight";
|
||||
import {
|
||||
CustomThemeProvider,
|
||||
useCustomTheme,
|
||||
} from "../../../design-system/CustomThemeProvider";
|
||||
import { ComponentProps, useContext, useEffect } from "react";
|
||||
import { useTWLocale } from "../../../evm/providers/locale-provider";
|
||||
import { StyledDiv } from "../../../design-system/elements";
|
||||
import { Theme, radius } from "../../../design-system";
|
||||
|
||||
export type ConnectEmbedProps = {
|
||||
className?: string;
|
||||
theme?: "dark" | "light" | Theme;
|
||||
|
||||
style?: React.CSSProperties;
|
||||
|
||||
/**
|
||||
* If provided, Embed will show a Terms of Service message at the bottom with below link
|
||||
*/
|
||||
termsOfServiceUrl?: string;
|
||||
|
||||
/**
|
||||
* If provided, Embed will show a Privacy Policy message at the bottom with below link
|
||||
*/
|
||||
privacyPolicyUrl?: string;
|
||||
};
|
||||
export const ConnectEmbed = (props: ConnectEmbedProps) => {
|
||||
const connectionStatus = useConnectionStatus();
|
||||
|
||||
if (connectionStatus === "connected") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <ConnectEmbedContent {...props} />;
|
||||
};
|
||||
|
||||
const ConnectEmbedContent = (props: ConnectEmbedProps) => {
|
||||
const { screen, setScreen, initialScreen } = useScreen();
|
||||
const modalSize = "compact" as const;
|
||||
|
||||
const content = (
|
||||
<ConnectModalContent
|
||||
initialScreen={initialScreen}
|
||||
screen={screen}
|
||||
setScreen={setScreen}
|
||||
setHideModal={() => {
|
||||
// no op
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const walletUIStatesProps = {
|
||||
theme: props.theme || defaultTheme,
|
||||
modalSize: modalSize,
|
||||
title: undefined,
|
||||
termsOfServiceUrl: props.termsOfServiceUrl,
|
||||
privacyPolicyUrl: props.privacyPolicyUrl,
|
||||
isEmbed: true,
|
||||
};
|
||||
|
||||
return (
|
||||
<WalletUIStatesProvider {...walletUIStatesProps}>
|
||||
<CustomThemeProvider theme={walletUIStatesProps.theme}>
|
||||
<EmbedContainer
|
||||
className={props.className}
|
||||
style={{
|
||||
height: "auto",
|
||||
maxWidth: modalMaxWidthCompact,
|
||||
...props.style,
|
||||
}}
|
||||
>
|
||||
<DynamicHeight> {content} </DynamicHeight>
|
||||
<SyncedWalletUIStates {...walletUIStatesProps} />
|
||||
</EmbedContainer>
|
||||
</CustomThemeProvider>
|
||||
</WalletUIStatesProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export function SyncedWalletUIStates(
|
||||
props: ComponentProps<typeof WalletUIStatesProvider>,
|
||||
) {
|
||||
const setModalConfig = useContext(SetModalConfigCtx);
|
||||
const locale = useTWLocale();
|
||||
|
||||
// update modalConfig on props change
|
||||
useEffect(() => {
|
||||
setModalConfig((c) => ({
|
||||
...c,
|
||||
title: props.title || locale.connectWallet.defaultModalTitle,
|
||||
theme: props.theme || "dark",
|
||||
modalSize: (isMobile() ? "compact" : props.modalSize) || "wide",
|
||||
termsOfServiceUrl: props.termsOfServiceUrl,
|
||||
privacyPolicyUrl: props.privacyPolicyUrl,
|
||||
welcomeScreen: props.welcomeScreen,
|
||||
titleIconUrl: props.titleIconUrl,
|
||||
}));
|
||||
}, [
|
||||
props.title,
|
||||
props.theme,
|
||||
props.modalSize,
|
||||
props.termsOfServiceUrl,
|
||||
props.privacyPolicyUrl,
|
||||
props.welcomeScreen,
|
||||
props.titleIconUrl,
|
||||
setModalConfig,
|
||||
locale.connectWallet.defaultModalTitle,
|
||||
]);
|
||||
|
||||
return <WalletUIStatesProvider {...props} />;
|
||||
}
|
||||
|
||||
const EmbedContainer = /* @__PURE__ */ StyledDiv(() => {
|
||||
const theme = useCustomTheme();
|
||||
return {
|
||||
color: theme.colors.primaryText,
|
||||
background: theme.colors.modalBg,
|
||||
width: "100%",
|
||||
boxSizing: "border-box",
|
||||
position: "relative",
|
||||
lineHeight: "normal",
|
||||
borderRadius: radius.xl,
|
||||
border: `1px solid ${theme.colors.borderColor}`,
|
||||
overflow: "hidden",
|
||||
fontFamily: theme.fontFamily,
|
||||
"& *::selection": {
|
||||
backgroundColor: theme.colors.primaryText,
|
||||
color: theme.colors.modalBg,
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
useThirdwebAuthContext,
|
||||
useUser,
|
||||
useWallet,
|
||||
useWalletContext,
|
||||
useWallets,
|
||||
} from "@thirdweb-dev/react-core";
|
||||
import {
|
||||
@@ -43,6 +44,8 @@ export const ConnectModalContent = (props: {
|
||||
const walletConfigs = useWallets();
|
||||
const connectionStatus = useConnectionStatus();
|
||||
const disconnect = useDisconnect();
|
||||
const { setIsConnectingToPersonalWallet, personalWalletInfo } =
|
||||
useWalletContext();
|
||||
|
||||
const isWalletModalOpen = useIsWalletModalOpen();
|
||||
const setIsWalletModalOpen = useSetIsWalletModalOpen();
|
||||
@@ -59,6 +62,9 @@ export const ConnectModalContent = (props: {
|
||||
const authConfig = useThirdwebAuthContext();
|
||||
|
||||
const closeModal = () => {
|
||||
personalWalletInfo.disconnect();
|
||||
setIsConnectingToPersonalWallet(false);
|
||||
|
||||
setIsWalletModalOpen(false);
|
||||
onModalUnmount(() => {
|
||||
setScreen(initialScreen);
|
||||
@@ -66,6 +72,7 @@ export const ConnectModalContent = (props: {
|
||||
};
|
||||
|
||||
const { setHideModal } = props;
|
||||
|
||||
const handleConnected = useCallback(() => {
|
||||
const requiresSignIn = modalConfig.auth?.loginOptional
|
||||
? false
|
||||
@@ -261,6 +268,9 @@ export const ConnectModal = () => {
|
||||
wallet,
|
||||
]);
|
||||
|
||||
const { setIsConnectingToPersonalWallet, personalWalletInfo } =
|
||||
useWalletContext();
|
||||
|
||||
return (
|
||||
<CustomThemeProvider theme={theme}>
|
||||
<Modal
|
||||
@@ -270,6 +280,9 @@ export const ConnectModal = () => {
|
||||
setOpen={(value) => {
|
||||
setIsWalletModalOpen(value);
|
||||
if (!value) {
|
||||
setIsConnectingToPersonalWallet(false);
|
||||
personalWalletInfo.disconnect();
|
||||
|
||||
const requiresSignIn = auth?.loginOptional
|
||||
? false
|
||||
: !!authConfig?.authUrl && !user?.address;
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
import { CrossContainer } from "../../../components/Modal";
|
||||
import { IconButton } from "../../../components/buttons";
|
||||
import { iconSize, radius, shadow } from "../../../design-system";
|
||||
import {
|
||||
SetModalConfigCtx,
|
||||
WalletUIStatesProvider,
|
||||
} from "../../../evm/providers/wallet-ui-states-provider";
|
||||
import { Theme, iconSize, radius, shadow } from "../../../design-system";
|
||||
import { WalletUIStatesProvider } from "../../../evm/providers/wallet-ui-states-provider";
|
||||
import {
|
||||
wideModalMaxHeight,
|
||||
modalMaxWidthCompact,
|
||||
@@ -20,26 +17,65 @@ import {
|
||||
CustomThemeProvider,
|
||||
useCustomTheme,
|
||||
} from "../../../design-system/CustomThemeProvider";
|
||||
import { ComponentProps, useContext, useEffect } from "react";
|
||||
import { ConnectWalletProps } from "../ConnectWallet";
|
||||
import { useTWLocale } from "../../../evm/providers/locale-provider";
|
||||
import { StyledDiv } from "../../../design-system/elements";
|
||||
import { SyncedWalletUIStates } from "./ConnectEmbed";
|
||||
import { WelcomeScreen } from "../screens/types";
|
||||
|
||||
export const ConnectModalInline = (
|
||||
props: Omit<
|
||||
ConnectWalletProps,
|
||||
| "detailsBtn"
|
||||
| "dropdownPosition"
|
||||
| "auth"
|
||||
| "networkSelector"
|
||||
| "hideTestnetFaucet"
|
||||
| "switchToActiveChain"
|
||||
| "supportedTokens"
|
||||
| "hideSwitchToPersonalWallet"
|
||||
> & {
|
||||
onModalHide?: () => void;
|
||||
},
|
||||
) => {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type ConnectModalInlineProps = {
|
||||
className?: string;
|
||||
theme?: "dark" | "light" | Theme;
|
||||
|
||||
/**
|
||||
* Set a custom title for the modal
|
||||
* @defaultValue "Connect"
|
||||
*/
|
||||
modalTitle?: string;
|
||||
|
||||
/**
|
||||
* Replace the thirdweb icon next to modalTitle and set your own iconUrl
|
||||
*
|
||||
* Set to empty string to hide the icon
|
||||
*/
|
||||
modalTitleIconUrl?: string;
|
||||
|
||||
style?: React.CSSProperties;
|
||||
|
||||
/**
|
||||
* Set the size of the modal - `compact` or `wide` on desktop
|
||||
*
|
||||
* Modal size is always `compact` on mobile
|
||||
*
|
||||
* @defaultValue "wide"
|
||||
*/
|
||||
modalSize?: "compact" | "wide";
|
||||
|
||||
/**
|
||||
* If provided, Modal will show a Terms of Service message at the bottom with below link
|
||||
*/
|
||||
termsOfServiceUrl?: string;
|
||||
|
||||
/**
|
||||
* If provided, Modal will show a Privacy Policy message at the bottom with below link
|
||||
*/
|
||||
privacyPolicyUrl?: string;
|
||||
|
||||
/**
|
||||
* Customize the welcome screen
|
||||
*
|
||||
* Either provide a component to replace the default screen entirely
|
||||
*
|
||||
* or an object with title, subtitle and imgSrc to change the content of the default screen
|
||||
*/
|
||||
welcomeScreen?: WelcomeScreen;
|
||||
};
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const ConnectModalInline = (props: ConnectModalInlineProps) => {
|
||||
const { screen, setScreen, initialScreen } = useScreen();
|
||||
const walletConfigs = useWallets();
|
||||
const modalSize =
|
||||
@@ -53,9 +89,7 @@ export const ConnectModalInline = (
|
||||
screen={screen}
|
||||
setScreen={setScreen}
|
||||
setHideModal={() => {
|
||||
if (props.onModalHide) {
|
||||
props.onModalHide();
|
||||
}
|
||||
// no op
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -95,9 +129,10 @@ export const ConnectModalInline = (
|
||||
modalSize === "compact"
|
||||
? modalMaxWidthCompact
|
||||
: modalMaxWidthWide,
|
||||
...props.style,
|
||||
}}
|
||||
>
|
||||
{props.modalSize === "compact" ? (
|
||||
{modalSize === "compact" ? (
|
||||
<DynamicHeight> {content} </DynamicHeight>
|
||||
) : (
|
||||
content
|
||||
@@ -109,39 +144,6 @@ export const ConnectModalInline = (
|
||||
);
|
||||
};
|
||||
|
||||
function SyncedWalletUIStates(
|
||||
props: ComponentProps<typeof WalletUIStatesProvider>,
|
||||
) {
|
||||
const setModalConfig = useContext(SetModalConfigCtx);
|
||||
const locale = useTWLocale();
|
||||
|
||||
// update modalConfig on props change
|
||||
useEffect(() => {
|
||||
setModalConfig((c) => ({
|
||||
...c,
|
||||
title: props.title || locale.connectWallet.defaultModalTitle,
|
||||
theme: props.theme || "dark",
|
||||
modalSize: (isMobile() ? "compact" : props.modalSize) || "wide",
|
||||
termsOfServiceUrl: props.termsOfServiceUrl,
|
||||
privacyPolicyUrl: props.privacyPolicyUrl,
|
||||
welcomeScreen: props.welcomeScreen,
|
||||
titleIconUrl: props.titleIconUrl,
|
||||
}));
|
||||
}, [
|
||||
props.title,
|
||||
props.theme,
|
||||
props.modalSize,
|
||||
props.termsOfServiceUrl,
|
||||
props.privacyPolicyUrl,
|
||||
props.welcomeScreen,
|
||||
props.titleIconUrl,
|
||||
setModalConfig,
|
||||
locale.connectWallet.defaultModalTitle,
|
||||
]);
|
||||
|
||||
return <WalletUIStatesProvider {...props} />;
|
||||
}
|
||||
|
||||
const ConnectModalInlineContainer = /* @__PURE__ */ StyledDiv(() => {
|
||||
const theme = useCustomTheme();
|
||||
return {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
ScreenBottomContainer,
|
||||
Line,
|
||||
} from "../../components/basic";
|
||||
import { Button } from "../../components/buttons";
|
||||
import { Button, IconButton } from "../../components/buttons";
|
||||
import { ModalTitle } from "../../components/modalElements";
|
||||
import { iconSize, radius, spacing } from "../../design-system";
|
||||
import {
|
||||
@@ -26,6 +26,7 @@ import { Spacer } from "../../components/Spacer";
|
||||
import { TextDivider } from "../../components/TextDivider";
|
||||
import { TOS } from "./Modal/TOS";
|
||||
import { useTWLocale } from "../../evm/providers/locale-provider";
|
||||
import { ChevronLeftIcon } from "@radix-ui/react-icons";
|
||||
import { StyledButton, StyledUl } from "../../design-system/elements";
|
||||
import { useCustomTheme } from "../../design-system/CustomThemeProvider";
|
||||
|
||||
@@ -343,28 +344,62 @@ export const WalletSelector: React.FC<{
|
||||
return (
|
||||
<Container scrollY flex="column" animate="fadein" fullHeight>
|
||||
{/* Header */}
|
||||
<Container p="lg">
|
||||
{isWalletGroupExpanded ? (
|
||||
<ModalHeader
|
||||
title={twTitle}
|
||||
onBack={() => {
|
||||
setIsWalletGroupExpanded(false);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
twTitle
|
||||
)}
|
||||
</Container>
|
||||
{!modalConfig.isEmbed && (
|
||||
<Container p="lg">
|
||||
{isWalletGroupExpanded ? (
|
||||
<ModalHeader
|
||||
title={twTitle}
|
||||
onBack={() => {
|
||||
setIsWalletGroupExpanded(false);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
twTitle
|
||||
)}
|
||||
</Container>
|
||||
)}
|
||||
|
||||
{/* Body */}
|
||||
<Container
|
||||
expand
|
||||
scrollY
|
||||
px="md"
|
||||
style={{
|
||||
paddingTop: "2px",
|
||||
}}
|
||||
style={
|
||||
modalConfig.isEmbed
|
||||
? {
|
||||
paddingTop: spacing.lg,
|
||||
}
|
||||
: {
|
||||
paddingTop: "2px",
|
||||
}
|
||||
}
|
||||
>
|
||||
{modalConfig.isEmbed && isWalletGroupExpanded && (
|
||||
<Container
|
||||
flex="row"
|
||||
center="y"
|
||||
style={{
|
||||
padding: spacing.sm,
|
||||
paddingTop: 0,
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
setIsWalletGroupExpanded(false);
|
||||
}}
|
||||
style={{
|
||||
gap: spacing.xxs,
|
||||
transform: `translateX(-${spacing.xs})`,
|
||||
paddingBlock: spacing.xxs,
|
||||
paddingRight: spacing.xs,
|
||||
}}
|
||||
>
|
||||
<ChevronLeftIcon width={iconSize.sm} height={iconSize.sm} />
|
||||
{locale.goBackButton}
|
||||
</IconButton>
|
||||
</Container>
|
||||
)}
|
||||
|
||||
{topSection}
|
||||
</Container>
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core";
|
||||
import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen";
|
||||
import { isMobile } from "../../../evm/utils/isMobile";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen";
|
||||
import type { Coin98Wallet } from "@thirdweb-dev/wallets";
|
||||
import { wait } from "../../../utils/wait";
|
||||
import { useTWLocale } from "../../../evm/providers/locale-provider";
|
||||
import { Coin98Scan } from "./Coin98Scan";
|
||||
import { WCOpenURI } from "../../ConnectWallet/screens/WCOpenUri";
|
||||
import { coin98WalletUris } from "./coin98WalletUris";
|
||||
|
||||
export const Coin98ConnectUI = (props: ConnectUIProps<Coin98Wallet>) => {
|
||||
const [screen, setScreen] = useState<
|
||||
"connecting" | "scanning" | "get-started" | "open-wc-uri"
|
||||
>("connecting");
|
||||
const locale = useTWLocale().wallets.coin98Wallet;
|
||||
const connectingLocale = {
|
||||
getStartedLink: locale.getStartedLink,
|
||||
instruction: locale.connectionScreen.instruction,
|
||||
tryAgain: locale.connectionScreen.retry,
|
||||
inProgress: locale.connectionScreen.inProgress,
|
||||
failed: locale.connectionScreen.failed,
|
||||
};
|
||||
const { walletConfig, connected } = props;
|
||||
const connect = useConnect();
|
||||
const [errorConnecting, setErrorConnecting] = useState(false);
|
||||
|
||||
const hideBackButton = props.supportedWallets.length === 1;
|
||||
|
||||
const connectToExtension = useCallback(async () => {
|
||||
try {
|
||||
connectPrompted.current = true;
|
||||
setErrorConnecting(false);
|
||||
setScreen("connecting");
|
||||
await wait(1000);
|
||||
await connect(walletConfig);
|
||||
connected();
|
||||
} catch (e) {
|
||||
setErrorConnecting(true);
|
||||
console.error(e);
|
||||
}
|
||||
}, [connected, connect, walletConfig]);
|
||||
|
||||
const connectPrompted = useRef(false);
|
||||
useEffect(() => {
|
||||
if (connectPrompted.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isInstalled = walletConfig.isInstalled
|
||||
? walletConfig.isInstalled()
|
||||
: false;
|
||||
|
||||
// if loading
|
||||
(async () => {
|
||||
if (isInstalled) {
|
||||
connectToExtension();
|
||||
}
|
||||
|
||||
// if wallet is not injected
|
||||
else {
|
||||
// on mobile, open the Coin98 Mobile via wallet connect
|
||||
if (isMobile()) {
|
||||
setScreen("open-wc-uri");
|
||||
} else {
|
||||
// on desktop, show the Coin98 app scan qr code
|
||||
setScreen("scanning");
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, [connectToExtension, walletConfig]);
|
||||
|
||||
if (screen === "connecting") {
|
||||
return (
|
||||
<ConnectingScreen
|
||||
locale={{
|
||||
getStartedLink: locale.getStartedLink,
|
||||
instruction: locale.connectionScreen.instruction,
|
||||
tryAgain: locale.connectionScreen.retry,
|
||||
inProgress: locale.connectionScreen.inProgress,
|
||||
failed: locale.connectionScreen.failed,
|
||||
}}
|
||||
errorConnecting={errorConnecting}
|
||||
onGetStarted={() => {
|
||||
setScreen("get-started");
|
||||
}}
|
||||
onRetry={connectToExtension}
|
||||
hideBackButton={hideBackButton}
|
||||
onBack={props.goBack}
|
||||
walletName={walletConfig.meta.name}
|
||||
walletIconURL={walletConfig.meta.iconURL}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (screen === "open-wc-uri") {
|
||||
return (
|
||||
<WCOpenURI
|
||||
locale={connectingLocale}
|
||||
onRetry={() => {
|
||||
// NOOP - TODO make onRetry optional
|
||||
}}
|
||||
errorConnecting={errorConnecting}
|
||||
onGetStarted={() => {
|
||||
setScreen("get-started");
|
||||
}}
|
||||
hideBackButton={hideBackButton}
|
||||
onBack={props.goBack}
|
||||
onConnected={connected}
|
||||
walletConfig={walletConfig}
|
||||
appUriPrefix={coin98WalletUris}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (screen === "get-started") {
|
||||
return (
|
||||
<GetStartedScreen
|
||||
locale={{
|
||||
scanToDownload: locale.getStartedScreen.instruction,
|
||||
}}
|
||||
walletIconURL={walletConfig.meta.iconURL}
|
||||
walletName={walletConfig.meta.name}
|
||||
chromeExtensionLink={walletConfig.meta.urls?.chrome}
|
||||
googlePlayStoreLink={walletConfig.meta.urls?.android}
|
||||
appleStoreLink={walletConfig.meta.urls?.ios}
|
||||
onBack={props.goBack}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (screen === "scanning") {
|
||||
return (
|
||||
<Coin98Scan
|
||||
onBack={props.goBack}
|
||||
onConnected={props.connected}
|
||||
onGetStarted={() => {
|
||||
setScreen("get-started");
|
||||
}}
|
||||
hideBackButton={hideBackButton}
|
||||
walletConfig={walletConfig}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
import { ScanScreen } from "../../ConnectWallet/screens/ScanScreen";
|
||||
import {
|
||||
useCreateWalletInstance,
|
||||
useWalletContext,
|
||||
} from "@thirdweb-dev/react-core";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { Coin98Wallet } from "@thirdweb-dev/wallets";
|
||||
import type { WalletConfig } from "@thirdweb-dev/react-core";
|
||||
import { useTWLocale } from "../../../evm/providers/locale-provider";
|
||||
|
||||
export const Coin98Scan: React.FC<{
|
||||
onBack: () => void;
|
||||
onGetStarted: () => void;
|
||||
onConnected: () => void;
|
||||
walletConfig: WalletConfig<Coin98Wallet>;
|
||||
hideBackButton: boolean;
|
||||
}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => {
|
||||
const locale = useTWLocale().wallets.coin98Wallet;
|
||||
const createInstance = useCreateWalletInstance();
|
||||
const [qrCodeUri, setQrCodeUri] = useState<string | undefined>();
|
||||
const { setConnectedWallet, chainToConnect, setConnectionStatus } =
|
||||
useWalletContext();
|
||||
|
||||
const scanStarted = useRef(false);
|
||||
useEffect(() => {
|
||||
if (scanStarted.current) {
|
||||
return;
|
||||
}
|
||||
scanStarted.current = true;
|
||||
|
||||
const wallet = createInstance(walletConfig);
|
||||
|
||||
setConnectionStatus("connecting");
|
||||
wallet.connectWithQrCode({
|
||||
chainId: chainToConnect?.chainId,
|
||||
onQrCodeUri(uri) {
|
||||
setQrCodeUri(uri);
|
||||
},
|
||||
onConnected() {
|
||||
setConnectedWallet(wallet);
|
||||
onConnected();
|
||||
},
|
||||
});
|
||||
}, [
|
||||
createInstance,
|
||||
setConnectedWallet,
|
||||
chainToConnect,
|
||||
onConnected,
|
||||
walletConfig,
|
||||
setConnectionStatus,
|
||||
]);
|
||||
|
||||
return (
|
||||
<ScanScreen
|
||||
qrScanInstruction={locale.scanScreen.instruction}
|
||||
onBack={onBack}
|
||||
onGetStarted={onGetStarted}
|
||||
qrCodeUri={qrCodeUri}
|
||||
walletName={walletConfig.meta.name}
|
||||
walletIconURL={walletConfig.meta.iconURL}
|
||||
hideBackButton={hideBackButton}
|
||||
getStartedLink={locale.getStartedLink}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
|
||||
import { Coin98Wallet, getInjectedCoin98Provider } from "@thirdweb-dev/wallets";
|
||||
import { Coin98ConnectUI } from "./Coin98ConnectUI";
|
||||
|
||||
type Coin98WalletOptions = {
|
||||
/**
|
||||
* When connecting Coin98 using the QR Code - Wallet Connect connector is used which requires a project id.
|
||||
* This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com.
|
||||
*
|
||||
* https://docs.walletconnect.com/2.0/web3modal/options#projectid-required
|
||||
*/
|
||||
projectId?: string;
|
||||
|
||||
/**
|
||||
* If true, the wallet will be tagged as "reccomended" in ConnectWallet Modal
|
||||
*/
|
||||
recommended?: boolean;
|
||||
};
|
||||
|
||||
export const coin98Wallet = (
|
||||
options?: Coin98WalletOptions,
|
||||
): WalletConfig<Coin98Wallet> => {
|
||||
return {
|
||||
id: Coin98Wallet.id,
|
||||
recommended: options?.recommended,
|
||||
meta: {
|
||||
name: "Coin98 Wallet",
|
||||
urls: {
|
||||
chrome:
|
||||
"https://chrome.google.com/webstore/detail/coin98-wallet/aeachknmefphepccionboohckonoeemg",
|
||||
android:
|
||||
"https://play.google.com/store/apps/details?id=coin98.crypto.finance.media",
|
||||
ios: "https://apps.apple.com/us/app/coin98-super-wallet/id1561969966",
|
||||
},
|
||||
iconURL:
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAABGCAMAAABG8BK2AAACf1BMVEUAAAD//wD/gAD/qlW/v0DMzDPVqivbtiTfv0DjqjnmszPRuS7VqivYsTvbtjfduzPfrzDStC3VuDnXrjbesTfVtTXWuDPYsTHZsy/bti7csDXdszPWtTHYsi7atDXcsTLWszDXtS/YtzTZszPatDLbsi/XtTPZszHatTDbtjTbszPXtDLYtTHZsjDZszTatTPbtjLbszHXtDDYtjTZszPZtDLatTHbsjHXszTYtDPYtjLatDHatTTbszPXtDLYtTLZszTatDPatTLbszLYtDHZszPZszLatDLatTHYtDPZszLZtDHatTHYszLZtTHatTLaszLYtDHYtDHZtTPatTLYtDPZtDPZtTLZszLatTPYszPYtDLZszHatDPatTLYtDHZtTPZszPZtDLZtDPZtTLZszLZtDHatDHYszPYtDLZtTHZszHatDPatTLYszLZtDHZtTPZtDLatDLatTLYszHZtDPZtDLZszLZtDLYtTPYtDLZtDLZtDLZszHZtDPatDLZtDLZtDHZtTPZtDLatDLYszHZtDPZtDLZtDLatDLatDPYszLZtDLZtDLZtDPatDLYtTLZszLZtDLZtDHZszLZtDHZtDLZtDLYtTLZtDLZtDLZtDLZtDHZtDLYtDLZszLZtDLZtDHZtTLatDLZszLZtDPZtDLZtTLZtDLatDLZtDPZtDLZtDLZtDLZszLZtDPatDLZtDLZtDLZtDLZtDPZtDLZtDLYtDLZtTLZtDLZtDLZtDLZtDLZtDHZszLZtDLZtDLZtDLZtDHatDLZtDLZtDLZtDLZtDLZtDLYtDLZtDLZtDLZtDLZtDLZtDLZtDLZtDLZtDLZtDLZtDL///+sdBnoAAAA03RSTlMAAQIDBAUGBwgJCgsMDQ4PEBESExcYGRobHB0eHyEiJCUmJygpKy0vMDEyMzQ1Njc4OTo7PD0+P0BBQkRFRkdISktMTU5QUVJTVVdYWVtdYGFiY2RnaWprbG5vcHJ0dXd4eXp+f4CBgoOEhoeIiYqMjY6PkJGSk5SVl5iZmpucnZ+goaOkpaanqaqrrK2usLGys7S1trq7vMDBwsPExcbHyMnKzM7P0NHS09TV1tfY2drb3N3e3+Dh4uTl5ufo6err7O3u7/Hy8/T19vf4+fr7/P3++uqKzgAAAAFiS0dE1Am7C4UAAAPWSURBVBgZrcGLf81lAMfxzzk2NrJJcoujG10QliiXhCRU0kWlUi4r1ySXmS5KF4nWbFKMUlic2pRmk5nNUouzNOf7D/X8dvbbeZ6zvV5edn7vN23C49YcrI7pOsROH1w9Noxj8k/qkoo5Idr12asuK82lTaRSaagYSqucqNJS2QfPXqWpGGOW0jYdwj8rbSfC5CkAY1irAKzikAJQRrUCUEVMAYihQKBAoECgQKBO/LVnc/7GXfVK0VS8MX/zvmZ1AnVwZGYPPN0mlMpy+qksPDmL6tUBShF7NkS7yXXyfZKFr+8+pUKuC+OwDT6uhMIQSRlFSoEcVx7ENficPN9lYutdKRdyvI5n+p6LaiyaiuchefIwbtpQq/OFN2PMlAvZzmQBWTuU8FF3jK8kHcYYVCXP2WEYUTmQ7VWMz+R7H2OcpOUYXyvhcAhYKQeyDQMeUdJ4IFwrzQFulW8UMFsOZDmDUayk7Rg7pCnAbPkWAGPlQJbvMRqV9BvGOmkWME2+ecAkOZClFAhdVVIjxlLpBeDGy0r4byCwQA5k+QHjvJIqMDZJhdB/ZZMSYqv6QaEcyFKL8YWStmKUSHU9FzdLV09+u6usWtLlxT0b5UC24cAEtWsZAWQ3SaqRKp/rj+e2JfVSjVzItgyjQL58jMfkubwwA1/O+rhSIFtDLpCxIS5PS34ICB+TcXYUtidiciHHejyjP6hojBYOx/O8jKYRuJ6UCzni83DlNcuYi+f+7b82HFs7BM9mOZArNhfbxAsyyjAy31Wr2EKMvhdlQynib+fg6/HaFXmmYnwo34sYK2RDHdQvvR3PLS9Vq9WfmcDDatc8FLhHNtSZMwd37z8l326MEiWtA0L1sqBrewejSUlHMY7Igq5tCZAty1mMUlnQta3B+EdJxzEOyYIcj0cikaflWxaJRO6TPsbYq6QtGNWyIMcsYHBcbR4A7paqQ8A0tWsZAQyRDTlWYBQp4WgYeEbSSIxt8r2BsUg25DiB0e+kPHV3YJRI2omRWRCXp2V5COj+u2zINQOjz1s1Or91AMZdVyXFx+MZ/V70XPmmO/G8IgdyVdyALWO/PH8MwJXXLAdKUZRBUqhACeUDsY2skwul2tcXX/an8tVOol1o/iWlQB00vJyLJ3t+jSwlE7vh6THzR3WAOvHvNwX5m0ovKUXDlxvzt5T8rU6gQKBAoECgQBBTAGKcVgBOcUgBOMBqBeBNxioA9xKOKm1HQ/Co0jYFo0Rp2omnd1RpKe9Fq6G/KA3lg2iTW6wu+7wXSZPL1SXRGTjCY1aVVcV0HWJVB1aMDJHwP77sAsSIUUvgAAAAAElFTkSuQmCC",
|
||||
},
|
||||
create: (walletOptions: WalletOptions) => {
|
||||
const wallet = new Coin98Wallet({
|
||||
...walletOptions,
|
||||
projectId: options?.projectId,
|
||||
qrcode: false,
|
||||
});
|
||||
|
||||
return wallet;
|
||||
},
|
||||
connectUI: Coin98ConnectUI,
|
||||
isInstalled() {
|
||||
return !!getInjectedCoin98Provider();
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
export const coin98WalletUris = {
|
||||
ios: "coin98://",
|
||||
android: "coin98://",
|
||||
other: "coin98://",
|
||||
};
|
||||
@@ -11,13 +11,10 @@ import {
|
||||
ExclamationTriangleIcon,
|
||||
} from "@radix-ui/react-icons";
|
||||
import {
|
||||
useChain,
|
||||
useChainId,
|
||||
useConnect,
|
||||
useConnectionStatus,
|
||||
useSupportedChains,
|
||||
useSwitchChain,
|
||||
useWallet,
|
||||
useWalletContext,
|
||||
} from "@thirdweb-dev/react-core";
|
||||
import { SafeSupportedChainsSet } from "@thirdweb-dev/wallets";
|
||||
import { utils } from "ethers";
|
||||
@@ -38,22 +35,24 @@ export const SelectAccount: React.FC<{
|
||||
renderBackButton?: boolean;
|
||||
}> = (props) => {
|
||||
const locale = useTWLocale().wallets.safeWallet.accountDetailsScreen;
|
||||
const activeWallet = useWallet();
|
||||
const connect = useConnect();
|
||||
const activeChain = useChain();
|
||||
const connectedChainId = useChainId();
|
||||
|
||||
const [safeAddress, setSafeAddress] = useState("");
|
||||
const [safeChainId, setSafeChainId] = useState(-1);
|
||||
|
||||
const [safeConnectError, setSafeConnectError] = useState(false);
|
||||
// personal wallet
|
||||
const { personalWalletInfo } = useWalletContext();
|
||||
const personalWallet = personalWalletInfo?.wallet;
|
||||
const personalWalletChainId = personalWalletInfo?.chainId;
|
||||
const switchChainPersonalWallet = personalWalletInfo?.switchChain;
|
||||
const [switchError, setSwitchError] = useState(false);
|
||||
const [switchingNetwork, setSwitchingNetwork] = useState(false);
|
||||
|
||||
// safe
|
||||
const connectSafe = useConnect();
|
||||
const [safeAddress, setSafeAddress] = useState("");
|
||||
const [safeChainId, setSafeChainId] = useState(-1);
|
||||
const [safeConnectError, setSafeConnectError] = useState(false);
|
||||
const connectionStatus = useConnectionStatus();
|
||||
|
||||
const chains = useSupportedChains();
|
||||
|
||||
// put supported chains first
|
||||
const supportedChains = chains.filter((c) =>
|
||||
SafeSupportedChainsSet.has(c.chainId),
|
||||
);
|
||||
@@ -69,15 +68,14 @@ export const SelectAccount: React.FC<{
|
||||
const useOptGroup = mainnets.length > 0 && testnets.length > 0;
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!selectedSafeChain || !activeWallet || !activeChain) {
|
||||
if (!selectedSafeChain || !personalWallet) {
|
||||
return;
|
||||
}
|
||||
setSafeConnectError(false);
|
||||
|
||||
try {
|
||||
await connect(props.safeWalletConfig, {
|
||||
await connectSafe(props.safeWalletConfig, {
|
||||
chain: selectedSafeChain,
|
||||
personalWallet: activeWallet,
|
||||
personalWallet: personalWallet,
|
||||
safeAddress,
|
||||
});
|
||||
props.onConnect();
|
||||
@@ -87,12 +85,11 @@ export const SelectAccount: React.FC<{
|
||||
}
|
||||
};
|
||||
|
||||
const mismatch = safeChainId !== -1 && connectedChainId !== safeChainId;
|
||||
const mismatch = safeChainId !== -1 && personalWalletChainId !== safeChainId;
|
||||
|
||||
const isValidAddress = utils.isAddress(safeAddress);
|
||||
const disableNetworkSelection = supportedChains.length === 1;
|
||||
|
||||
const switchChain = useSwitchChain();
|
||||
const modalConfig = useContext(ModalConfigCtx);
|
||||
|
||||
return (
|
||||
@@ -326,14 +323,14 @@ export const SelectAccount: React.FC<{
|
||||
width: modalConfig.modalSize === "compact" ? "100%" : undefined,
|
||||
}}
|
||||
onClick={async () => {
|
||||
if (!activeWallet) {
|
||||
throw new Error("No active wallet");
|
||||
if (!personalWallet) {
|
||||
throw new Error("No personal wallet connected");
|
||||
}
|
||||
setSafeConnectError(false);
|
||||
setSwitchError(false);
|
||||
setSwitchingNetwork(true);
|
||||
try {
|
||||
await switchChain(safeChainId);
|
||||
await switchChainPersonalWallet(safeChainId);
|
||||
} catch (e) {
|
||||
setSwitchError(true);
|
||||
} finally {
|
||||
|
||||
@@ -3,11 +3,10 @@ import {
|
||||
WalletConfig,
|
||||
ConnectUIProps,
|
||||
WalletOptions,
|
||||
useDisconnect,
|
||||
useWallet,
|
||||
useWalletContext,
|
||||
} from "@thirdweb-dev/react-core";
|
||||
import { defaultWallets } from "../defaultWallets";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { SelectpersonalWallet } from "./SelectPersonalWallet";
|
||||
import type { SafeWalletConfigOptions, SafeWalletConfig } from "./types";
|
||||
import { SelectAccount } from "./SelectAccount";
|
||||
@@ -44,19 +43,57 @@ export const safeWallet = (
|
||||
export const SafeConnectUI = (
|
||||
props: ConnectUIProps<SafeWallet> & { personalWallets: WalletConfig[] },
|
||||
) => {
|
||||
const activeWallet = useWallet();
|
||||
// selected personal wallet config
|
||||
const [personalWalletConfig, setPersonalWalletConfig] = useState<
|
||||
WalletConfig | undefined
|
||||
>();
|
||||
const disconnect = useDisconnect();
|
||||
|
||||
if (personalWalletConfig) {
|
||||
const [isPersonalWalletConnected, setIsPersonalWalletConnected] =
|
||||
useState(false);
|
||||
|
||||
const { setIsConnectingToPersonalWallet, personalWalletInfo } =
|
||||
useWalletContext();
|
||||
|
||||
const goBackToMain = () => {
|
||||
personalWalletInfo.disconnect();
|
||||
setIsConnectingToPersonalWallet(false);
|
||||
props.goBack();
|
||||
};
|
||||
|
||||
const mounted = useRef(false);
|
||||
useEffect(() => {
|
||||
if (mounted.current) {
|
||||
return;
|
||||
}
|
||||
mounted.current = true;
|
||||
setIsConnectingToPersonalWallet(true);
|
||||
}, [setIsConnectingToPersonalWallet]);
|
||||
|
||||
// if personal wallet is not selected
|
||||
if (!personalWalletConfig) {
|
||||
return (
|
||||
<SelectpersonalWallet
|
||||
personalWallets={props.personalWallets}
|
||||
onBack={goBackToMain}
|
||||
safeWallet={props.walletConfig}
|
||||
selectWallet={(walletConfig) => {
|
||||
setPersonalWalletConfig(walletConfig);
|
||||
}}
|
||||
renderBackButton={props.supportedWallets.length > 1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// if the personal wallet is selected, but not connected
|
||||
if (!isPersonalWalletConnected) {
|
||||
const _props: ConnectUIProps = {
|
||||
goBack: () => {
|
||||
setIsPersonalWalletConnected(false);
|
||||
setPersonalWalletConfig(undefined);
|
||||
},
|
||||
connected() {
|
||||
setPersonalWalletConfig(undefined);
|
||||
setIsPersonalWalletConnected(true);
|
||||
setIsConnectingToPersonalWallet(false);
|
||||
},
|
||||
isOpen: props.isOpen,
|
||||
hide: props.hide,
|
||||
@@ -76,25 +113,11 @@ export const SafeConnectUI = (
|
||||
return <HeadlessConnectUI {..._props} />;
|
||||
}
|
||||
|
||||
if (!activeWallet) {
|
||||
return (
|
||||
<SelectpersonalWallet
|
||||
personalWallets={props.personalWallets}
|
||||
onBack={props.goBack}
|
||||
safeWallet={props.walletConfig}
|
||||
selectWallet={setPersonalWalletConfig}
|
||||
renderBackButton={props.supportedWallets.length > 1}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// if personal wallet is selected and connected
|
||||
return (
|
||||
<SelectAccount
|
||||
renderBackButton={props.supportedWallets.length > 1}
|
||||
onBack={() => {
|
||||
disconnect();
|
||||
props.goBack();
|
||||
}}
|
||||
onBack={goBackToMain}
|
||||
onConnect={props.connected}
|
||||
safeWalletConfig={props.walletConfig}
|
||||
/>
|
||||
|
||||
@@ -4,13 +4,9 @@ import { Button } from "../../../components/buttons";
|
||||
import { iconSize, spacing, fontSize } from "../../../design-system";
|
||||
import { ExclamationTriangleIcon } from "@radix-ui/react-icons";
|
||||
import {
|
||||
useChain,
|
||||
useConnect,
|
||||
useConnectionStatus,
|
||||
useNetworkMismatch,
|
||||
useWalletContext,
|
||||
useWallet,
|
||||
useSwitchChain,
|
||||
WalletConfig,
|
||||
} from "@thirdweb-dev/react-core";
|
||||
import { useCallback, useContext, useEffect, useRef, useState } from "react";
|
||||
@@ -24,31 +20,32 @@ export const SmartWalletConnecting: React.FC<{
|
||||
onBack: () => void;
|
||||
onConnect: () => void;
|
||||
smartWallet: SmartWalletConfig;
|
||||
personalWallet: WalletConfig;
|
||||
personalWalletConfig: WalletConfig;
|
||||
}> = (props) => {
|
||||
const locale = useTWLocale().wallets.smartWallet;
|
||||
const activeWallet = useWallet(); // personal wallet
|
||||
|
||||
const connect = useConnect();
|
||||
const connectedChain = useChain();
|
||||
const targetChain = useWalletContext().activeChain;
|
||||
|
||||
const mismatch = useNetworkMismatch();
|
||||
|
||||
const [connectError, setConnectError] = useState(false);
|
||||
// personal wallet
|
||||
const { personalWalletInfo } = useWalletContext();
|
||||
const personalWallet = personalWalletInfo?.wallet;
|
||||
const personalWalletChainId = personalWalletInfo?.chainId;
|
||||
const [switchError, setSwitchError] = useState(false);
|
||||
const switchPersonalWalletChain = personalWalletInfo.switchChain;
|
||||
const [switchingNetwork, setSwitchingNetwork] = useState(false);
|
||||
|
||||
// smart wallet
|
||||
const connect = useConnect();
|
||||
const targetChain = useWalletContext().activeChain;
|
||||
const mismatch = targetChain.chainId !== personalWalletChainId;
|
||||
const [connectError, setConnectError] = useState(false);
|
||||
const connectionStatus = useConnectionStatus();
|
||||
|
||||
const { onConnect } = props;
|
||||
const connectStarted = useRef(false);
|
||||
|
||||
const switchChain = useSwitchChain();
|
||||
const modalSize = useContext(ModalConfigCtx).modalSize;
|
||||
|
||||
const handleConnect = useCallback(async () => {
|
||||
if (!activeWallet || !connectedChain || connectStarted.current) {
|
||||
if (!personalWallet || !personalWalletChainId || connectStarted.current) {
|
||||
return;
|
||||
}
|
||||
setConnectError(false);
|
||||
@@ -56,20 +53,26 @@ export const SmartWalletConnecting: React.FC<{
|
||||
try {
|
||||
connectStarted.current = true;
|
||||
await connect(props.smartWallet, {
|
||||
personalWallet: activeWallet,
|
||||
personalWallet: personalWallet,
|
||||
});
|
||||
onConnect();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
setConnectError(true);
|
||||
}
|
||||
}, [activeWallet, connectedChain, connect, props.smartWallet, onConnect]);
|
||||
}, [
|
||||
personalWallet,
|
||||
personalWalletChainId,
|
||||
connect,
|
||||
props.smartWallet,
|
||||
onConnect,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mismatch) {
|
||||
handleConnect();
|
||||
}
|
||||
}, [mismatch, handleConnect, activeWallet, connectedChain]);
|
||||
}, [mismatch, handleConnect, personalWallet, personalWalletChainId]);
|
||||
|
||||
if (!connectError && (connectionStatus === "connecting" || !mismatch)) {
|
||||
return (
|
||||
@@ -111,8 +114,8 @@ export const SmartWalletConnecting: React.FC<{
|
||||
<Container fullHeight animate="fadein" flex="column">
|
||||
<Container p="lg">
|
||||
<ModalHeader
|
||||
title={props.personalWallet.meta.name}
|
||||
imgSrc={props.personalWallet.meta.iconURL}
|
||||
title={props.personalWalletConfig.meta.name}
|
||||
imgSrc={props.personalWalletConfig.meta.iconURL}
|
||||
onBack={props.onBack}
|
||||
/>
|
||||
</Container>
|
||||
@@ -150,14 +153,14 @@ export const SmartWalletConnecting: React.FC<{
|
||||
gap: spacing.sm,
|
||||
}}
|
||||
onClick={async () => {
|
||||
if (!activeWallet) {
|
||||
if (!personalWallet) {
|
||||
throw new Error("No active wallet");
|
||||
}
|
||||
setConnectError(false);
|
||||
setSwitchError(false);
|
||||
setSwitchingNetwork(true);
|
||||
try {
|
||||
await switchChain(targetChain.chainId);
|
||||
await switchPersonalWalletChain(targetChain.chainId);
|
||||
} catch (e) {
|
||||
setSwitchError(true);
|
||||
} finally {
|
||||
|
||||
@@ -2,12 +2,15 @@ import {
|
||||
WalletConfig,
|
||||
ConnectUIProps,
|
||||
WalletOptions,
|
||||
useWallet,
|
||||
useWalletContext,
|
||||
} from "@thirdweb-dev/react-core";
|
||||
import { SmartWallet } from "@thirdweb-dev/wallets";
|
||||
import { SmartWalletConfig, SmartWalletConfigOptions } from "./types";
|
||||
import { SmartWalletConnecting } from "./SmartWalletConnecting";
|
||||
import { HeadlessConnectUI } from "../headlessConnectUI";
|
||||
import { useRef, useEffect, useState } from "react";
|
||||
import { Container } from "../../../components/basic";
|
||||
import { Spinner } from "../../../components/Spinner";
|
||||
|
||||
export const smartWallet = (
|
||||
wallet: WalletConfig<any>,
|
||||
@@ -34,17 +37,53 @@ export const smartWallet = (
|
||||
export const SmartConnectUI = (
|
||||
props: ConnectUIProps<SmartWallet> & { personalWallet: WalletConfig },
|
||||
) => {
|
||||
const activeWallet = useWallet();
|
||||
const {
|
||||
setIsConnectingToPersonalWallet,
|
||||
personalWalletInfo,
|
||||
isConnectingToPersonalWallet,
|
||||
} = useWalletContext();
|
||||
const { walletConfig } = props;
|
||||
const [isPersonalWalletConnected, setIsPersonalWalletConnected] =
|
||||
useState(false);
|
||||
|
||||
// hide the connection of personal wallet
|
||||
const mounted = useRef(false);
|
||||
useEffect(() => {
|
||||
if (!mounted.current) {
|
||||
setIsConnectingToPersonalWallet(true);
|
||||
mounted.current = true;
|
||||
}
|
||||
}, [setIsConnectingToPersonalWallet]);
|
||||
|
||||
const PersonalWalletConfig = props.personalWallet;
|
||||
|
||||
if (!activeWallet) {
|
||||
if (!isPersonalWalletConnected) {
|
||||
// if the isConnectingToPersonalWallet is not yet updated
|
||||
if (!isConnectingToPersonalWallet) {
|
||||
return (
|
||||
<Container
|
||||
flex="row"
|
||||
center="both"
|
||||
style={{
|
||||
minHeight: "250px",
|
||||
}}
|
||||
>
|
||||
<Spinner size="xl" color="accentText" />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
const _props: ConnectUIProps = {
|
||||
...props,
|
||||
walletConfig: PersonalWalletConfig,
|
||||
connected: () => {
|
||||
// override to no-op
|
||||
setIsPersonalWalletConnected(true);
|
||||
setIsConnectingToPersonalWallet(false);
|
||||
},
|
||||
goBack() {
|
||||
personalWalletInfo.disconnect();
|
||||
setIsConnectingToPersonalWallet(false);
|
||||
props.goBack();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -57,10 +96,14 @@ export const SmartConnectUI = (
|
||||
|
||||
return (
|
||||
<SmartWalletConnecting
|
||||
onBack={props.goBack}
|
||||
onBack={() => {
|
||||
personalWalletInfo.disconnect();
|
||||
setIsConnectingToPersonalWallet(false);
|
||||
props.goBack();
|
||||
}}
|
||||
onConnect={props.connected}
|
||||
smartWallet={walletConfig}
|
||||
personalWallet={props.personalWallet}
|
||||
personalWalletConfig={props.personalWallet}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 4.0.16
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1974](https://github.com/thirdweb-dev/js/pull/1974) [`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4) Thanks [@adam-maj](https://github.com/adam-maj)! - Add support for new relayer and polygon relayer
|
||||
|
||||
## 4.0.15
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1935](https://github.com/thirdweb-dev/js/pull/1935) [`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Resolve diamond pattern facets ABIs
|
||||
|
||||
## 4.0.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "4.0.14",
|
||||
"version": "4.0.16",
|
||||
"description": "The main thirdweb SDK.",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Abi, AbiSchema } from "../../schema/contracts/custom";
|
||||
import { SDKOptions } from "../../schema/sdk-options";
|
||||
import { isExtensionEnabled } from "../feature-detection/isFeatureEnabled";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { providers } from "ethers";
|
||||
import { Contract, providers } from "ethers";
|
||||
import { joinABIs } from "./joinABIs";
|
||||
import { getPluginABI } from "./getPluginABI";
|
||||
|
||||
@@ -24,7 +24,7 @@ export async function getCompositeABI(
|
||||
let pluginABIs: Abi[] = [];
|
||||
|
||||
try {
|
||||
// check if contract is plugin-pattern
|
||||
// check if contract is plugin-pattern / dynamic
|
||||
const isPluginRouter: boolean = isExtensionEnabled(
|
||||
AbiSchema.parse(abi),
|
||||
"PluginRouter",
|
||||
@@ -33,6 +33,13 @@ export async function getCompositeABI(
|
||||
AbiSchema.parse(abi),
|
||||
"DynamicContract",
|
||||
);
|
||||
|
||||
// check if the contract has fallback function - we'll further check for diamond pattern if needed
|
||||
const isFallback: boolean = isExtensionEnabled(
|
||||
AbiSchema.parse(abi),
|
||||
"Fallback",
|
||||
);
|
||||
|
||||
if (isbaseRouter) {
|
||||
const contract = new ContractWrapper(
|
||||
provider,
|
||||
@@ -68,6 +75,21 @@ export async function getCompositeABI(
|
||||
|
||||
// get ABIs of extension contracts
|
||||
pluginABIs = await getPluginABI(plugins as any[], provider, storage);
|
||||
} else if (isFallback) {
|
||||
// check if diamond pattern
|
||||
const dimaondAbi = [
|
||||
"function facets() external view returns (tuple(address,bytes4[])[])",
|
||||
];
|
||||
const contract = new Contract(address, dimaondAbi, provider);
|
||||
|
||||
// get facets
|
||||
const facets = await contract.facets();
|
||||
|
||||
// filter facet addresses
|
||||
const facetAddresses = facets.map((item: any) => item[0]);
|
||||
|
||||
// get ABI of facets
|
||||
pluginABIs = await getPluginABI(facetAddresses, provider, storage);
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ import {
|
||||
FEATURE_ACCOUNT_FACTORY,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
FEATURE_ACCOUNT_PERMISSIONS_V1,
|
||||
FEATURE_FALLBACK,
|
||||
} from "./thirdweb-features";
|
||||
|
||||
/**
|
||||
@@ -148,7 +149,8 @@ export type Feature =
|
||||
| typeof FEATURE_ACCOUNT
|
||||
| typeof FEATURE_ACCOUNT_PERMISSIONS
|
||||
| typeof FEATURE_ACCOUNT_PERMISSIONS_V1
|
||||
| typeof FEATURE_ACCOUNT_FACTORY;
|
||||
| typeof FEATURE_ACCOUNT_FACTORY
|
||||
| typeof FEATURE_FALLBACK;
|
||||
|
||||
export type Extension = Feature;
|
||||
|
||||
@@ -197,4 +199,5 @@ export const SUPPORTED_FEATURES: Record<string, Feature> = /* @__PURE__ */ {
|
||||
[FEATURE_ACCOUNT.name]: FEATURE_ACCOUNT,
|
||||
[FEATURE_NFT_LOYALTY_CARD.name]: FEATURE_NFT_LOYALTY_CARD,
|
||||
[FEATURE_NFT_UPDATABLE_METADATA.name]: FEATURE_NFT_UPDATABLE_METADATA,
|
||||
[FEATURE_FALLBACK.name]: FEATURE_FALLBACK,
|
||||
};
|
||||
|
||||
@@ -19,6 +19,13 @@ import IAccountPermissions from "@thirdweb-dev/contracts-js/dist/abis/IAccountPe
|
||||
import IAccountPermissionsV1 from "@thirdweb-dev/contracts-js/dist/abis/IAccountPermissions_V1.json";
|
||||
import IAccount from "@thirdweb-dev/contracts-js/dist/abis/IAccount.json";
|
||||
|
||||
export const fallbackAbi = [
|
||||
{
|
||||
stateMutability: "payable",
|
||||
type: "fallback",
|
||||
},
|
||||
];
|
||||
|
||||
export const getAllPluginsAbi = [
|
||||
{
|
||||
inputs: [],
|
||||
@@ -370,3 +377,14 @@ export const FEATURE_AIRDROP_ERC1155 = {
|
||||
abis: [IAirdropERC1155],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
export const FEATURE_FALLBACK = {
|
||||
name: "Fallback",
|
||||
namespace: "fallback",
|
||||
docLinks: {
|
||||
sdk: "",
|
||||
contracts: "",
|
||||
},
|
||||
abis: [fallbackAbi],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
@@ -333,7 +333,8 @@ export class ContractWrapper<
|
||||
if (
|
||||
this.options?.gasless &&
|
||||
("openzeppelin" in this.options.gasless ||
|
||||
"biconomy" in this.options.gasless)
|
||||
"biconomy" in this.options.gasless ||
|
||||
"engine" in this.options.gasless)
|
||||
) {
|
||||
if (fn === "multicall" && Array.isArray(args[0]) && args[0].length > 0) {
|
||||
const from = await this.getSignerAddress();
|
||||
@@ -632,8 +633,211 @@ export class ContractWrapper<
|
||||
): Promise<string> {
|
||||
if (this.options.gasless && "biconomy" in this.options.gasless) {
|
||||
return this.biconomySendFunction(transaction);
|
||||
} else if (this.options.gasless && "openzeppelin" in this.options.gasless) {
|
||||
return this.defenderSendFunction(transaction);
|
||||
}
|
||||
return this.engineSendFunction(transaction);
|
||||
}
|
||||
|
||||
private async engineSendFunction(
|
||||
transaction: GaslessTransaction,
|
||||
): Promise<string> {
|
||||
invariant(
|
||||
this.options.gasless && "engine" in this.options.gasless,
|
||||
"calling engine gasless transaction without engine config in the SDK options",
|
||||
);
|
||||
|
||||
const request = await this.enginePrepareRequest(transaction);
|
||||
|
||||
const res = await fetch(this.options.gasless.engine.relayerUrl, {
|
||||
...request,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(data.error?.message || JSON.stringify(data.error));
|
||||
}
|
||||
|
||||
const queueId = data.result.queueId as string;
|
||||
const engineUrl =
|
||||
this.options.gasless.engine.relayerUrl.split("/relayer/")[0];
|
||||
const startTime = Date.now();
|
||||
while (true) {
|
||||
const txRes = await fetch(`${engineUrl}/transaction/status/${queueId}`);
|
||||
const txData = await txRes.json();
|
||||
|
||||
if (txData.result.transactionHash) {
|
||||
return txData.result.transactionHash as string;
|
||||
}
|
||||
|
||||
// Time out after 30s
|
||||
if (Date.now() - startTime > 30 * 1000) {
|
||||
throw new Error("timeout");
|
||||
}
|
||||
|
||||
// Poll to check if the transaction was mined
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
}
|
||||
}
|
||||
|
||||
private async enginePrepareRequest(transaction: GaslessTransaction) {
|
||||
const signer = this.getSigner();
|
||||
const provider = this.getProvider();
|
||||
const storage = this.storage;
|
||||
|
||||
invariant(signer, "signer is not set");
|
||||
|
||||
try {
|
||||
const { abi } = await fetchContractMetadataFromAddress(
|
||||
transaction.to,
|
||||
provider,
|
||||
storage,
|
||||
);
|
||||
|
||||
const chainId = (await provider.getNetwork()).chainId;
|
||||
const contract = new ContractWrapper(
|
||||
signer,
|
||||
transaction.to,
|
||||
abi,
|
||||
{},
|
||||
storage,
|
||||
);
|
||||
if (abi.find((item) => item.name === "executeMetaTransaction")) {
|
||||
const name: string = await contract.call("name", []);
|
||||
|
||||
const domain = {
|
||||
name,
|
||||
version: "1",
|
||||
salt: "0x" + chainId.toString(16).padStart(64, "0"), // Use 64 length hex chain id as salt
|
||||
verifyingContract: transaction.to,
|
||||
};
|
||||
|
||||
const types = {
|
||||
MetaTransaction: [
|
||||
{ name: "nonce", type: "uint256" },
|
||||
{ name: "from", type: "address" },
|
||||
{ name: "functionSignature", type: "bytes" },
|
||||
],
|
||||
};
|
||||
|
||||
const nonce = await contract.call("getNonce", [transaction.from]);
|
||||
const message = {
|
||||
nonce: nonce,
|
||||
from: transaction.from,
|
||||
functionSignature: transaction.data,
|
||||
};
|
||||
|
||||
const { signature } = await signTypedDataInternal(
|
||||
signer,
|
||||
domain,
|
||||
types,
|
||||
message,
|
||||
);
|
||||
|
||||
return {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
type: "execute-meta-transaction",
|
||||
request: {
|
||||
from: transaction.from,
|
||||
to: transaction.to,
|
||||
data: transaction.data,
|
||||
},
|
||||
signature,
|
||||
}),
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
|
||||
if (
|
||||
transaction.functionName === "approve" &&
|
||||
transaction.functionArgs.length === 2
|
||||
) {
|
||||
const spender = transaction.functionArgs[0];
|
||||
const amount = transaction.functionArgs[1];
|
||||
// TODO: support DAI permit by signDAIPermit
|
||||
const { message: permit, signature: sig } = await signEIP2612Permit(
|
||||
signer,
|
||||
transaction.to,
|
||||
transaction.from,
|
||||
spender,
|
||||
amount,
|
||||
);
|
||||
|
||||
const message = {
|
||||
to: transaction.to,
|
||||
owner: permit.owner,
|
||||
spender: permit.spender,
|
||||
value: BigNumber.from(permit.value).toString(),
|
||||
nonce: BigNumber.from(permit.nonce).toString(),
|
||||
deadline: BigNumber.from(permit.deadline).toString(),
|
||||
};
|
||||
|
||||
return {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
type: "permit",
|
||||
request: message,
|
||||
signature: sig,
|
||||
}),
|
||||
};
|
||||
} else {
|
||||
const forwarderAddress =
|
||||
CONTRACT_ADDRESSES[
|
||||
transaction.chainId as keyof typeof CONTRACT_ADDRESSES
|
||||
].openzeppelinForwarder ||
|
||||
(await computeForwarderAddress(provider, storage));
|
||||
const ForwarderABI = (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/Forwarder.json")
|
||||
).default;
|
||||
|
||||
const forwarder = new Contract(forwarderAddress, ForwarderABI, provider);
|
||||
const nonce = await getAndIncrementNonce(forwarder, "getNonce", [
|
||||
transaction.from,
|
||||
]);
|
||||
|
||||
const domain = {
|
||||
name: "GSNv2 Forwarder",
|
||||
version: "0.0.1",
|
||||
chainId: transaction.chainId,
|
||||
verifyingContract: forwarderAddress,
|
||||
};
|
||||
const types = {
|
||||
ForwardRequest,
|
||||
};
|
||||
|
||||
const message = {
|
||||
from: transaction.from,
|
||||
to: transaction.to,
|
||||
value: BigNumber.from(0).toString(),
|
||||
gas: BigNumber.from(transaction.gasLimit).toString(),
|
||||
nonce: BigNumber.from(nonce).toString(),
|
||||
data: transaction.data,
|
||||
};
|
||||
|
||||
const { signature: sig } = await signTypedDataInternal(
|
||||
signer,
|
||||
domain,
|
||||
types,
|
||||
message,
|
||||
);
|
||||
const signature: BytesLike = sig;
|
||||
|
||||
return {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
type: "forward",
|
||||
request: message,
|
||||
signature,
|
||||
forwarderAddress,
|
||||
}),
|
||||
};
|
||||
}
|
||||
return this.defenderSendFunction(transaction);
|
||||
}
|
||||
|
||||
private async biconomySendFunction(
|
||||
|
||||
@@ -44,6 +44,8 @@ import { signTypedDataInternal } from "../../common/sign";
|
||||
import { BytesLike } from "ethers";
|
||||
import { CONTRACT_ADDRESSES } from "../../constants/addresses/CONTRACT_ADDRESSES";
|
||||
import { getContractAddressByChainId } from "../../constants/addresses/getContractAddressByChainId";
|
||||
import { getCompositeABI } from "../../common/plugin/getCompositePluginABI";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
|
||||
abstract class TransactionContext {
|
||||
protected args: any[];
|
||||
@@ -1019,29 +1021,76 @@ async function enginePrepareRequest(
|
||||
provider: providers.Provider,
|
||||
storage: ThirdwebStorage,
|
||||
) {
|
||||
const forwarderAddress =
|
||||
CONTRACT_ADDRESSES[transaction.chainId as keyof typeof CONTRACT_ADDRESSES]
|
||||
.openzeppelinForwarder ||
|
||||
(await computeForwarderAddress(provider, storage));
|
||||
const ForwarderABI = (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/Forwarder.json")
|
||||
).default;
|
||||
try {
|
||||
const metadata = await fetchContractMetadataFromAddress(
|
||||
transaction.to,
|
||||
provider,
|
||||
storage,
|
||||
);
|
||||
|
||||
const forwarder = new Contract(forwarderAddress, ForwarderABI, provider);
|
||||
const nonce = await getAndIncrementNonce(forwarder, "getNonce", [
|
||||
transaction.from,
|
||||
]);
|
||||
const chainId = (await provider.getNetwork()).chainId;
|
||||
const abi = await getCompositeABI(
|
||||
transaction.to,
|
||||
metadata.abi,
|
||||
provider,
|
||||
{},
|
||||
storage,
|
||||
);
|
||||
const contract = new ContractWrapper(
|
||||
signer,
|
||||
transaction.to,
|
||||
abi,
|
||||
{},
|
||||
storage,
|
||||
);
|
||||
if (abi.find((item) => item.name === "executeMetaTransaction")) {
|
||||
const name: string = await contract.call("name", []);
|
||||
|
||||
const domain = {
|
||||
name: "GSNv2 Forwarder",
|
||||
version: "0.0.1",
|
||||
chainId: transaction.chainId,
|
||||
verifyingContract: forwarderAddress,
|
||||
};
|
||||
const types = {
|
||||
ForwardRequest,
|
||||
};
|
||||
let message: ForwardRequestMessage | PermitRequestMessage;
|
||||
const domain = {
|
||||
name,
|
||||
version: "1",
|
||||
salt: "0x" + chainId.toString(16).padStart(64, "0"), // Use 64 length hex chain id as salt
|
||||
verifyingContract: transaction.to,
|
||||
};
|
||||
|
||||
const types = {
|
||||
MetaTransaction: [
|
||||
{ name: "nonce", type: "uint256" },
|
||||
{ name: "from", type: "address" },
|
||||
{ name: "functionSignature", type: "bytes" },
|
||||
],
|
||||
};
|
||||
|
||||
const nonce = await contract.call("getNonce", [transaction.from]);
|
||||
const message = {
|
||||
nonce: nonce,
|
||||
from: transaction.from,
|
||||
functionSignature: transaction.data,
|
||||
};
|
||||
|
||||
const { signature } = await signTypedDataInternal(
|
||||
signer,
|
||||
domain,
|
||||
types,
|
||||
message,
|
||||
);
|
||||
|
||||
return {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
type: "execute-meta-transaction",
|
||||
request: {
|
||||
from: transaction.from,
|
||||
to: transaction.to,
|
||||
data: transaction.data,
|
||||
},
|
||||
signature,
|
||||
}),
|
||||
};
|
||||
}
|
||||
} catch {
|
||||
// no-op
|
||||
}
|
||||
|
||||
if (
|
||||
transaction.functionName === "approve" &&
|
||||
@@ -1058,18 +1107,13 @@ async function enginePrepareRequest(
|
||||
amount,
|
||||
);
|
||||
|
||||
const { r, s, v } = utils.splitSignature(sig);
|
||||
|
||||
message = {
|
||||
const message = {
|
||||
to: transaction.to,
|
||||
owner: permit.owner,
|
||||
spender: permit.spender,
|
||||
value: BigNumber.from(permit.value).toString(),
|
||||
nonce: BigNumber.from(permit.nonce).toString(),
|
||||
deadline: BigNumber.from(permit.deadline).toString(),
|
||||
r,
|
||||
s,
|
||||
v,
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -1077,10 +1121,34 @@ async function enginePrepareRequest(
|
||||
body: JSON.stringify({
|
||||
type: "permit",
|
||||
request: message,
|
||||
signature: sig,
|
||||
}),
|
||||
};
|
||||
} else {
|
||||
message = {
|
||||
const forwarderAddress =
|
||||
CONTRACT_ADDRESSES[transaction.chainId as keyof typeof CONTRACT_ADDRESSES]
|
||||
.openzeppelinForwarder ||
|
||||
(await computeForwarderAddress(provider, storage));
|
||||
const ForwarderABI = (
|
||||
await import("@thirdweb-dev/contracts-js/dist/abis/Forwarder.json")
|
||||
).default;
|
||||
|
||||
const forwarder = new Contract(forwarderAddress, ForwarderABI, provider);
|
||||
const nonce = await getAndIncrementNonce(forwarder, "getNonce", [
|
||||
transaction.from,
|
||||
]);
|
||||
|
||||
const domain = {
|
||||
name: "GSNv2 Forwarder",
|
||||
version: "0.0.1",
|
||||
chainId: transaction.chainId,
|
||||
verifyingContract: forwarderAddress,
|
||||
};
|
||||
const types = {
|
||||
ForwardRequest,
|
||||
};
|
||||
|
||||
const message = {
|
||||
from: transaction.from,
|
||||
to: transaction.to,
|
||||
value: BigNumber.from(0).toString(),
|
||||
|
||||
@@ -173,6 +173,7 @@ export { fetchSnapshotEntryForAddress } from "./common/claim-conditions/fetchSna
|
||||
export { getCachedAbiForContract } from "./common/abi";
|
||||
export * from "./common/ens/resolveEns";
|
||||
export * from "./common/ens/resolveAddress";
|
||||
export * from "./common/sign";
|
||||
//#endregion @r/packages/sdk/src/evm/common/*
|
||||
|
||||
//#region @r/packages/sdk/src/evm/constants/*
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @thirdweb-dev/service-utils
|
||||
|
||||
## 0.4.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1988](https://github.com/thirdweb-dev/js/pull/1988) [`17fe41d4`](https://github.com/thirdweb-dev/js/commit/17fe41d42bf99ccc83669557dc48d6fd22a5a04e) Thanks [@assimovt](https://github.com/assimovt)! - fix api key caching ttl
|
||||
|
||||
## 0.4.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1976](https://github.com/thirdweb-dev/js/pull/1976) [`8ff87be6`](https://github.com/thirdweb-dev/js/commit/8ff87be631f1754b15e7ec4556d51e8eec5cbd18) Thanks [@arcoraven](https://github.com/arcoraven)! - Handle if error parsing JSON response in authorization helper
|
||||
|
||||
## 0.4.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/service-utils",
|
||||
"version": "0.4.8",
|
||||
"version": "0.4.10",
|
||||
"main": "dist/thirdweb-dev-service-utils.cjs.js",
|
||||
"module": "dist/thirdweb-dev-service-utils.esm.js",
|
||||
"exports": {
|
||||
|
||||
@@ -80,17 +80,16 @@ export async function fetchKeyMetadataFromApi(
|
||||
"content-type": "application/json",
|
||||
},
|
||||
});
|
||||
let json: ApiResponse;
|
||||
|
||||
let text = "";
|
||||
try {
|
||||
json = await response.json();
|
||||
text = await response.text();
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Error fetching key metadata from API: ${response.status} - ${
|
||||
response.statusText
|
||||
} - ${await response.text()}`,
|
||||
`Error fetching key metadata from API: ${response.status} - ${text}`,
|
||||
);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
export async function fetchAccountFromApi(
|
||||
@@ -110,17 +109,16 @@ export async function fetchAccountFromApi(
|
||||
authorization: `Bearer ${jwt}`,
|
||||
},
|
||||
});
|
||||
let json: ApiAccountResponse;
|
||||
|
||||
let text = "";
|
||||
try {
|
||||
json = await response.json();
|
||||
text = await response.text();
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Error fetching account from API: ${response.status} - ${
|
||||
response.statusText
|
||||
} - ${await response.text()}`,
|
||||
`Error fetching account from API: ${response.status} - ${text}`,
|
||||
);
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
export async function updateRateLimitedAt(
|
||||
|
||||
@@ -76,9 +76,9 @@ export async function authorize(
|
||||
// if the difference is greater than the cacheTtl we want to ignore the cached data
|
||||
const now = Date.now();
|
||||
const diff = now - parsed.updatedAt;
|
||||
const cacheTtl = cacheOptions.cacheTtlSeconds * 1000;
|
||||
const cacheTtlMs = cacheOptions.cacheTtlSeconds * 1000;
|
||||
// only if the diff is less than the cacheTtl do we want to use the cached key
|
||||
if (diff < cacheTtl * 1000) {
|
||||
if (diff < cacheTtlMs) {
|
||||
accountMeta = parsed.apiKeyMeta;
|
||||
}
|
||||
} else {
|
||||
@@ -160,9 +160,9 @@ export async function authorize(
|
||||
// if the difference is greater than the cacheTtl we want to ignore the cached data
|
||||
const now = Date.now();
|
||||
const diff = now - parsed.updatedAt;
|
||||
const cacheTtl = cacheOptions.cacheTtlSeconds * 1000;
|
||||
const cacheTtlMs = cacheOptions.cacheTtlSeconds * 1000;
|
||||
// only if the diff is less than the cacheTtl do we want to use the cached key
|
||||
if (diff < cacheTtl * 1000) {
|
||||
if (diff < cacheTtlMs) {
|
||||
apiKeyMeta = parsed.apiKeyMeta;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# @thirdweb-dev/unity-js-bridge
|
||||
|
||||
## 0.2.105
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1985](https://github.com/thirdweb-dev/js/pull/1985) [`62cc785c`](https://github.com/thirdweb-dev/js/commit/62cc785c4ac6886fbc3b117bfb3d85dcf7523df1) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - [Unity] Support Add/Remove Admin and Create Session Key for SW
|
||||
|
||||
- [#1986](https://github.com/thirdweb-dev/js/pull/1986) [`e09221f1`](https://github.com/thirdweb-dev/js/commit/e09221f1e0cf89bf4a8b512c107cfa7cb9bcb82e) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - [Unity] Support Smart Wallet Account Override
|
||||
|
||||
- [#1981](https://github.com/thirdweb-dev/js/pull/1981) [`7a2e0eda`](https://github.com/thirdweb-dev/js/commit/7a2e0edab4e8c11d6c8bfc8970ed2768583a48d6) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - [Unity] Added Facebook and Apple providers
|
||||
|
||||
- Updated dependencies [[`36600dec`](https://github.com/thirdweb-dev/js/commit/36600decefca7b9ebb1924757b4186b559e439f6), [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1), [`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4)]:
|
||||
- @thirdweb-dev/auth@4.1.11
|
||||
- @thirdweb-dev/wallets@2.1.8
|
||||
- @thirdweb-dev/sdk@4.0.16
|
||||
|
||||
## 0.2.104
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881), [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863), [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96), [`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec)]:
|
||||
- @thirdweb-dev/wallets@2.1.7
|
||||
- @thirdweb-dev/sdk@4.0.15
|
||||
- @thirdweb-dev/auth@4.1.10
|
||||
|
||||
## 0.2.103
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/unity-js-bridge",
|
||||
"version": "0.2.103",
|
||||
"version": "0.2.105",
|
||||
"main": "dist/thirdweb-unity-bridge.js",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ThirdwebSDK, ChainIdOrName } from "@thirdweb-dev/sdk";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import {
|
||||
DAppMetaData,
|
||||
EmbeddedWalletOauthStrategy,
|
||||
SmartWalletConfig,
|
||||
walletIds,
|
||||
} from "@thirdweb-dev/wallets";
|
||||
@@ -17,7 +18,7 @@ import { MetaMaskWallet } from "@thirdweb-dev/wallets/evm/wallets/metamask";
|
||||
import { SmartWallet } from "@thirdweb-dev/wallets/evm/wallets/smart-wallet";
|
||||
import { WalletConnect } from "@thirdweb-dev/wallets/evm/wallets/wallet-connect";
|
||||
import { EmbeddedWallet } from "@thirdweb-dev/wallets/evm/wallets/embedded-wallet";
|
||||
import { BigNumber } from "ethers";
|
||||
import { BigNumber, ethers } from "ethers";
|
||||
import {
|
||||
Ethereum,
|
||||
defaultChains,
|
||||
@@ -74,6 +75,7 @@ interface TWBridge {
|
||||
email?: string,
|
||||
personalWallet?: PossibleWallet,
|
||||
authOptions?: string,
|
||||
smartWalletAccountOverride?: string,
|
||||
) => Promise<string>;
|
||||
disconnect: () => Promise<void>;
|
||||
switchNetwork: (chainId: string) => Promise<void>;
|
||||
@@ -87,6 +89,9 @@ interface TWBridge {
|
||||
) => void;
|
||||
fundWallet: (options: string) => Promise<void>;
|
||||
exportWallet: (password: string) => Promise<string>;
|
||||
smartWalletAddAdmin: (admin: string) => Promise<string | undefined>;
|
||||
smartWalletRemoveAdmin: (admin: string) => Promise<string | undefined>;
|
||||
smartWalletCreateSessionKey: (options: string) => Promise<string | undefined>;
|
||||
}
|
||||
|
||||
const w = window;
|
||||
@@ -252,6 +257,7 @@ class ThirdwebBridge implements TWBridge {
|
||||
email?: string,
|
||||
personalWallet: PossibleWallet = "localWallet",
|
||||
authOptions?: string,
|
||||
smartWalletAccountOverride?: string,
|
||||
) {
|
||||
if (!this.activeSDK) {
|
||||
throw new Error("SDK not initialized");
|
||||
@@ -281,15 +287,31 @@ class ThirdwebBridge implements TWBridge {
|
||||
chainId: chainIdNumber,
|
||||
authResult,
|
||||
});
|
||||
} else if (authOptionsParsed.authProvider === 1) {
|
||||
// GoogleManaged
|
||||
const googleWindow = this.openGoogleSignInWindow();
|
||||
if (!googleWindow) {
|
||||
throw new Error("Failed to open google login window");
|
||||
} else if (authOptionsParsed.authProvider < 4) {
|
||||
// OAuth
|
||||
let authProvider: EmbeddedWalletOauthStrategy;
|
||||
switch (authOptionsParsed.authProvider) {
|
||||
case 1:
|
||||
authProvider = "google";
|
||||
break;
|
||||
case 2:
|
||||
authProvider = "apple";
|
||||
break;
|
||||
case 3:
|
||||
authProvider = "facebook";
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
"Invalid auth provider: " + authOptionsParsed.authProvider,
|
||||
);
|
||||
}
|
||||
const popupWindow = this.openPopupWindow();
|
||||
if (!popupWindow) {
|
||||
throw new Error("Failed to open login window");
|
||||
}
|
||||
const authResult = await embeddedWallet.authenticate({
|
||||
strategy: "google",
|
||||
openedWindow: googleWindow,
|
||||
strategy: authProvider,
|
||||
openedWindow: popupWindow,
|
||||
closeOpenedWindow: (openedWindow) => {
|
||||
openedWindow.close();
|
||||
},
|
||||
@@ -298,7 +320,7 @@ class ThirdwebBridge implements TWBridge {
|
||||
chainId: chainIdNumber,
|
||||
authResult,
|
||||
});
|
||||
} else if (authOptionsParsed.authProvider === 2) {
|
||||
} else if (authOptionsParsed.authProvider === 4) {
|
||||
// CustomAuth
|
||||
const authResult = await embeddedWallet.authenticate({
|
||||
strategy: "jwt",
|
||||
@@ -326,7 +348,7 @@ class ThirdwebBridge implements TWBridge {
|
||||
);
|
||||
if (this.activeWallet) {
|
||||
// Pass EOA and reconnect to initialize smart wallet
|
||||
await this.initializeSmartWallet(smartWallet, this.activeWallet);
|
||||
await this.initializeSmartWallet(smartWallet, this.activeWallet, smartWalletAccountOverride);
|
||||
} else {
|
||||
// If EOA wallet is not connected, throw error
|
||||
throw new Error(
|
||||
@@ -603,11 +625,16 @@ class ThirdwebBridge implements TWBridge {
|
||||
public async initializeSmartWallet(
|
||||
sw: SmartWallet,
|
||||
personalWallet: AbstractClientWallet,
|
||||
accountAddress?: string,
|
||||
) {
|
||||
if(accountAddress) {
|
||||
console.debug("Initializing smart wallet with account address override:", accountAddress);
|
||||
}
|
||||
const personalWalletAddress = await personalWallet.getAddress();
|
||||
console.debug("Personal wallet address:", personalWalletAddress);
|
||||
await sw.connect({
|
||||
personalWallet,
|
||||
accountAddress: accountAddress,
|
||||
});
|
||||
if (sw.listenerCount("disconnect") === 1) {
|
||||
sw.on("disconnect", () => {
|
||||
@@ -627,10 +654,49 @@ class ThirdwebBridge implements TWBridge {
|
||||
return localWallet;
|
||||
}
|
||||
|
||||
public openGoogleSignInWindow() {
|
||||
public async smartWalletAddAdmin(admin: string) {
|
||||
if (!this.activeWallet) {
|
||||
throw new Error("No wallet connected");
|
||||
}
|
||||
const smartWallet = this.activeWallet as SmartWallet;
|
||||
const result = await smartWallet.addAdmin(admin);
|
||||
return JSON.stringify({ result: result }, bigNumberReplacer);
|
||||
}
|
||||
|
||||
public async smartWalletRemoveAdmin(admin: string) {
|
||||
if (!this.activeWallet) {
|
||||
throw new Error("No wallet connected");
|
||||
}
|
||||
const smartWallet = this.activeWallet as SmartWallet;
|
||||
const result = await smartWallet.removeAdmin(admin);
|
||||
return JSON.stringify({ result: result }, bigNumberReplacer);
|
||||
}
|
||||
|
||||
public async smartWalletCreateSessionKey(options: string) {
|
||||
if (!this.activeWallet) {
|
||||
throw new Error("No wallet connected");
|
||||
}
|
||||
const smartWallet = this.activeWallet as SmartWallet;
|
||||
const optionsParsed = JSON.parse(options);
|
||||
const approvedCallTargets = optionsParsed.approvedCallTargets;
|
||||
const nativeTokenLimitPerTransaction = ethers.utils.formatEther(
|
||||
optionsParsed.nativeTokenLimitPerTransactionInWei,
|
||||
);
|
||||
const startDate = BigNumber.from(optionsParsed.startDate).toNumber();
|
||||
const expirationDate = BigNumber.from(optionsParsed.expirationDate).toNumber();
|
||||
const result = await smartWallet.createSessionKey(optionsParsed.signerAddress, {
|
||||
approvedCallTargets: approvedCallTargets,
|
||||
nativeTokenLimitPerTransaction: nativeTokenLimitPerTransaction,
|
||||
startDate: startDate,
|
||||
expirationDate: expirationDate,
|
||||
});
|
||||
return JSON.stringify({ result: result }, bigNumberReplacer);
|
||||
}
|
||||
|
||||
public openPopupWindow() {
|
||||
const win = window.open("", undefined, "width=350, height=500");
|
||||
if (win) {
|
||||
win.document.title = "Sign In - Google Accounts";
|
||||
win.document.title = "Sign In - OAuth";
|
||||
win.document.body.innerHTML = `
|
||||
<svg class="loader" viewBox="0 0 50 50">
|
||||
<circle
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @thirdweb-dev/wallets
|
||||
|
||||
## 2.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1921](https://github.com/thirdweb-dev/js/pull/1921) [`1094a03a`](https://github.com/thirdweb-dev/js/commit/1094a03a04f3e7ade84af0f7a6073794c0904cb1) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Add Coin98 wallet
|
||||
|
||||
- Updated dependencies [[`7c45941b`](https://github.com/thirdweb-dev/js/commit/7c45941b57fb5a6f83be507ee64dbf885d93c9d4)]:
|
||||
- @thirdweb-dev/sdk@4.0.16
|
||||
|
||||
## 2.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1977](https://github.com/thirdweb-dev/js/pull/1977) [`6832b19f`](https://github.com/thirdweb-dev/js/commit/6832b19fc5eedc7c17e69940a361840de0a84881) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fix potential auto connection race condition
|
||||
|
||||
- [#1978](https://github.com/thirdweb-dev/js/pull/1978) [`7abb36a7`](https://github.com/thirdweb-dev/js/commit/7abb36a7ec39766dd7da03015ccef401ef7a7863) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Enforce authenticated users when getting embedded wallet signers
|
||||
|
||||
- [#1953](https://github.com/thirdweb-dev/js/pull/1953) [`1c4cbedb`](https://github.com/thirdweb-dev/js/commit/1c4cbedb6a3c9f9014ffbf83c02b998768958d96) Thanks [@nkrishang](https://github.com/nkrishang)! - Patched smart wallet signatures for updated eip-1271 sig verification
|
||||
|
||||
- Updated dependencies [[`e43beec3`](https://github.com/thirdweb-dev/js/commit/e43beec36a569211b6fe708107aa7dc04bff08ec)]:
|
||||
- @thirdweb-dev/sdk@4.0.15
|
||||
|
||||
## 2.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-wallets-evm-connectors-coin98.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets-evm-connectors-coin98.esm.js",
|
||||
"browser": {
|
||||
"./dist/thirdweb-dev-wallets-evm-connectors-coin98.esm.js": "./dist/thirdweb-dev-wallets-evm-connectors-coin98.browser.esm.js"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"main": "dist/thirdweb-dev-wallets-evm-wallets-coin98.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets-evm-wallets-coin98.esm.js",
|
||||
"browser": {
|
||||
"./dist/thirdweb-dev-wallets-evm-wallets-coin98.esm.js": "./dist/thirdweb-dev-wallets-evm-wallets-coin98.browser.esm.js"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/wallets",
|
||||
"version": "2.1.6",
|
||||
"version": "2.1.8",
|
||||
"main": "dist/thirdweb-dev-wallets.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets.esm.js",
|
||||
"browser": {
|
||||
@@ -77,6 +77,13 @@
|
||||
},
|
||||
"default": "./evm/wallets/blocto/dist/thirdweb-dev-wallets-evm-wallets-blocto.cjs.js"
|
||||
},
|
||||
"./evm/wallets/coin98": {
|
||||
"module": {
|
||||
"browser": "./evm/wallets/coin98/dist/thirdweb-dev-wallets-evm-wallets-coin98.browser.esm.js",
|
||||
"default": "./evm/wallets/coin98/dist/thirdweb-dev-wallets-evm-wallets-coin98.esm.js"
|
||||
},
|
||||
"default": "./evm/wallets/coin98/dist/thirdweb-dev-wallets-evm-wallets-coin98.cjs.js"
|
||||
},
|
||||
"./evm/wallets/engine": {
|
||||
"module": {
|
||||
"browser": "./evm/wallets/engine/dist/thirdweb-dev-wallets-evm-wallets-engine.browser.esm.js",
|
||||
@@ -245,6 +252,13 @@
|
||||
},
|
||||
"default": "./evm/connectors/blocto/dist/thirdweb-dev-wallets-evm-connectors-blocto.cjs.js"
|
||||
},
|
||||
"./evm/connectors/coin98": {
|
||||
"module": {
|
||||
"browser": "./evm/connectors/coin98/dist/thirdweb-dev-wallets-evm-connectors-coin98.browser.esm.js",
|
||||
"default": "./evm/connectors/coin98/dist/thirdweb-dev-wallets-evm-connectors-coin98.esm.js"
|
||||
},
|
||||
"default": "./evm/connectors/coin98/dist/thirdweb-dev-wallets-evm-connectors-coin98.cjs.js"
|
||||
},
|
||||
"./evm/connectors/signer": {
|
||||
"module": {
|
||||
"browser": "./evm/connectors/signer/dist/thirdweb-dev-wallets-evm-connectors-signer.browser.esm.js",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Ethereum } from "../injected/types";
|
||||
import { assertWindowEthereum } from "../../utils/assertWindowEthereum";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
coin98?: Ethereum;
|
||||
}
|
||||
}
|
||||
|
||||
export function getInjectedCoin98Provider(): Ethereum | undefined {
|
||||
if (typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (assertWindowEthereum(globalThis.window)) {
|
||||
if (window.coin98) {
|
||||
return window.ethereum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
import { AsyncStorage } from "../../../core/AsyncStorage";
|
||||
import {
|
||||
ConnectorNotFoundError,
|
||||
ResourceUnavailableError,
|
||||
RpcError,
|
||||
UserRejectedRequestError,
|
||||
} from "../../../lib/wagmi-core/errors";
|
||||
import { walletIds } from "../../constants/walletIds";
|
||||
import { InjectedConnector, InjectedConnectorOptions } from "../injected";
|
||||
import type { Chain } from "@thirdweb-dev/chains";
|
||||
import { utils } from "ethers";
|
||||
import { getInjectedCoin98Provider } from "./getInjectedCoin98Provider";
|
||||
|
||||
export type Coin98ConnectorOptions = InjectedConnectorOptions;
|
||||
|
||||
type Coin98ConnectorConstructorArg = {
|
||||
chains?: Chain[];
|
||||
connectorStorage: AsyncStorage;
|
||||
options?: Coin98ConnectorOptions;
|
||||
};
|
||||
|
||||
export class Coin98Connector extends InjectedConnector {
|
||||
readonly id = walletIds.coin98;
|
||||
|
||||
constructor(arg: Coin98ConnectorConstructorArg) {
|
||||
const defaultOptions = {
|
||||
name: "Coin98 Wallet",
|
||||
shimDisconnect: true,
|
||||
shimChainChangedDisconnect: true,
|
||||
getProvider: getInjectedCoin98Provider,
|
||||
};
|
||||
|
||||
const options = {
|
||||
...defaultOptions,
|
||||
...arg.options,
|
||||
};
|
||||
|
||||
super({
|
||||
chains: arg.chains,
|
||||
options,
|
||||
connectorStorage: arg.connectorStorage,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to injected Coin98Wallet provider
|
||||
*/
|
||||
async connect(options: { chainId?: number } = {}) {
|
||||
try {
|
||||
const provider = await this.getProvider();
|
||||
if (!provider) {
|
||||
throw new ConnectorNotFoundError();
|
||||
}
|
||||
|
||||
this.setupListeners();
|
||||
|
||||
// emit "connecting" event
|
||||
this.emit("message", { type: "connecting" });
|
||||
|
||||
// Attempt to show wallet select prompt with `wallet_requestPermissions` when
|
||||
// `shimDisconnect` is active and account is in disconnected state (flag in storage)
|
||||
let account: string | null = null;
|
||||
if (
|
||||
this.options?.shimDisconnect &&
|
||||
!Boolean(this.connectorStorage.getItem(this.shimDisconnectKey))
|
||||
) {
|
||||
account = await this.getAccount().catch(() => null);
|
||||
const isConnected = !!account;
|
||||
if (isConnected) {
|
||||
// Attempt to show another prompt for selecting wallet if already connected
|
||||
try {
|
||||
await provider.request({
|
||||
method: "wallet_requestPermissions",
|
||||
params: [{ eth_accounts: {} }],
|
||||
});
|
||||
} catch (error) {
|
||||
// Not all injected providers support `wallet_requestPermissions` (e.g. iOS).
|
||||
// Only bubble up error if user rejects request
|
||||
if (this.isUserRejectedRequestError(error)) {
|
||||
throw new UserRejectedRequestError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if account is not already set, request accounts and use the first account
|
||||
if (!account) {
|
||||
const accounts = await provider.request({
|
||||
method: "eth_requestAccounts",
|
||||
});
|
||||
account = utils.getAddress(accounts[0] as string);
|
||||
}
|
||||
|
||||
// get currently connected chainId
|
||||
let connectedChainId = await this.getChainId();
|
||||
// check if connected chain is unsupported
|
||||
let isUnsupported = this.isChainUnsupported(connectedChainId);
|
||||
|
||||
// if chainId is given, but does not match the currently connected chainId, switch to the given chainId
|
||||
if (options.chainId && connectedChainId !== options.chainId) {
|
||||
try {
|
||||
await this.switchChain(options.chainId);
|
||||
// recalculate the chainId and isUnsupported
|
||||
connectedChainId = options.chainId;
|
||||
isUnsupported = this.isChainUnsupported(options.chainId);
|
||||
} catch (e) {
|
||||
console.error(`Could not switch to chain id : ${options.chainId}`, e);
|
||||
}
|
||||
}
|
||||
|
||||
// if shimDisconnect is enabled
|
||||
if (this.options?.shimDisconnect) {
|
||||
// add shimDisconnectKey in storage - this signals that connector is "connected"
|
||||
await this.connectorStorage.setItem(this.shimDisconnectKey, "true");
|
||||
}
|
||||
|
||||
const connectionInfo = {
|
||||
chain: { id: connectedChainId, unsupported: isUnsupported },
|
||||
provider: provider,
|
||||
account,
|
||||
};
|
||||
|
||||
this.emit("connect", connectionInfo);
|
||||
return connectionInfo;
|
||||
} catch (error) {
|
||||
if (this.isUserRejectedRequestError(error)) {
|
||||
throw new UserRejectedRequestError(error);
|
||||
}
|
||||
if ((error as RpcError).code === -32002) {
|
||||
throw new ResourceUnavailableError(error);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ export class EmbeddedWalletConnector extends Connector<EmbeddedWalletConnectionA
|
||||
if (!this.user) {
|
||||
throw new Error("Embedded Wallet is not connected");
|
||||
}
|
||||
return this.user.walletAddress;
|
||||
return await this.getSigner().then((signer) => signer.getAddress());
|
||||
}
|
||||
|
||||
async isConnected(): Promise<boolean> {
|
||||
@@ -115,7 +115,7 @@ export class EmbeddedWalletConnector extends Connector<EmbeddedWalletConnectionA
|
||||
}
|
||||
|
||||
const user = await this.getUser();
|
||||
const signer = await user?.wallet.getEthersJsSigner({
|
||||
const signer = await user.wallet.getEthersJsSigner({
|
||||
rpcEndpoint: this.options.chain.rpc[0] || "", // TODO: handle chain.rpc being empty array
|
||||
});
|
||||
|
||||
@@ -180,11 +180,11 @@ export class EmbeddedWalletConnector extends Connector<EmbeddedWalletConnectionA
|
||||
this.emit("disconnect");
|
||||
};
|
||||
|
||||
async getUser(): Promise<InitializedUser | null> {
|
||||
private async getUser(): Promise<InitializedUser> {
|
||||
if (
|
||||
!this.user ||
|
||||
!this.user.wallet ||
|
||||
typeof this.user.wallet.getEthersJsSigner !== "function"
|
||||
!this.user.wallet.getEthersJsSigner // when serializing, functions are lost, need to rehydrate
|
||||
) {
|
||||
const embeddedWalletSdk = this.getEmbeddedWalletSDK();
|
||||
const user = await embeddedWalletSdk.getUser();
|
||||
@@ -193,27 +193,25 @@ export class EmbeddedWalletConnector extends Connector<EmbeddedWalletConnectionA
|
||||
this.user = user;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
// if logged out or unitialized, we can't get a signer, so throw an error
|
||||
throw new Error(
|
||||
"Embedded Wallet is not authenticated, please authenticate first",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.user;
|
||||
}
|
||||
|
||||
async getEmail() {
|
||||
// implicit call to set the user
|
||||
await this.getSigner();
|
||||
if (!this.user) {
|
||||
throw new Error("No user found, Embedded Wallet is not connected");
|
||||
}
|
||||
return this.user.authDetails.email;
|
||||
const user = await this.getUser();
|
||||
return user.authDetails.email;
|
||||
}
|
||||
|
||||
async getRecoveryInformation() {
|
||||
// implicit call to set the user
|
||||
await this.getSigner();
|
||||
if (!this.user) {
|
||||
throw new Error("No user found, Embedded Wallet is not connected");
|
||||
}
|
||||
return this.user.authDetails;
|
||||
const user = await this.getUser();
|
||||
return user.authDetails;
|
||||
}
|
||||
|
||||
async sendVerificationEmail({
|
||||
|
||||
@@ -64,5 +64,4 @@ export type AuthResult = {
|
||||
user?: InitializedUser;
|
||||
isNewUser?: boolean;
|
||||
needsRecoveryCode?: boolean;
|
||||
verifyOTP?: (otp: string, recoveryCode?: string) => Promise<AuthResult>;
|
||||
};
|
||||
|
||||
@@ -52,6 +52,7 @@ type InjectedProviderFlags = {
|
||||
isZerion?: true;
|
||||
isOkxWallet?: true;
|
||||
isCoreWallet?: true;
|
||||
isCoin98Wallet?: true;
|
||||
};
|
||||
type InjectedProviders = InjectedProviderFlags & {
|
||||
isMetaMask: true;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const walletIds = {
|
||||
blocto: "blocto",
|
||||
coin98: "coin98",
|
||||
coinbase: "coinbase",
|
||||
coreWallet: "coreWallet",
|
||||
frame: "frame",
|
||||
|
||||
@@ -53,6 +53,9 @@ export { getInjectedOKXProvider } from "./connectors/okx/getInjectedOKXProvider"
|
||||
export { CoreWallet, type CoreWalletOptions } from "./wallets/core-wallet";
|
||||
export { getInjectedCoreWalletProvider } from "./connectors/core-wallet/getInjectedCoreWalletProvider";
|
||||
|
||||
export { Coin98Wallet, type Coin98Options } from "./wallets/coin98";
|
||||
export { getInjectedCoin98Provider } from "./connectors/coin98/getInjectedCoin98Provider";
|
||||
|
||||
export type { Chain } from "@thirdweb-dev/chains";
|
||||
|
||||
// export the window ethereum util
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import type { WalletConnectConnector as WalletConnectConnectorType } from "../connectors/wallet-connect";
|
||||
import type { QRModalOptions } from "../connectors/wallet-connect/qrModalOptions";
|
||||
import { Connector, WagmiAdapter } from "../interfaces/connector";
|
||||
import { AbstractClientWallet, WalletOptions } from "./base";
|
||||
import type { Coin98Connector as Coin98ConnectorType } from "../connectors/coin98";
|
||||
import { walletIds } from "../constants/walletIds";
|
||||
import { TW_WC_PROJECT_ID } from "../constants/wc";
|
||||
import { getInjectedCoin98Provider } from "../connectors/coin98/getInjectedCoin98Provider";
|
||||
|
||||
type Coin98AdditionalOptions = {
|
||||
/**
|
||||
* Whether to open the default Wallet Connect QR code Modal for connecting to Coin98 app on mobile if Coin98 wallet is not injected when calling connect().
|
||||
*/
|
||||
qrcode?: boolean;
|
||||
|
||||
/**
|
||||
* When connecting Coin98 wallet using the QR Code - Wallet Connect connector is used which requires a project id.
|
||||
* This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com.
|
||||
*
|
||||
* https://docs.walletconnect.com/2.0/web3modal/options#projectid-required
|
||||
*/
|
||||
projectId?: string;
|
||||
|
||||
/**
|
||||
* options to customize the Wallet Connect QR Code Modal ( only relevant when qrcode is true )
|
||||
*
|
||||
* https://docs.walletconnect.com/2.0/web3modal/options
|
||||
*/
|
||||
qrModalOptions?: QRModalOptions;
|
||||
};
|
||||
|
||||
export type Coin98Options = WalletOptions<Coin98AdditionalOptions>;
|
||||
|
||||
type ConnectWithQrCodeArgs = {
|
||||
chainId?: number;
|
||||
onQrCodeUri: (uri: string) => void;
|
||||
onConnected: (accountAddress: string) => void;
|
||||
};
|
||||
|
||||
export class Coin98Wallet extends AbstractClientWallet<Coin98AdditionalOptions> {
|
||||
connector?: Connector;
|
||||
walletConnectConnector?: WalletConnectConnectorType;
|
||||
Coin98Connector?: Coin98ConnectorType;
|
||||
isInjected: boolean;
|
||||
|
||||
static id = walletIds.coin98 as string;
|
||||
|
||||
public get walletName() {
|
||||
return "Coin98 wallet" as const;
|
||||
}
|
||||
|
||||
constructor(options: Coin98Options) {
|
||||
super(Coin98Wallet.id, options);
|
||||
this.isInjected = !!getInjectedCoin98Provider();
|
||||
}
|
||||
|
||||
protected async getConnector(): Promise<Connector> {
|
||||
if (!this.connector) {
|
||||
// if Coin98 wallet is injected, use the injected connector
|
||||
// otherwise, use the wallet connect connector for using the Coin98 app on mobile via QR code scan
|
||||
|
||||
if (this.isInjected) {
|
||||
// import the connector dynamically
|
||||
const { Coin98Connector } = await import("../connectors/coin98");
|
||||
this.Coin98Connector = new Coin98Connector({
|
||||
chains: this.chains,
|
||||
connectorStorage: this.walletStorage,
|
||||
options: {
|
||||
shimDisconnect: true,
|
||||
},
|
||||
});
|
||||
|
||||
this.connector = new WagmiAdapter(this.Coin98Connector);
|
||||
} else {
|
||||
const { WalletConnectConnector } = await import(
|
||||
"../connectors/wallet-connect"
|
||||
);
|
||||
|
||||
const walletConnectConnector = new WalletConnectConnector({
|
||||
chains: this.chains,
|
||||
options: {
|
||||
projectId: this.options?.projectId || TW_WC_PROJECT_ID, // TODO,
|
||||
storage: this.walletStorage,
|
||||
qrcode: this.options?.qrcode,
|
||||
dappMetadata: this.dappMetadata,
|
||||
qrModalOptions: this.options?.qrModalOptions,
|
||||
},
|
||||
});
|
||||
|
||||
walletConnectConnector.getProvider().then((provider) => {
|
||||
provider.signer.client.on("session_request_sent", () => {
|
||||
this.emit("wc_session_request_sent");
|
||||
});
|
||||
});
|
||||
|
||||
// need to save this for getting the QR code URI
|
||||
this.walletConnectConnector = walletConnectConnector;
|
||||
this.connector = new WagmiAdapter(walletConnectConnector);
|
||||
}
|
||||
}
|
||||
|
||||
return this.connector;
|
||||
}
|
||||
|
||||
/**
|
||||
* connect to wallet with QR code
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* wallet.connectWithQrCode({
|
||||
* chainId: 1,
|
||||
* onQrCodeUri(qrCodeUri) {
|
||||
* // render the QR code with `qrCodeUri`
|
||||
* },
|
||||
* onConnected(accountAddress) {
|
||||
* // update UI to show connected state
|
||||
* },
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
async connectWithQrCode(options: ConnectWithQrCodeArgs) {
|
||||
await this.getConnector();
|
||||
const wcConnector = this.walletConnectConnector;
|
||||
|
||||
if (!wcConnector) {
|
||||
throw new Error("WalletConnect connector not found");
|
||||
}
|
||||
|
||||
const wcProvider = await wcConnector.getProvider();
|
||||
|
||||
// set a listener for display_uri event
|
||||
wcProvider.on("display_uri", (uri) => {
|
||||
options.onQrCodeUri(uri);
|
||||
});
|
||||
|
||||
// trigger connect flow
|
||||
this.connect({ chainId: options.chainId }).then(options.onConnected);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AbstractClientWallet, WalletOptions } from "./base";
|
||||
import { checkContractWalletSignature } from "./abstract";
|
||||
import type { ConnectParams } from "../interfaces/connector";
|
||||
import type {
|
||||
SmartWalletConfig,
|
||||
@@ -14,7 +15,8 @@ import {
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import { walletIds } from "../constants/walletIds";
|
||||
import { getValidChainRPCs } from "@thirdweb-dev/chains";
|
||||
import { providers, utils } from "ethers";
|
||||
import { providers, utils, Bytes, Signer } from "ethers";
|
||||
import { signTypedDataInternal } from "@thirdweb-dev/sdk";
|
||||
|
||||
// export types and utils for convenience
|
||||
export type * from "../connectors/smart-wallet/types";
|
||||
@@ -76,6 +78,67 @@ export class SmartWallet extends AbstractClientWallet<
|
||||
return this.connector?.personalWallet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the signature of the message
|
||||
*/
|
||||
public async signMessage(message: Bytes | string): Promise<string> {
|
||||
// Deploy smart wallet if needed
|
||||
const connector = await this.getConnector();
|
||||
await connector.deployIfNeeded();
|
||||
|
||||
const erc4337Signer = await this.getSigner();
|
||||
const chainId = await erc4337Signer.getChainId();
|
||||
const address = await connector.getAddress();
|
||||
|
||||
/**
|
||||
* We first try to sign the EIP-712 typed data i.e. the message mixed with the smart wallet's domain separator.
|
||||
* If this fails, we fallback to the legacy signing method.
|
||||
*/
|
||||
try {
|
||||
const result = await signTypedDataInternal(
|
||||
erc4337Signer,
|
||||
{
|
||||
name: "Account",
|
||||
version: "1",
|
||||
chainId,
|
||||
verifyingContract: address,
|
||||
},
|
||||
{ AccountMessage: [{ name: "message", type: "bytes" }] },
|
||||
{
|
||||
message: utils.defaultAbiCoder.encode(
|
||||
["bytes32"],
|
||||
[utils.hashMessage(message)],
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
const isValid = await checkContractWalletSignature(
|
||||
message as string,
|
||||
result.signature,
|
||||
address,
|
||||
chainId,
|
||||
);
|
||||
|
||||
if (!isValid) {
|
||||
throw new Error("Invalid signature");
|
||||
}
|
||||
|
||||
return result.signature;
|
||||
} catch {
|
||||
return await this.signMessageLegacy(erc4337Signer, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns the signature of the message (for legacy EIP-1271 signature verification)
|
||||
*/
|
||||
private async signMessageLegacy(
|
||||
signer: Signer,
|
||||
message: Bytes | string,
|
||||
): Promise<string> {
|
||||
return await signer.signMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the connected signer can execute a given transaction using the smart wallet.
|
||||
* @param transaction - the transaction to execute using the smart wallet.
|
||||
|
||||
Reference in New Issue
Block a user