Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26367c04f3 | ||
|
|
39e2af6f2f | ||
|
|
a5ba9e4fbf | ||
|
|
c9b0131acd | ||
|
|
34b315993c | ||
|
|
14d9a71c92 | ||
|
|
7f5311222b |
@@ -1,5 +1,19 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 3.2.31
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
|
||||
- @thirdweb-dev/wallets@1.1.14
|
||||
|
||||
## 3.2.30
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2)]:
|
||||
- @thirdweb-dev/wallets@1.1.13
|
||||
|
||||
## 3.2.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "3.2.29",
|
||||
"version": "3.2.31",
|
||||
"main": "dist/thirdweb-dev-auth.cjs.js",
|
||||
"module": "dist/thirdweb-dev-auth.esm.js",
|
||||
"browser": {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { Chain } from "../src/types";
|
||||
export default {
|
||||
"name": "Japan Open Chain Testnet",
|
||||
"chain": "JOCT",
|
||||
"rpc": [
|
||||
"https://japan-open-chain-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
|
||||
"https://rpc-1.testnet.japanopenchain.org:8545",
|
||||
"https://rpc-2.testnet.japanopenchain.org:8545"
|
||||
],
|
||||
"faucets": [],
|
||||
"nativeCurrency": {
|
||||
"name": "Japan Open Chain Testnet Token",
|
||||
"symbol": "JOCT",
|
||||
"decimals": 18
|
||||
},
|
||||
"infoURL": "https://www.japanopenchain.org/",
|
||||
"shortName": "joct",
|
||||
"chainId": 10081,
|
||||
"networkId": 10081,
|
||||
"explorers": [
|
||||
{
|
||||
"name": "Testnet Block Explorer",
|
||||
"url": "https://explorer.testnet.japanopenchain.org",
|
||||
"standard": "EIP3091"
|
||||
}
|
||||
],
|
||||
"testnet": true,
|
||||
"slug": "japan-open-chain-testnet"
|
||||
} as const satisfies Chain;
|
||||
@@ -1,5 +1,19 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.11.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117), [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
|
||||
- @thirdweb-dev/sdk@3.10.50
|
||||
|
||||
## 0.11.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696)]:
|
||||
- @thirdweb-dev/sdk@3.10.49
|
||||
|
||||
## 0.11.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.11.12",
|
||||
"version": "0.11.14",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @thirdweb-dev/react-core
|
||||
|
||||
## 3.14.31
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74), [`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117), [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
|
||||
- @thirdweb-dev/wallets@1.1.14
|
||||
- @thirdweb-dev/sdk@3.10.50
|
||||
- @thirdweb-dev/auth@3.2.31
|
||||
|
||||
## 3.14.30
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2), [`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696)]:
|
||||
- @thirdweb-dev/wallets@1.1.13
|
||||
- @thirdweb-dev/sdk@3.10.49
|
||||
- @thirdweb-dev/auth@3.2.30
|
||||
|
||||
## 3.14.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-core",
|
||||
"version": "3.14.29",
|
||||
"version": "3.14.31",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @thirdweb-dev/react-native-compat
|
||||
|
||||
## 0.2.50
|
||||
|
||||
## 0.2.49
|
||||
|
||||
## 0.2.48
|
||||
|
||||
## 0.2.47
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-native-compat",
|
||||
"description": "Shims for Thirdweb in React Native Projects",
|
||||
"version": "0.2.48",
|
||||
"version": "0.2.50",
|
||||
"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,27 @@
|
||||
# @thirdweb-dev/react-native
|
||||
|
||||
## 0.2.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74), [`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117), [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
|
||||
- @thirdweb-dev/wallets@1.1.14
|
||||
- @thirdweb-dev/sdk@3.10.50
|
||||
- @thirdweb-dev/react-core@3.14.31
|
||||
|
||||
## 0.2.49
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1479](https://github.com/thirdweb-dev/js/pull/1479) [`14d9a71c`](https://github.com/thirdweb-dev/js/commit/14d9a71c928019907618cc8694af0bc631e94b68) Thanks [@iketw](https://github.com/iketw)! - Deprecates magicWallet in favor of magicLink for consistency with our React package
|
||||
|
||||
- [#1477](https://github.com/thirdweb-dev/js/pull/1477) [`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Pass unauth'd RPCs to wallets when adding new chains
|
||||
|
||||
- Updated dependencies [[`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2), [`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696)]:
|
||||
- @thirdweb-dev/wallets@1.1.13
|
||||
- @thirdweb-dev/sdk@3.10.49
|
||||
- @thirdweb-dev/react-core@3.14.30
|
||||
|
||||
## 0.2.48
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-native",
|
||||
"version": "0.2.48",
|
||||
"version": "0.2.50",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -3,7 +3,12 @@ import Modal, { ModalProps } from "react-native-modal";
|
||||
// Populate with the data...
|
||||
export function TWModal(props: Partial<ModalProps>) {
|
||||
return (
|
||||
<Modal {...props} useNativeDriver hideModalContentWhileAnimating={true}>
|
||||
<Modal
|
||||
{...props}
|
||||
avoidKeyboard
|
||||
useNativeDriver
|
||||
hideModalContentWhileAnimating={true}
|
||||
>
|
||||
{props.children}
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@@ -15,8 +15,10 @@ export { TrustWallet, trustWallet } from "./wallets/wallets/trust-wallet";
|
||||
export { smartWallet } from "./wallets/wallets/smart-wallet";
|
||||
export { localWallet } from "./wallets/wallets/local-wallet";
|
||||
export { LocalWallet } from "./wallets/wallets/LocalWallet";
|
||||
export { magicWallet } from "./wallets/wallets/magic-wallet";
|
||||
export { MagicWallet } from "./wallets/wallets/MagicWallet";
|
||||
export { magicWallet } from "./wallets/wallets/magic-link";
|
||||
export { MagicWallet } from "./wallets/wallets/MagicLink";
|
||||
export { MagicLink } from "./wallets/wallets/MagicLink";
|
||||
export { magicLink } from "./wallets/wallets/magic-link";
|
||||
export * from "./wallets/wallets/WalletConnectV2";
|
||||
export type { WCMeta } from "./wallets/types/wc";
|
||||
|
||||
|
||||
@@ -7,47 +7,47 @@ import {
|
||||
useState,
|
||||
} from "react";
|
||||
import { useWalletConnectListener } from "../wallets/hooks/useWalletConnectListener";
|
||||
import { MagicWallet } from "../wallets/wallets/MagicWallet";
|
||||
import { MagicLink } from "../wallets/wallets/MagicLink";
|
||||
import { useWalletContext } from "@thirdweb-dev/react-core";
|
||||
|
||||
type DappContextType = {
|
||||
smartWallet?: SmartWallet;
|
||||
setSmartWallet?: (value?: SmartWallet) => void;
|
||||
magicWallet?: MagicWallet;
|
||||
setMagicWallet?: (value?: MagicWallet) => void;
|
||||
magicLink?: MagicLink;
|
||||
setMagicLink?: (value?: MagicLink) => void;
|
||||
};
|
||||
|
||||
const DappContext = createContext<DappContextType>({});
|
||||
|
||||
export const DappContextProvider = (props: React.PropsWithChildren) => {
|
||||
const [smartWallet, setSmartWallet] = useState<SmartWallet | undefined>();
|
||||
const [magicWallet, setMagicWallet] = useState<MagicWallet | undefined>();
|
||||
const [magicLink, setMagicLink] = useState<MagicLink | undefined>();
|
||||
const createdWalletInstance = useWalletContext().createdWalletInstance;
|
||||
|
||||
useEffect(() => {
|
||||
if (createdWalletInstance?.walletId === walletIds.magicLink) {
|
||||
setMagicWallet(createdWalletInstance as MagicWallet);
|
||||
setMagicLink(createdWalletInstance as MagicLink);
|
||||
}
|
||||
}, [createdWalletInstance, setMagicWallet]);
|
||||
}, [createdWalletInstance, setMagicLink]);
|
||||
|
||||
useWalletConnectListener();
|
||||
|
||||
const magicSDK = useCallback(() => {
|
||||
if (magicWallet) {
|
||||
const magic = magicWallet.getMagicSDK();
|
||||
if (magicLink) {
|
||||
const magic = magicLink.getMagicSDK();
|
||||
return <magic.Relayer />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [magicWallet]);
|
||||
}, [magicLink]);
|
||||
|
||||
return (
|
||||
<DappContext.Provider
|
||||
value={{
|
||||
smartWallet,
|
||||
setSmartWallet,
|
||||
magicWallet: magicWallet,
|
||||
setMagicWallet: setMagicWallet,
|
||||
magicLink: magicLink,
|
||||
setMagicLink: setMagicLink,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
@@ -66,11 +66,11 @@ export const useSmartWallet = () => {
|
||||
return [context.smartWallet, context.setSmartWallet] as const;
|
||||
};
|
||||
|
||||
export const useMagicWallet = () => {
|
||||
export const useMagicLink = () => {
|
||||
const context = useContext(DappContext);
|
||||
|
||||
return {
|
||||
magicWallet: context.magicWallet,
|
||||
setMagicWallet: context.setMagicWallet,
|
||||
magicLink: context.magicLink,
|
||||
setMagicLink: context.setMagicLink,
|
||||
} as const;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Chain, getValidChainRPCs } from "@thirdweb-dev/chains";
|
||||
import { WCMeta } from "../wallets/types/wc";
|
||||
|
||||
/**
|
||||
@@ -20,3 +21,19 @@ export function formatWalletConnectDisplayUri(
|
||||
}wc?uri=${encodedUri}`
|
||||
: `${uri}`;
|
||||
}
|
||||
|
||||
export function getValidPublicRPCUrl(chain: Chain) {
|
||||
return getValidChainRPCs(chain).map((rpc) => {
|
||||
try {
|
||||
const url = new URL(rpc);
|
||||
// remove client id from url
|
||||
if (url.hostname.endsWith(".thirdweb.com")) {
|
||||
url.pathname = "";
|
||||
url.search = "";
|
||||
}
|
||||
return url.toString();
|
||||
} catch (e) {
|
||||
return rpc;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import type {
|
||||
WalletMobileSDKProviderOptions,
|
||||
} from "@coinbase/wallet-mobile-sdk/build/WalletMobileSDKEVMProvider";
|
||||
import type { ConfigurationParams } from "@coinbase/wallet-mobile-sdk/src/CoinbaseWalletSDK.types";
|
||||
import { getValidChainRPCs } from "@thirdweb-dev/chains";
|
||||
import {
|
||||
UserRejectedRequestError,
|
||||
ChainNotConfiguredError,
|
||||
@@ -18,6 +17,7 @@ import {
|
||||
} from "@thirdweb-dev/wallets";
|
||||
import type { Address } from "abitype";
|
||||
import { providers, utils } from "ethers";
|
||||
import { getValidPublicRPCUrl } from "evm/utils/uri";
|
||||
|
||||
export type CoinbaseWalletConnectorOptions = WalletMobileSDKProviderOptions &
|
||||
ConfigurationParams & {
|
||||
@@ -227,7 +227,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
|
||||
chainId: id,
|
||||
chainName: chain.name,
|
||||
nativeCurrency: chain.nativeCurrency,
|
||||
rpcUrls: [getValidChainRPCs(chain)],
|
||||
rpcUrls: getValidPublicRPCUrl(chain), // no client id on purpose here
|
||||
blockExplorerUrls: this.getBlockExplorerUrls(chain),
|
||||
},
|
||||
],
|
||||
|
||||
+8
-1
@@ -6,7 +6,7 @@ import {
|
||||
import type { MagicConnector as MagicConnectorType } from "../connectors/magic/magic-connector";
|
||||
import { MagicConnector } from "../connectors/magic/magic-connector";
|
||||
|
||||
export class MagicWallet extends AbstractClientWallet<
|
||||
export class MagicLink extends AbstractClientWallet<
|
||||
MagicLinkOptions,
|
||||
MagicConnectorOptions
|
||||
> {
|
||||
@@ -68,3 +68,10 @@ export class MagicWallet extends AbstractClientWallet<
|
||||
return super.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `MagicLink` instead
|
||||
*
|
||||
* Renamed for consistency with our React package
|
||||
*/
|
||||
export class MagicWallet extends MagicLink {}
|
||||
+24
-16
@@ -10,22 +10,22 @@ import {
|
||||
useWalletContext,
|
||||
} from "@thirdweb-dev/react-core";
|
||||
import { MagicLinkOptions } from "../connectors/magic/types";
|
||||
import { MagicWallet } from "./MagicWallet";
|
||||
import { MagicLink } from "./MagicLink";
|
||||
import { useEffect, useRef } from "react";
|
||||
import Box from "../../components/base/Box";
|
||||
import { TextInput } from "../../components/base/TextInput";
|
||||
import Text from "../../components/base/Text";
|
||||
import { ActivityIndicator } from "react-native";
|
||||
import { useMagicWallet } from "../../providers/context-provider";
|
||||
import { useMagicLink } from "../../providers/context-provider";
|
||||
|
||||
export const magicWallet = (
|
||||
export const magicLink = (
|
||||
magicLinkOptions: MagicLinkOptions,
|
||||
): WalletConfig<MagicWallet> => {
|
||||
): WalletConfig<MagicLink> => {
|
||||
return {
|
||||
id: walletIds.magicLink,
|
||||
meta: MagicWallet.meta,
|
||||
meta: MagicLink.meta,
|
||||
create: (options: WalletOptions) => {
|
||||
return new MagicWallet({
|
||||
return new MagicLink({
|
||||
...options,
|
||||
...magicLinkOptions,
|
||||
chainId: options.chain.chainId,
|
||||
@@ -42,7 +42,7 @@ export const magicWallet = (
|
||||
/**
|
||||
* UI for selecting wallet - this UI is rendered in the wallet selection screen
|
||||
*/
|
||||
selectUI: (props: SelectUIProps<MagicWallet>) => {
|
||||
selectUI: (props: SelectUIProps<MagicLink>) => {
|
||||
return (
|
||||
<Box flex={1}>
|
||||
<TextInput
|
||||
@@ -71,23 +71,23 @@ export const magicWallet = (
|
||||
};
|
||||
|
||||
const MagicConnectionUI: React.FC<
|
||||
ConnectUIProps<MagicWallet> & { magicLinkOptions: MagicLinkOptions }
|
||||
ConnectUIProps<MagicLink> & { magicLinkOptions: MagicLinkOptions }
|
||||
> = ({ selectionData, walletConfig, close, magicLinkOptions }) => {
|
||||
const createWalletInstance = useCreateWalletInstance();
|
||||
const setConnectedWallet = useSetConnectedWallet();
|
||||
const chainToConnect = useWalletContext().chainToConnect;
|
||||
const setConnectionStatus = useSetConnectionStatus();
|
||||
const { magicWallet: ctxMagicWallet, setMagicWallet } = useMagicWallet();
|
||||
const { magicLink: ctxMagicLink, setMagicLink } = useMagicLink();
|
||||
|
||||
useEffect(() => {
|
||||
const inst = createWalletInstance(walletConfig);
|
||||
setMagicWallet?.(inst);
|
||||
}, [createWalletInstance, setMagicWallet, walletConfig]);
|
||||
setMagicLink?.(inst);
|
||||
}, [createWalletInstance, setMagicLink, walletConfig]);
|
||||
|
||||
const connectPrompted = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (connectPrompted.current || !ctxMagicWallet) {
|
||||
if (connectPrompted.current || !ctxMagicLink) {
|
||||
return;
|
||||
}
|
||||
connectPrompted.current = true;
|
||||
@@ -105,10 +105,10 @@ const MagicConnectionUI: React.FC<
|
||||
: { phoneNumber: selectionData }),
|
||||
};
|
||||
close();
|
||||
await ctxMagicWallet.connect(connectParams);
|
||||
await ctxMagicWallet.getMagicSDK().user.getMetadata();
|
||||
await ctxMagicLink.connect(connectParams);
|
||||
await ctxMagicLink.getMagicSDK().user.getMetadata();
|
||||
|
||||
setConnectedWallet(ctxMagicWallet, connectParams);
|
||||
setConnectedWallet(ctxMagicLink, connectParams);
|
||||
setConnectionStatus("connected");
|
||||
} catch (e) {
|
||||
setConnectionStatus("disconnected");
|
||||
@@ -119,7 +119,7 @@ const MagicConnectionUI: React.FC<
|
||||
selectionData,
|
||||
walletConfig,
|
||||
close,
|
||||
ctxMagicWallet,
|
||||
ctxMagicLink,
|
||||
setConnectedWallet,
|
||||
setConnectionStatus,
|
||||
chainToConnect?.chainId,
|
||||
@@ -129,6 +129,14 @@ const MagicConnectionUI: React.FC<
|
||||
return (
|
||||
<Box minHeight={500}>
|
||||
<ActivityIndicator size="small" />
|
||||
<TextInput placeholder="Enter the OTP sent to your email / phone number" />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated Use `magicLink()` instead
|
||||
*
|
||||
* Renamed for consistency with our React package
|
||||
*/
|
||||
export const magicWallet = magicLink;
|
||||
@@ -1,5 +1,23 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 3.14.31
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74), [`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117), [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
|
||||
- @thirdweb-dev/wallets@1.1.14
|
||||
- @thirdweb-dev/sdk@3.10.50
|
||||
- @thirdweb-dev/react-core@3.14.31
|
||||
|
||||
## 3.14.30
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2), [`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696)]:
|
||||
- @thirdweb-dev/wallets@1.1.13
|
||||
- @thirdweb-dev/sdk@3.10.49
|
||||
- @thirdweb-dev/react-core@3.14.30
|
||||
|
||||
## 3.14.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.14.29",
|
||||
"version": "3.14.31",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 3.10.50
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1480](https://github.com/thirdweb-dev/js/pull/1480) [`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Separate Account and AccountPermissions extensions
|
||||
|
||||
- [#1484](https://github.com/thirdweb-dev/js/pull/1484) [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose getPolygonGasPriorityFee()
|
||||
|
||||
## 3.10.49
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1468](https://github.com/thirdweb-dev/js/pull/1468) [`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Enable dynamic contract deployment with any evm
|
||||
|
||||
## 3.10.48
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "3.10.48",
|
||||
"version": "3.10.50",
|
||||
"description": "The main thirdweb SDK.",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -9,8 +9,13 @@ import { fetchAndCacheDeployMetadata } from "./fetchAndCacheDeployMetadata";
|
||||
import { getCreate2FactoryAddress } from "./getCreate2FactoryAddress";
|
||||
import { caches } from "./caches";
|
||||
import { computeDeploymentInfo } from "./computeDeploymentInfo";
|
||||
import { generatePluginFunctions } from "../plugin/generatePluginFunctions";
|
||||
import {
|
||||
generateExtensionFunctions,
|
||||
generatePluginFunctions,
|
||||
} from "../plugin/generatePluginFunctions";
|
||||
import { getMetadataForPlugins } from "../plugin/getMetadataForPlugins";
|
||||
import { getMetadataForExtensions } from "../plugin/getMetadataForExtensions";
|
||||
import { Extension } from "../../types/extensions";
|
||||
/**
|
||||
*
|
||||
* Returns txn data for keyless deploys as well as signer deploys.
|
||||
@@ -42,6 +47,10 @@ export async function getDeploymentInfo(
|
||||
storage,
|
||||
);
|
||||
const pluginMetadata = await getMetadataForPlugins(metadataUri, storage);
|
||||
const extensionMetadata = await getMetadataForExtensions(
|
||||
metadataUri,
|
||||
storage,
|
||||
);
|
||||
|
||||
// if pluginMetadata is not empty, then it's a plugin-pattern router contract
|
||||
if (pluginMetadata.length > 0) {
|
||||
@@ -87,6 +96,42 @@ export async function getDeploymentInfo(
|
||||
};
|
||||
|
||||
finalDeploymentInfo.push(...pluginDeploymentInfo, pluginMapTransaction);
|
||||
} else if (extensionMetadata.length > 0) {
|
||||
// get deployment info for all extensions
|
||||
const extensionDeploymentInfo = await Promise.all(
|
||||
extensionMetadata.map(async (metadata) => {
|
||||
const info = await computeDeploymentInfo(
|
||||
"extension",
|
||||
provider,
|
||||
storage,
|
||||
create2FactoryAddress,
|
||||
{ metadata: metadata },
|
||||
);
|
||||
return info;
|
||||
}),
|
||||
);
|
||||
|
||||
// create constructor param input for BaseRouter
|
||||
const routerInput: Extension[] = [];
|
||||
extensionMetadata.forEach((metadata, index) => {
|
||||
const extensionFunctions = generateExtensionFunctions(metadata.abi);
|
||||
routerInput.push({
|
||||
metadata: {
|
||||
name: metadata.name,
|
||||
metadataURI: "",
|
||||
implementation:
|
||||
extensionDeploymentInfo[index].transaction.predictedAddress,
|
||||
},
|
||||
functions: extensionFunctions,
|
||||
});
|
||||
});
|
||||
|
||||
// routerInput as constructor param for BaseRouter
|
||||
customParams["_extensions"] = {
|
||||
value: routerInput,
|
||||
};
|
||||
|
||||
finalDeploymentInfo.push(...extensionDeploymentInfo);
|
||||
}
|
||||
|
||||
const implementationDeployInfo = await computeDeploymentInfo(
|
||||
|
||||
@@ -2,6 +2,7 @@ export * from "./error";
|
||||
export * from "./snapshots";
|
||||
export * from "./role";
|
||||
export * from "./metadata-resolver";
|
||||
export * from "./gas-price";
|
||||
|
||||
// feature detection
|
||||
export * from "./feature-detection/matchesPrebuiltAbi";
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { Abi } from "../../schema/contracts/custom";
|
||||
import { utils } from "ethers";
|
||||
import { Plugin } from "../../types/plugins";
|
||||
import { ExtensionFunction } from "../../types/extensions";
|
||||
|
||||
function getFunctionSignature(fnInputs: any): string {
|
||||
return (
|
||||
"(" +
|
||||
fnInputs
|
||||
.map((i: any) => {
|
||||
return i.type === "tuple" ? getFunctionSignature(i.components) : i.type;
|
||||
return i.type === "tuple"
|
||||
? getFunctionSignature(i.components)
|
||||
: i.type === "tuple[]"
|
||||
? getFunctionSignature(i.components) + `[]`
|
||||
: i.type;
|
||||
})
|
||||
.join(",") +
|
||||
")"
|
||||
@@ -34,3 +39,25 @@ export function generatePluginFunctions(
|
||||
}
|
||||
return pluginFunctions;
|
||||
}
|
||||
|
||||
export function generateExtensionFunctions(
|
||||
extensionAbi: any,
|
||||
): ExtensionFunction[] {
|
||||
const extensionInterface = new utils.Interface(extensionAbi);
|
||||
const extensionFunctions: ExtensionFunction[] = [];
|
||||
// TODO - filter out common functions like _msgSender(), contractType(), etc.
|
||||
|
||||
for (const fnFragment of Object.values(extensionInterface.functions)) {
|
||||
const fn = extensionInterface.getFunction(
|
||||
extensionInterface.getSighash(fnFragment),
|
||||
);
|
||||
if (fn.name.startsWith("_")) {
|
||||
continue;
|
||||
}
|
||||
extensionFunctions.push({
|
||||
functionSelector: extensionInterface.getSighash(fn),
|
||||
functionSignature: fn.name + getFunctionSignature(fn.inputs),
|
||||
});
|
||||
}
|
||||
return extensionFunctions;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { getAllExtensionsAbi } from "../../constants/thirdweb-features";
|
||||
import { ContractWrapper } from "../../core/classes/contract-wrapper";
|
||||
import {
|
||||
AbiSchema,
|
||||
PreDeployMetadataFetched,
|
||||
} from "../../schema/contracts/custom";
|
||||
import { isFeatureEnabled } from "../feature-detection/isFeatureEnabled";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { getChainProvider } from "../../constants/urls";
|
||||
import { fetchAndCachePublishedContractURI } from "../any-evm-utils/fetchAndCachePublishedContractURI";
|
||||
import { fetchAndCacheDeployMetadata } from "../any-evm-utils/fetchAndCacheDeployMetadata";
|
||||
import { Extension } from "../../types/extensions";
|
||||
|
||||
export async function getMetadataForExtensions(
|
||||
publishedMetadataUri: string,
|
||||
storage: ThirdwebStorage,
|
||||
): Promise<PreDeployMetadataFetched[]> {
|
||||
let extensionMetadata: PreDeployMetadataFetched[] = [];
|
||||
|
||||
const { compilerMetadata, extendedMetadata } =
|
||||
await fetchAndCacheDeployMetadata(publishedMetadataUri, storage);
|
||||
// check if contract is extension-pattern
|
||||
const isExtensionRouter: boolean = isFeatureEnabled(
|
||||
AbiSchema.parse(compilerMetadata.abi),
|
||||
"ExtensionRouter",
|
||||
);
|
||||
|
||||
if (isExtensionRouter) {
|
||||
if (
|
||||
extendedMetadata &&
|
||||
extendedMetadata.factoryDeploymentData?.implementationAddresses
|
||||
) {
|
||||
const implementationsAddresses = Object.entries(
|
||||
extendedMetadata.factoryDeploymentData.implementationAddresses,
|
||||
);
|
||||
try {
|
||||
const entry = implementationsAddresses.find(
|
||||
([, implementation]) => implementation !== "",
|
||||
);
|
||||
const [network, implementation] = entry ? entry : [];
|
||||
if (network && implementation) {
|
||||
const provider = getChainProvider(parseInt(network), {});
|
||||
const contract = new ContractWrapper(
|
||||
provider,
|
||||
implementation,
|
||||
getAllExtensionsAbi,
|
||||
{},
|
||||
storage,
|
||||
);
|
||||
|
||||
const extensions = await contract.call("getAllExtensions");
|
||||
|
||||
const extensionNames: string[] = Array.from(
|
||||
new Set(extensions.map((item: Extension) => item.metadata.name)),
|
||||
);
|
||||
|
||||
const extensionUris = await Promise.all(
|
||||
extensionNames.map((name) => {
|
||||
return fetchAndCachePublishedContractURI(name);
|
||||
}),
|
||||
);
|
||||
extensionMetadata = (
|
||||
await Promise.all(
|
||||
extensionUris.map(async (uri) => {
|
||||
return fetchAndCacheDeployMetadata(uri, storage);
|
||||
}),
|
||||
)
|
||||
).map((fetchedMetadata) => fetchedMetadata.compilerMetadata);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
return extensionMetadata;
|
||||
}
|
||||
@@ -67,6 +67,7 @@ import {
|
||||
FEATURE_EXTENSION_ROUTER,
|
||||
FEATURE_ACCOUNT,
|
||||
FEATURE_ACCOUNT_FACTORY,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
} from "./thirdweb-features";
|
||||
|
||||
/**
|
||||
@@ -134,6 +135,7 @@ export type Feature =
|
||||
| typeof FEATURE_ENGLISH_AUCTIONS
|
||||
| typeof FEATURE_OFFERS
|
||||
| typeof FEATURE_ACCOUNT
|
||||
| typeof FEATURE_ACCOUNT_PERMISSIONS
|
||||
| typeof FEATURE_ACCOUNT_FACTORY;
|
||||
|
||||
export type Extension = Feature;
|
||||
|
||||
@@ -12,7 +12,8 @@ import IThirdwebPrimarySaleAbi from "@thirdweb-dev/contracts-js/dist/abis/IPrima
|
||||
import IThirdwebRoyaltyAbi from "@thirdweb-dev/contracts-js/dist/abis/IRoyalty.json";
|
||||
import IOwnableAbi from "@thirdweb-dev/contracts-js/dist/abis/Ownable.json";
|
||||
import IAccountFactory from "@thirdweb-dev/contracts-js/dist/abis/IAccountFactory.json";
|
||||
import IAccountCore from "@thirdweb-dev/contracts-js/dist/abis/IAccountCore.json";
|
||||
import IAccountPermissions from "@thirdweb-dev/contracts-js/dist/abis/IAccountPermissions.json";
|
||||
import IAccount from "@thirdweb-dev/contracts-js/dist/abis/IAccount.json";
|
||||
|
||||
export const getAllPluginsAbi = [
|
||||
{
|
||||
@@ -291,6 +292,18 @@ export const FEATURE_ACCOUNT_FACTORY = {
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
export const FEATURE_ACCOUNT_PERMISSIONS = {
|
||||
name: "AccountPermissions",
|
||||
namespace: "accountPermissions",
|
||||
docLinks: {
|
||||
// TODO
|
||||
sdk: "sdk.account",
|
||||
contracts: "",
|
||||
},
|
||||
abis: [IAccountPermissions],
|
||||
features: {},
|
||||
} as const;
|
||||
|
||||
export const FEATURE_ACCOUNT = {
|
||||
name: "Account",
|
||||
namespace: "account",
|
||||
@@ -299,6 +312,6 @@ export const FEATURE_ACCOUNT = {
|
||||
sdk: "sdk.account",
|
||||
contracts: "",
|
||||
},
|
||||
abis: [IAccountCore],
|
||||
features: {},
|
||||
abis: [IAccount],
|
||||
features: { [FEATURE_ACCOUNT_PERMISSIONS.name]: FEATURE_ACCOUNT_PERMISSIONS },
|
||||
} as const;
|
||||
|
||||
@@ -0,0 +1,727 @@
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { FEATURE_ACCOUNT_PERMISSIONS } from "../../constants/thirdweb-features";
|
||||
import { utils, BigNumber } from "ethers";
|
||||
import { Transaction } from "./transactions";
|
||||
|
||||
import type {
|
||||
IAccountCore,
|
||||
IAccountPermissions,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import {
|
||||
SignedSignerPermissionsPayload,
|
||||
SignerPermissionsOutput,
|
||||
SignerWithPermissions,
|
||||
SignerPermissions,
|
||||
SignerPermissionsInput,
|
||||
SignerPermissionRequest,
|
||||
SignerPermissionsSchema,
|
||||
PermissionSnapshotInput,
|
||||
PermissionSnapshotSchema,
|
||||
PermissionSnapshotOutput,
|
||||
} from "../../types";
|
||||
import invariant from "tiny-invariant";
|
||||
import { buildTransactionFunction } from "../../common/transactions";
|
||||
import { resolveOrGenerateId } from "../../common/signature-minting";
|
||||
import { AddressOrEns } from "../../schema";
|
||||
import { resolveAddress } from "../../common";
|
||||
|
||||
export class AccountPermissions implements DetectableFeature {
|
||||
featureName = FEATURE_ACCOUNT_PERMISSIONS.name;
|
||||
private contractWrapper: ContractWrapper<IAccountCore>;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<IAccountCore>) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
}
|
||||
|
||||
getAddress(): string {
|
||||
return this.contractWrapper.readContract.address;
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* HELPER FUNCTIONS
|
||||
********************************/
|
||||
|
||||
private hasDuplicateSigners(snapshot: PermissionSnapshotOutput): boolean {
|
||||
const checkedSigner: Record<string, boolean> = {};
|
||||
|
||||
const signers = snapshot.map((item) => item.signer);
|
||||
|
||||
for (const signer of signers) {
|
||||
if (!checkedSigner[signer]) {
|
||||
checkedSigner[signer] = true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the access restrictions for a given role
|
||||
*
|
||||
* @param restrictions - The access restrictions for a given role
|
||||
* @returns formatted role restrictions
|
||||
*
|
||||
*/
|
||||
private parseSignerPermissionsStruct(
|
||||
permissions: IAccountPermissions.SignerPermissionsStruct,
|
||||
): SignerPermissions {
|
||||
return {
|
||||
startDate: new Date(
|
||||
parseInt(permissions.startTimestamp.toString()) * 1000,
|
||||
),
|
||||
expirationDate: new Date(
|
||||
parseInt(permissions.endTimestamp.toString()) * 1000,
|
||||
),
|
||||
nativeTokenLimitPerTransaction: BigNumber.from(
|
||||
permissions.nativeTokenLimitPerTransaction,
|
||||
),
|
||||
approvedCallTargets: permissions.approvedTargets,
|
||||
};
|
||||
}
|
||||
|
||||
private async sendSignerPermissionRequest(
|
||||
signerAddress: string,
|
||||
permissions: SignerPermissionsOutput,
|
||||
): Promise<Transaction> {
|
||||
const { payload, signature } = await this.generatePayload(
|
||||
signerAddress,
|
||||
permissions,
|
||||
);
|
||||
|
||||
const { success } =
|
||||
await this.contractWrapper.readContract.verifySignerPermissionRequest(
|
||||
payload,
|
||||
signature,
|
||||
);
|
||||
if (!success) {
|
||||
throw new Error(`Invalid signature.`);
|
||||
}
|
||||
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "setPermissionsForSigner",
|
||||
args: [payload, signature],
|
||||
});
|
||||
}
|
||||
|
||||
private async buildSignerPermissionRequest(
|
||||
signerAddress: string,
|
||||
permissions: SignerPermissionsOutput,
|
||||
): Promise<string> {
|
||||
const { payload, signature } = await this.generatePayload(
|
||||
signerAddress,
|
||||
permissions,
|
||||
);
|
||||
|
||||
const isValidSigner =
|
||||
await this.contractWrapper.readContract.verifySignerPermissionRequest(
|
||||
payload,
|
||||
signature,
|
||||
);
|
||||
if (!isValidSigner) {
|
||||
throw new Error(`Invalid signature.`);
|
||||
}
|
||||
|
||||
return this.contractWrapper.writeContract.interface.encodeFunctionData(
|
||||
"setPermissionsForSigner",
|
||||
[payload, signature],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and sign a payload to grant or revoke a signer's access to the account.
|
||||
*
|
||||
* @param signer - The address of the signer
|
||||
* @param roleAction - The address of the signer
|
||||
* @returns The generated payload and signature produced on signing that payload.
|
||||
*
|
||||
*/
|
||||
private async generatePayload(
|
||||
signerAddress: string,
|
||||
permissions: SignerPermissionsOutput,
|
||||
): Promise<SignedSignerPermissionsPayload> {
|
||||
// Get payload struct.
|
||||
const payload: IAccountPermissions.SignerPermissionRequestStruct = {
|
||||
signer: signerAddress,
|
||||
approvedTargets: permissions.approvedCallTargets,
|
||||
nativeTokenLimitPerTransaction: utils.parseEther(
|
||||
permissions.nativeTokenLimitPerTransaction,
|
||||
),
|
||||
permissionStartTimestamp: permissions.startDate,
|
||||
permissionEndTimestamp: permissions.expirationDate,
|
||||
reqValidityStartTimestamp: 0,
|
||||
// Req validity ends 10 years from now.
|
||||
reqValidityEndTimestamp: BigNumber.from(
|
||||
Math.floor(
|
||||
new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10).getTime() /
|
||||
1000,
|
||||
),
|
||||
),
|
||||
uid: resolveOrGenerateId(undefined),
|
||||
};
|
||||
|
||||
// Generate signature
|
||||
const chainId = await this.contractWrapper.getChainID();
|
||||
const connectedSigner = this.contractWrapper.getSigner();
|
||||
invariant(connectedSigner, "No signer available");
|
||||
|
||||
const signature = await this.contractWrapper.signTypedData(
|
||||
connectedSigner,
|
||||
{
|
||||
name: "Account",
|
||||
version: "1",
|
||||
chainId,
|
||||
verifyingContract: this.getAddress(),
|
||||
},
|
||||
{ SignerPermissionRequest },
|
||||
payload,
|
||||
);
|
||||
|
||||
return { payload, signature };
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* READ FUNCTIONS
|
||||
********************************/
|
||||
|
||||
/**
|
||||
* Get whether a signer is an admin on the account.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const isAdmin = await contract.account.isAdmin(signer);
|
||||
* ```
|
||||
* @param signer - The address of a signer of the account.
|
||||
* @returns whether a signer is an admin on the account.
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
public async isAdmin(signerAddress: AddressOrEns): Promise<boolean> {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
return await this.contractWrapper.readContract.isAdmin(
|
||||
resolvedSignerAddress,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether a signer has permissions to use the account.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const isAdmin = await contract.account.isSigner(signer);
|
||||
* ```
|
||||
* @param signer - The address of a signer of the account.
|
||||
* @returns whether a signer has permissions to use the account.
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
public async isSigner(signerAddress: AddressOrEns): Promise<boolean> {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
return await this.contractWrapper.readContract.isActiveSigner(
|
||||
resolvedSignerAddress,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all admins of the account.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const allAdmins = await contract.account.getAllAdmins();
|
||||
* ```
|
||||
*
|
||||
* @returns all admins of the account.
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
public async getAllAdmins(): Promise<string[]> {
|
||||
return await this.contractWrapper.readContract.getAllAdmins();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all (non-admin) signers with permissions to use the account.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const allSigners = await contract.account.getAllSigners();
|
||||
* ```
|
||||
*
|
||||
* @returns all (non-admin) signers with permissions to use the account.
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
public async getAllSigners(): Promise<SignerWithPermissions[]> {
|
||||
const activeSignersWithPerms: IAccountPermissions.SignerPermissionsStruct[] =
|
||||
await this.contractWrapper.readContract.getAllActiveSigners();
|
||||
|
||||
return await Promise.all(
|
||||
activeSignersWithPerms.map(async (signerWithPermissions) => {
|
||||
const signer = signerWithPermissions.signer;
|
||||
const permissions = this.parseSignerPermissionsStruct(
|
||||
signerWithPermissions,
|
||||
);
|
||||
return { signer, permissions };
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all admins and non-admin signers with permissions to use the account.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const allAdminsAndSigners = await contract.account.getAllAdminsAndSigners();
|
||||
* ```
|
||||
*
|
||||
* @returns all admins and non-admin signers with permissions to use the account.
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
public async getAllAdminsAndSigners(): Promise<SignerWithPermissions[]> {
|
||||
const allAdmins = await this.getAllAdmins();
|
||||
const transformedAdmins: SignerWithPermissions[] = allAdmins.map(
|
||||
(admin) => {
|
||||
return {
|
||||
isAdmin: true,
|
||||
signer: admin,
|
||||
permissions: {
|
||||
startDate: new Date(0),
|
||||
expirationDate: new Date(0),
|
||||
nativeTokenLimitPerTransaction: BigNumber.from(0),
|
||||
approvedCallTargets: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
const allSigners = await this.getAllSigners();
|
||||
|
||||
return [...transformedAdmins, ...allSigners];
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* WRITE FUNCTIONS
|
||||
********************************/
|
||||
|
||||
/**
|
||||
* Grant an address admin access to the account.
|
||||
*
|
||||
* @remarks Grants an address admin access to the account. The admin will have complete authority over the account.
|
||||
*
|
||||
* @param signer - The address to be granted admin access to the account.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const tx = await contract.account.grantAdminAccess(signer);
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
grantAdminPermissions = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (signerAddress: AddressOrEns): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "setAdmin",
|
||||
args: [resolvedSignerAddress, true],
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Revoke an address' admin access to the account.
|
||||
*
|
||||
* @remarks Revokes an address' admin access to the account.
|
||||
*
|
||||
* @param signer - The address of an admin of the account.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const tx = await contract.account.revokeAdminAccess(signer);
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
revokeAdminPermissions = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (signerAddress: AddressOrEns): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "setAdmin",
|
||||
args: [resolvedSignerAddress, false],
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Grant a signer permissions to use the account.
|
||||
*
|
||||
* @remarks Grants a signer permissions to use the account.
|
||||
*
|
||||
* @param signer - The signer to be granted permissions to use the account.
|
||||
* @param permissions - The permissions to be applied to the signer's use of the account.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const tx = await contract.account.grantPermissions(signer, permissions);
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
grantPermissions = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (
|
||||
signerAddress: AddressOrEns,
|
||||
permissions: SignerPermissionsInput,
|
||||
): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
const resolvedPermissions = await SignerPermissionsSchema.parseAsync(
|
||||
permissions,
|
||||
);
|
||||
|
||||
if (await this.isAdmin(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer is already an admin. Cannot grant permissions to an existing admin.",
|
||||
);
|
||||
}
|
||||
|
||||
if (await this.isSigner(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer already has permissions. Cannot grant permissions to an existing signer. You can update permissions using `updatePermissions`.",
|
||||
);
|
||||
}
|
||||
|
||||
return await this.sendSignerPermissionRequest(
|
||||
resolvedSignerAddress,
|
||||
resolvedPermissions,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Update the permissions of a signer for using the account.
|
||||
*
|
||||
* @remarks Updates the permissions of a signer for using the account.
|
||||
*
|
||||
* @param signer - The signer whose permissions to use the account are to be updated.
|
||||
* @param permissions - The permissions to be applied to the signer's use of the account.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const tx = await contract.account.updateAccess(signer, restrictions);
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
updatePermissions = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (
|
||||
signerAddress: AddressOrEns,
|
||||
permissions: SignerPermissionsInput,
|
||||
): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
const resolvedPermissions = await SignerPermissionsSchema.parseAsync(
|
||||
permissions,
|
||||
);
|
||||
|
||||
if (await this.isAdmin(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer is already an admin. Cannot update permissions of an existing admin.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await this.isSigner(resolvedSignerAddress))) {
|
||||
throw new Error(
|
||||
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
|
||||
);
|
||||
}
|
||||
|
||||
return await this.sendSignerPermissionRequest(
|
||||
resolvedSignerAddress,
|
||||
resolvedPermissions,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Revoke a scoped access address to the account
|
||||
*
|
||||
* @remarks Revokes an address' access to the account.
|
||||
*
|
||||
* @param signer - The address whose access to the account is to be revoked.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const tx = await contract.account.revokeAccess(signer);
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
revokeAccess = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (signerAddress: AddressOrEns): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
|
||||
if (await this.isAdmin(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer is already an admin. Cannot revoke permissions of an admin.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await this.isSigner(resolvedSignerAddress))) {
|
||||
throw new Error(
|
||||
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
|
||||
);
|
||||
}
|
||||
|
||||
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
|
||||
startDate: BigNumber.from(0),
|
||||
expirationDate: BigNumber.from(0),
|
||||
approvedCallTargets: [],
|
||||
nativeTokenLimitPerTransaction: "0",
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Approve an address as a call target for a given signer on the account
|
||||
*
|
||||
* @remarks Approves an address as a call target for a given signer on the account.
|
||||
*
|
||||
* @param signer - A signer with restricted access to the account.
|
||||
* @param target - The address to approve as a call target for the signer.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const tx = await contract.account.approveTargetForSigner(signer, target);
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
approveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (
|
||||
signerAddress: AddressOrEns,
|
||||
target: AddressOrEns,
|
||||
): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
const resolvedTarget = await resolveAddress(target);
|
||||
|
||||
if (await this.isAdmin(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer is already an admin. Cannot approve targets for an admin.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await this.isSigner(resolvedSignerAddress))) {
|
||||
throw new Error(
|
||||
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
|
||||
);
|
||||
}
|
||||
|
||||
const permissions: IAccountPermissions.SignerPermissionsStruct =
|
||||
await this.contractWrapper.readContract.getPermissionsForSigner(
|
||||
resolvedSignerAddress,
|
||||
);
|
||||
|
||||
if (permissions.approvedTargets.includes(target)) {
|
||||
throw new Error("Target is already approved");
|
||||
}
|
||||
|
||||
const newTargets = [...permissions.approvedTargets, resolvedTarget];
|
||||
|
||||
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
|
||||
startDate: BigNumber.from(permissions.startTimestamp),
|
||||
expirationDate: BigNumber.from(permissions.endTimestamp),
|
||||
approvedCallTargets: newTargets,
|
||||
nativeTokenLimitPerTransaction:
|
||||
permissions.nativeTokenLimitPerTransaction.toString(),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Disapprove an address as a call target for a given signer on the account
|
||||
*
|
||||
* @remarks Disapprove an address as a call target for a given signer on the account.
|
||||
*
|
||||
* @param signer - A signer with restricted access to the account.
|
||||
* @param target - The address to disapprove as a call target for the signer.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const tx = await contract.account.disapproveTargetForSigner(signer, target);
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
disapproveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (
|
||||
signerAddress: AddressOrEns,
|
||||
target: AddressOrEns,
|
||||
): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
const resolvedTarget = await resolveAddress(target);
|
||||
|
||||
if (await this.isAdmin(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer is already an admin. Cannot approve targets for an admin.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await this.isSigner(resolvedSignerAddress))) {
|
||||
throw new Error(
|
||||
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
|
||||
);
|
||||
}
|
||||
|
||||
const permissions: IAccountPermissions.SignerPermissionsStruct =
|
||||
await this.contractWrapper.readContract.getPermissionsForSigner(
|
||||
resolvedSignerAddress,
|
||||
);
|
||||
|
||||
if (!permissions.approvedTargets.includes(resolvedTarget)) {
|
||||
throw new Error("Target is currently not approved");
|
||||
}
|
||||
|
||||
const newTargets = permissions.approvedTargets.filter(
|
||||
(approvedTarget) =>
|
||||
utils.getAddress(approvedTarget) !== utils.getAddress(resolvedTarget),
|
||||
);
|
||||
|
||||
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
|
||||
startDate: BigNumber.from(permissions.startTimestamp),
|
||||
expirationDate: BigNumber.from(permissions.endTimestamp),
|
||||
approvedCallTargets: newTargets,
|
||||
nativeTokenLimitPerTransaction:
|
||||
permissions.nativeTokenLimitPerTransaction.toString(),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Set the account's entire snapshot of permissions.
|
||||
*
|
||||
* @remarks Sets the account's entire snapshot of permissions.
|
||||
*
|
||||
* @param permissionSnapshot - the snapshot to set as the account's entire permission snapshot.
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const tx = await contract.account.setAccess(permissionSnapshot);
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
resetAllPermissions = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (
|
||||
permissionSnapshot: PermissionSnapshotInput,
|
||||
): Promise<Transaction> => {
|
||||
const resolvedSnapshot = await PermissionSnapshotSchema.parseAsync(
|
||||
permissionSnapshot,
|
||||
);
|
||||
|
||||
/**
|
||||
* All cases
|
||||
*
|
||||
* - Add new admin :check:
|
||||
* - Remove current admin :check:
|
||||
* - Add new scoped :check:
|
||||
* - Remove current scoped :check:
|
||||
* - Update current scoped :check:
|
||||
* - Current admin -> new scoped :check:
|
||||
* - Current scoped -> new admin :check:
|
||||
**/
|
||||
|
||||
// No duplicate signers in input!
|
||||
if (this.hasDuplicateSigners(resolvedSnapshot)) {
|
||||
throw new Error("Duplicate signers found in input.");
|
||||
}
|
||||
|
||||
const addAdminData: string[] = [];
|
||||
const removeAdminData: string[] = [];
|
||||
const addOrUpdateSignerData: string[] = [];
|
||||
const removeSignerData: string[] = [];
|
||||
|
||||
// Remove all existing admins not included in the passed snapshot.
|
||||
const allAdmins = await this.getAllAdmins();
|
||||
const allToMakeAdmin = resolvedSnapshot
|
||||
.filter((item) => item.makeAdmin)
|
||||
.map((item) => item.signer);
|
||||
allAdmins.forEach((admin) => {
|
||||
if (!allToMakeAdmin.includes(admin)) {
|
||||
removeAdminData.push(
|
||||
this.contractWrapper.writeContract.interface.encodeFunctionData(
|
||||
"setAdmin",
|
||||
[admin, false],
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove all existing signers not included in the passed snapshot.
|
||||
const allSigners = await this.getAllSigners();
|
||||
const allToMakeSigners = resolvedSnapshot
|
||||
.filter((item) => {
|
||||
return !item.makeAdmin;
|
||||
})
|
||||
.map((item) => item.signer);
|
||||
await Promise.all(
|
||||
allSigners.map(async (item) => {
|
||||
if (!allToMakeSigners.includes(item.signer)) {
|
||||
const data = await this.buildSignerPermissionRequest(item.signer, {
|
||||
startDate: BigNumber.from(0),
|
||||
expirationDate: BigNumber.from(0),
|
||||
approvedCallTargets: [],
|
||||
nativeTokenLimitPerTransaction: "0",
|
||||
});
|
||||
|
||||
removeSignerData.push(data);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
for (const member of resolvedSnapshot) {
|
||||
// Add new admin
|
||||
if (member.makeAdmin) {
|
||||
addAdminData.push(
|
||||
this.contractWrapper.writeContract.interface.encodeFunctionData(
|
||||
"setAdmin",
|
||||
[member.signer, true],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Add new scoped
|
||||
const data = await this.buildSignerPermissionRequest(
|
||||
member.signer,
|
||||
member.permissions,
|
||||
);
|
||||
addOrUpdateSignerData.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
const data: string[] = [];
|
||||
removeAdminData.forEach((item) => {
|
||||
data.push(item);
|
||||
});
|
||||
removeSignerData.forEach((item) => {
|
||||
data.push(item);
|
||||
});
|
||||
addOrUpdateSignerData.forEach((item) => {
|
||||
data.push(item);
|
||||
});
|
||||
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "multicall",
|
||||
args: [data],
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -1,190 +1,48 @@
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { FEATURE_ACCOUNT } from "../../constants/thirdweb-features";
|
||||
import { utils, BigNumber } from "ethers";
|
||||
import { Transaction } from "./transactions";
|
||||
|
||||
import type {
|
||||
IAccountCore,
|
||||
IAccountPermissions,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import {
|
||||
SignedSignerPermissionsPayload,
|
||||
SignerPermissionsOutput,
|
||||
FEATURE_ACCOUNT,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
} from "../../constants/thirdweb-features";
|
||||
|
||||
import type { IAccountCore } from "@thirdweb-dev/contracts-js";
|
||||
import {
|
||||
SignerWithPermissions,
|
||||
SignerPermissions,
|
||||
SignerPermissionsInput,
|
||||
SignerPermissionRequest,
|
||||
SignerPermissionsSchema,
|
||||
PermissionSnapshotInput,
|
||||
PermissionSnapshotSchema,
|
||||
PermissionSnapshotOutput,
|
||||
} from "../../types";
|
||||
import invariant from "tiny-invariant";
|
||||
import { buildTransactionFunction } from "../../common/transactions";
|
||||
import { resolveOrGenerateId } from "../../common/signature-minting";
|
||||
import { AddressOrEns } from "../../schema";
|
||||
import { resolveAddress } from "../../common";
|
||||
import { assertEnabled, detectContractFeature } from "../../common";
|
||||
import { AccountPermissions } from "./account-permissions";
|
||||
|
||||
export class Account<TContract extends IAccountCore>
|
||||
implements DetectableFeature
|
||||
{
|
||||
featureName = FEATURE_ACCOUNT.name;
|
||||
private contractWrapper: ContractWrapper<IAccountCore>;
|
||||
private accountPermissions: AccountPermissions | undefined;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<TContract>) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.accountPermissions = this.detectAccountPermissions();
|
||||
}
|
||||
|
||||
private detectAccountPermissions(): AccountPermissions | undefined {
|
||||
if (
|
||||
detectContractFeature<IAccountCore>(
|
||||
this.contractWrapper,
|
||||
"AccountPermissions",
|
||||
)
|
||||
) {
|
||||
return new AccountPermissions(this.contractWrapper);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
getAddress(): string {
|
||||
return this.contractWrapper.readContract.address;
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* HELPER FUNCTIONS
|
||||
********************************/
|
||||
|
||||
private hasDuplicateSigners(snapshot: PermissionSnapshotOutput): boolean {
|
||||
const checkedSigner: Record<string, boolean> = {};
|
||||
|
||||
const signers = snapshot.map((item) => item.signer);
|
||||
|
||||
for (const signer of signers) {
|
||||
if (!checkedSigner[signer]) {
|
||||
checkedSigner[signer] = true;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the access restrictions for a given role
|
||||
*
|
||||
* @param restrictions - The access restrictions for a given role
|
||||
* @returns formatted role restrictions
|
||||
*
|
||||
*/
|
||||
private parseSignerPermissionsStruct(
|
||||
permissions: IAccountPermissions.SignerPermissionsStruct,
|
||||
): SignerPermissions {
|
||||
return {
|
||||
startDate: new Date(
|
||||
parseInt(permissions.startTimestamp.toString()) * 1000,
|
||||
),
|
||||
expirationDate: new Date(
|
||||
parseInt(permissions.endTimestamp.toString()) * 1000,
|
||||
),
|
||||
nativeTokenLimitPerTransaction: BigNumber.from(
|
||||
permissions.nativeTokenLimitPerTransaction,
|
||||
),
|
||||
approvedCallTargets: permissions.approvedTargets,
|
||||
};
|
||||
}
|
||||
|
||||
private async sendSignerPermissionRequest(
|
||||
signerAddress: string,
|
||||
permissions: SignerPermissionsOutput,
|
||||
): Promise<Transaction> {
|
||||
const { payload, signature } = await this.generatePayload(
|
||||
signerAddress,
|
||||
permissions,
|
||||
);
|
||||
|
||||
const { success } =
|
||||
await this.contractWrapper.readContract.verifySignerPermissionRequest(
|
||||
payload,
|
||||
signature,
|
||||
);
|
||||
if (!success) {
|
||||
throw new Error(`Invalid signature.`);
|
||||
}
|
||||
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "setPermissionsForSigner",
|
||||
args: [payload, signature],
|
||||
});
|
||||
}
|
||||
|
||||
private async buildSignerPermissionRequest(
|
||||
signerAddress: string,
|
||||
permissions: SignerPermissionsOutput,
|
||||
): Promise<string> {
|
||||
const { payload, signature } = await this.generatePayload(
|
||||
signerAddress,
|
||||
permissions,
|
||||
);
|
||||
|
||||
const isValidSigner =
|
||||
await this.contractWrapper.readContract.verifySignerPermissionRequest(
|
||||
payload,
|
||||
signature,
|
||||
);
|
||||
if (!isValidSigner) {
|
||||
throw new Error(`Invalid signature.`);
|
||||
}
|
||||
|
||||
return this.contractWrapper.writeContract.interface.encodeFunctionData(
|
||||
"setPermissionsForSigner",
|
||||
[payload, signature],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate and sign a payload to grant or revoke a signer's access to the account.
|
||||
*
|
||||
* @param signer - The address of the signer
|
||||
* @param roleAction - The address of the signer
|
||||
* @returns The generated payload and signature produced on signing that payload.
|
||||
*
|
||||
*/
|
||||
private async generatePayload(
|
||||
signerAddress: string,
|
||||
permissions: SignerPermissionsOutput,
|
||||
): Promise<SignedSignerPermissionsPayload> {
|
||||
// Get payload struct.
|
||||
const payload: IAccountPermissions.SignerPermissionRequestStruct = {
|
||||
signer: signerAddress,
|
||||
approvedTargets: permissions.approvedCallTargets,
|
||||
nativeTokenLimitPerTransaction: utils.parseEther(
|
||||
permissions.nativeTokenLimitPerTransaction,
|
||||
),
|
||||
permissionStartTimestamp: permissions.startDate,
|
||||
permissionEndTimestamp: permissions.expirationDate,
|
||||
reqValidityStartTimestamp: 0,
|
||||
// Req validity ends 10 years from now.
|
||||
reqValidityEndTimestamp: BigNumber.from(
|
||||
Math.floor(
|
||||
new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10).getTime() /
|
||||
1000,
|
||||
),
|
||||
),
|
||||
uid: resolveOrGenerateId(undefined),
|
||||
};
|
||||
|
||||
// Generate signature
|
||||
const chainId = await this.contractWrapper.getChainID();
|
||||
const connectedSigner = this.contractWrapper.getSigner();
|
||||
invariant(connectedSigner, "No signer available");
|
||||
|
||||
const signature = await this.contractWrapper.signTypedData(
|
||||
connectedSigner,
|
||||
{
|
||||
name: "Account",
|
||||
version: "1",
|
||||
chainId,
|
||||
verifyingContract: this.getAddress(),
|
||||
},
|
||||
{ SignerPermissionRequest },
|
||||
payload,
|
||||
);
|
||||
|
||||
return { payload, signature };
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* READ FUNCTIONS
|
||||
********************************/
|
||||
@@ -199,13 +57,13 @@ export class Account<TContract extends IAccountCore>
|
||||
* @param signer - The address of a signer of the account.
|
||||
* @returns whether a signer is an admin on the account.
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
public async isAdmin(signerAddress: AddressOrEns): Promise<boolean> {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
return await this.contractWrapper.readContract.isAdmin(
|
||||
resolvedSignerAddress,
|
||||
);
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).isAdmin(signerAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,13 +76,13 @@ export class Account<TContract extends IAccountCore>
|
||||
* @param signer - The address of a signer of the account.
|
||||
* @returns whether a signer has permissions to use the account.
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
public async isSigner(signerAddress: AddressOrEns): Promise<boolean> {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
return await this.contractWrapper.readContract.isActiveSigner(
|
||||
resolvedSignerAddress,
|
||||
);
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).isSigner(signerAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,10 +95,13 @@ export class Account<TContract extends IAccountCore>
|
||||
*
|
||||
* @returns all admins of the account.
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
public async getAllAdmins(): Promise<string[]> {
|
||||
return await this.contractWrapper.readContract.getAllAdmins();
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).getAllAdmins();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,21 +114,13 @@ export class Account<TContract extends IAccountCore>
|
||||
*
|
||||
* @returns all (non-admin) signers with permissions to use the account.
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
public async getAllSigners(): Promise<SignerWithPermissions[]> {
|
||||
const activeSignersWithPerms: IAccountPermissions.SignerPermissionsStruct[] =
|
||||
await this.contractWrapper.readContract.getAllActiveSigners();
|
||||
|
||||
return await Promise.all(
|
||||
activeSignersWithPerms.map(async (signerWithPermissions) => {
|
||||
const signer = signerWithPermissions.signer;
|
||||
const permissions = this.parseSignerPermissionsStruct(
|
||||
signerWithPermissions,
|
||||
);
|
||||
return { signer, permissions };
|
||||
}),
|
||||
);
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).getAllSigners();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,27 +133,13 @@ export class Account<TContract extends IAccountCore>
|
||||
*
|
||||
* @returns all admins and non-admin signers with permissions to use the account.
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
public async getAllAdminsAndSigners(): Promise<SignerWithPermissions[]> {
|
||||
const allAdmins = await this.getAllAdmins();
|
||||
const transformedAdmins: SignerWithPermissions[] = allAdmins.map(
|
||||
(admin) => {
|
||||
return {
|
||||
isAdmin: true,
|
||||
signer: admin,
|
||||
permissions: {
|
||||
startDate: new Date(0),
|
||||
expirationDate: new Date(0),
|
||||
nativeTokenLimitPerTransaction: BigNumber.from(0),
|
||||
approvedCallTargets: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
const allSigners = await this.getAllSigners();
|
||||
|
||||
return [...transformedAdmins, ...allSigners];
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).getAllAdminsAndSigners();
|
||||
}
|
||||
|
||||
/*********************************
|
||||
@@ -320,18 +159,14 @@ export class Account<TContract extends IAccountCore>
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
grantAdminPermissions = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (signerAddress: AddressOrEns): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "setAdmin",
|
||||
args: [resolvedSignerAddress, true],
|
||||
});
|
||||
},
|
||||
);
|
||||
public async grantAdminPermissions(signerAddress: AddressOrEns) {
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).grantAdminPermissions(signerAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke an address' admin access to the account.
|
||||
@@ -346,18 +181,14 @@ export class Account<TContract extends IAccountCore>
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
revokeAdminPermissions = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (signerAddress: AddressOrEns): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "setAdmin",
|
||||
args: [resolvedSignerAddress, false],
|
||||
});
|
||||
},
|
||||
);
|
||||
public async revokeAdminPermissions(signerAddress: AddressOrEns) {
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).revokeAdminPermissions(signerAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Grant a signer permissions to use the account.
|
||||
@@ -373,36 +204,17 @@ export class Account<TContract extends IAccountCore>
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
grantPermissions = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (
|
||||
signerAddress: AddressOrEns,
|
||||
permissions: SignerPermissionsInput,
|
||||
): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
const resolvedPermissions = await SignerPermissionsSchema.parseAsync(
|
||||
permissions,
|
||||
);
|
||||
|
||||
if (await this.isAdmin(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer is already an admin. Cannot grant permissions to an existing admin.",
|
||||
);
|
||||
}
|
||||
|
||||
if (await this.isSigner(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer already has permissions. Cannot grant permissions to an existing signer. You can update permissions using `updatePermissions`.",
|
||||
);
|
||||
}
|
||||
|
||||
return await this.sendSignerPermissionRequest(
|
||||
resolvedSignerAddress,
|
||||
resolvedPermissions,
|
||||
);
|
||||
},
|
||||
);
|
||||
public async grantPermissions(
|
||||
signerAddress: AddressOrEns,
|
||||
permissions: SignerPermissionsInput,
|
||||
) {
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).grantPermissions(signerAddress, permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the permissions of a signer for using the account.
|
||||
@@ -418,36 +230,17 @@ export class Account<TContract extends IAccountCore>
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
updatePermissions = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (
|
||||
signerAddress: AddressOrEns,
|
||||
permissions: SignerPermissionsInput,
|
||||
): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
const resolvedPermissions = await SignerPermissionsSchema.parseAsync(
|
||||
permissions,
|
||||
);
|
||||
|
||||
if (await this.isAdmin(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer is already an admin. Cannot update permissions of an existing admin.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await this.isSigner(resolvedSignerAddress))) {
|
||||
throw new Error(
|
||||
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
|
||||
);
|
||||
}
|
||||
|
||||
return await this.sendSignerPermissionRequest(
|
||||
resolvedSignerAddress,
|
||||
resolvedPermissions,
|
||||
);
|
||||
},
|
||||
);
|
||||
public async updatePermissions(
|
||||
signerAddress: AddressOrEns,
|
||||
permissions: SignerPermissionsInput,
|
||||
) {
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).updatePermissions(signerAddress, permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke a scoped access address to the account
|
||||
@@ -462,32 +255,14 @@ export class Account<TContract extends IAccountCore>
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
revokeAccess = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (signerAddress: AddressOrEns): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
|
||||
if (await this.isAdmin(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer is already an admin. Cannot revoke permissions of an admin.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await this.isSigner(resolvedSignerAddress))) {
|
||||
throw new Error(
|
||||
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
|
||||
);
|
||||
}
|
||||
|
||||
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
|
||||
startDate: BigNumber.from(0),
|
||||
expirationDate: BigNumber.from(0),
|
||||
approvedCallTargets: [],
|
||||
nativeTokenLimitPerTransaction: "0",
|
||||
});
|
||||
},
|
||||
);
|
||||
public async revokeAccess(signerAddress: AddressOrEns) {
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).revokeAccess(signerAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve an address as a call target for a given signer on the account
|
||||
@@ -503,48 +278,17 @@ export class Account<TContract extends IAccountCore>
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
approveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (
|
||||
signerAddress: AddressOrEns,
|
||||
target: AddressOrEns,
|
||||
): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
const resolvedTarget = await resolveAddress(target);
|
||||
|
||||
if (await this.isAdmin(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer is already an admin. Cannot approve targets for an admin.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await this.isSigner(resolvedSignerAddress))) {
|
||||
throw new Error(
|
||||
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
|
||||
);
|
||||
}
|
||||
|
||||
const permissions: IAccountPermissions.SignerPermissionsStruct =
|
||||
await this.contractWrapper.readContract.getPermissionsForSigner(
|
||||
resolvedSignerAddress,
|
||||
);
|
||||
|
||||
if (permissions.approvedTargets.includes(target)) {
|
||||
throw new Error("Target is already approved");
|
||||
}
|
||||
|
||||
const newTargets = [...permissions.approvedTargets, resolvedTarget];
|
||||
|
||||
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
|
||||
startDate: BigNumber.from(permissions.startTimestamp),
|
||||
expirationDate: BigNumber.from(permissions.endTimestamp),
|
||||
approvedCallTargets: newTargets,
|
||||
nativeTokenLimitPerTransaction:
|
||||
permissions.nativeTokenLimitPerTransaction.toString(),
|
||||
});
|
||||
},
|
||||
);
|
||||
public async approveTargetForSigner(
|
||||
signerAddress: AddressOrEns,
|
||||
target: AddressOrEns,
|
||||
) {
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).approveTargetForSigner(signerAddress, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disapprove an address as a call target for a given signer on the account
|
||||
@@ -560,51 +304,17 @@ export class Account<TContract extends IAccountCore>
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
disapproveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (
|
||||
signerAddress: AddressOrEns,
|
||||
target: AddressOrEns,
|
||||
): Promise<Transaction> => {
|
||||
const resolvedSignerAddress = await resolveAddress(signerAddress);
|
||||
const resolvedTarget = await resolveAddress(target);
|
||||
|
||||
if (await this.isAdmin(resolvedSignerAddress)) {
|
||||
throw new Error(
|
||||
"Signer is already an admin. Cannot approve targets for an admin.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!(await this.isSigner(resolvedSignerAddress))) {
|
||||
throw new Error(
|
||||
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
|
||||
);
|
||||
}
|
||||
|
||||
const permissions: IAccountPermissions.SignerPermissionsStruct =
|
||||
await this.contractWrapper.readContract.getPermissionsForSigner(
|
||||
resolvedSignerAddress,
|
||||
);
|
||||
|
||||
if (!permissions.approvedTargets.includes(resolvedTarget)) {
|
||||
throw new Error("Target is currently not approved");
|
||||
}
|
||||
|
||||
const newTargets = permissions.approvedTargets.filter(
|
||||
(approvedTarget) =>
|
||||
utils.getAddress(approvedTarget) !== utils.getAddress(resolvedTarget),
|
||||
);
|
||||
|
||||
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
|
||||
startDate: BigNumber.from(permissions.startTimestamp),
|
||||
expirationDate: BigNumber.from(permissions.endTimestamp),
|
||||
approvedCallTargets: newTargets,
|
||||
nativeTokenLimitPerTransaction:
|
||||
permissions.nativeTokenLimitPerTransaction.toString(),
|
||||
});
|
||||
},
|
||||
);
|
||||
public async disapproveTargetForSigner(
|
||||
signerAddress: AddressOrEns,
|
||||
target: AddressOrEns,
|
||||
) {
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).disapproveTargetForSigner(signerAddress, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the account's entire snapshot of permissions.
|
||||
@@ -619,111 +329,14 @@ export class Account<TContract extends IAccountCore>
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
* @twfeature AccountPermissions
|
||||
*/
|
||||
resetAllPermissions = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (
|
||||
permissionSnapshot: PermissionSnapshotInput,
|
||||
): Promise<Transaction> => {
|
||||
const resolvedSnapshot = await PermissionSnapshotSchema.parseAsync(
|
||||
permissionSnapshot,
|
||||
);
|
||||
|
||||
/**
|
||||
* All cases
|
||||
*
|
||||
* - Add new admin :check:
|
||||
* - Remove current admin :check:
|
||||
* - Add new scoped :check:
|
||||
* - Remove current scoped :check:
|
||||
* - Update current scoped :check:
|
||||
* - Current admin -> new scoped :check:
|
||||
* - Current scoped -> new admin :check:
|
||||
**/
|
||||
|
||||
// No duplicate signers in input!
|
||||
if (this.hasDuplicateSigners(resolvedSnapshot)) {
|
||||
throw new Error("Duplicate signers found in input.");
|
||||
}
|
||||
|
||||
const addAdminData: string[] = [];
|
||||
const removeAdminData: string[] = [];
|
||||
const addOrUpdateSignerData: string[] = [];
|
||||
const removeSignerData: string[] = [];
|
||||
|
||||
// Remove all existing admins not included in the passed snapshot.
|
||||
const allAdmins = await this.getAllAdmins();
|
||||
const allToMakeAdmin = resolvedSnapshot
|
||||
.filter((item) => item.makeAdmin)
|
||||
.map((item) => item.signer);
|
||||
allAdmins.forEach((admin) => {
|
||||
if (!allToMakeAdmin.includes(admin)) {
|
||||
removeAdminData.push(
|
||||
this.contractWrapper.writeContract.interface.encodeFunctionData(
|
||||
"setAdmin",
|
||||
[admin, false],
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove all existing signers not included in the passed snapshot.
|
||||
const allSigners = await this.getAllSigners();
|
||||
const allToMakeSigners = resolvedSnapshot
|
||||
.filter((item) => {
|
||||
return !item.makeAdmin;
|
||||
})
|
||||
.map((item) => item.signer);
|
||||
await Promise.all(
|
||||
allSigners.map(async (item) => {
|
||||
if (!allToMakeSigners.includes(item.signer)) {
|
||||
const data = await this.buildSignerPermissionRequest(item.signer, {
|
||||
startDate: BigNumber.from(0),
|
||||
expirationDate: BigNumber.from(0),
|
||||
approvedCallTargets: [],
|
||||
nativeTokenLimitPerTransaction: "0",
|
||||
});
|
||||
|
||||
removeSignerData.push(data);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
for (const member of resolvedSnapshot) {
|
||||
// Add new admin
|
||||
if (member.makeAdmin) {
|
||||
addAdminData.push(
|
||||
this.contractWrapper.writeContract.interface.encodeFunctionData(
|
||||
"setAdmin",
|
||||
[member.signer, true],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// Add new scoped
|
||||
const data = await this.buildSignerPermissionRequest(
|
||||
member.signer,
|
||||
member.permissions,
|
||||
);
|
||||
addOrUpdateSignerData.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
const data: string[] = [];
|
||||
removeAdminData.forEach((item) => {
|
||||
data.push(item);
|
||||
});
|
||||
removeSignerData.forEach((item) => {
|
||||
data.push(item);
|
||||
});
|
||||
addOrUpdateSignerData.forEach((item) => {
|
||||
data.push(item);
|
||||
});
|
||||
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "multicall",
|
||||
args: [data],
|
||||
});
|
||||
},
|
||||
);
|
||||
public async resetAllPermissions(
|
||||
permissionSnapshot: PermissionSnapshotInput,
|
||||
) {
|
||||
return assertEnabled(
|
||||
this.accountPermissions,
|
||||
FEATURE_ACCOUNT_PERMISSIONS,
|
||||
).resetAllPermissions(permissionSnapshot);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { BytesLikeSchema } from "../../../../core/schema/shared";
|
||||
import { AddressOrEnsSchema } from "../../shared/AddressOrEnsSchema";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const ExtensionMetadataInput = /* @__PURE__ */ (() =>
|
||||
z.object({
|
||||
name: z.string(),
|
||||
metadataURI: z.string(),
|
||||
implementation: AddressOrEnsSchema,
|
||||
}))();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const ExtensionFunctionInput = /* @__PURE__ */ (() =>
|
||||
z.object({
|
||||
functionSelector: BytesLikeSchema,
|
||||
functionSignature: z.string(),
|
||||
}))();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const ExtensionInput = /* @__PURE__ */ (() =>
|
||||
z.object({
|
||||
metadata: ExtensionMetadataInput,
|
||||
functions: z.array(ExtensionFunctionInput),
|
||||
}))();
|
||||
@@ -31,6 +31,7 @@ export type DeployedContractType =
|
||||
| "implementation"
|
||||
| "create2Factory"
|
||||
| "plugin"
|
||||
| "extension"
|
||||
| "custom";
|
||||
|
||||
export type DeploymentTransaction = {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
ExtensionFunctionInput,
|
||||
ExtensionInput,
|
||||
ExtensionMetadataInput,
|
||||
} from "../schema/contracts/common/extensions";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* Input model to pass an extension's metadata -- name, metadataURI, implementation address
|
||||
* @public
|
||||
*/
|
||||
export type ExtensionMetadata = z.input<typeof ExtensionMetadataInput>;
|
||||
|
||||
/**
|
||||
* Input model to pass a list function-selectors
|
||||
* @public
|
||||
*/
|
||||
export type ExtensionFunction = z.input<typeof ExtensionFunctionInput>;
|
||||
|
||||
/**
|
||||
* Input model to pass a list of extension-addresses + function-selectors
|
||||
* @public
|
||||
*/
|
||||
export type Extension = z.input<typeof ExtensionInput>;
|
||||
@@ -1,5 +1,23 @@
|
||||
# @thirdweb-dev/unity-js-bridge
|
||||
|
||||
## 0.2.68
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74), [`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117), [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
|
||||
- @thirdweb-dev/wallets@1.1.14
|
||||
- @thirdweb-dev/sdk@3.10.50
|
||||
- @thirdweb-dev/auth@3.2.31
|
||||
|
||||
## 0.2.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2), [`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696)]:
|
||||
- @thirdweb-dev/wallets@1.1.13
|
||||
- @thirdweb-dev/sdk@3.10.49
|
||||
- @thirdweb-dev/auth@3.2.30
|
||||
|
||||
## 0.2.66
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/unity-js-bridge",
|
||||
"version": "0.2.66",
|
||||
"version": "0.2.68",
|
||||
"main": "dist/thirdweb-unity-bridge.js",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @thirdweb-dev/wallets
|
||||
|
||||
## 1.1.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1484](https://github.com/thirdweb-dev/js/pull/1484) [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Correct gas estimation for polygon/mumbai
|
||||
|
||||
- Updated dependencies [[`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117), [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
|
||||
- @thirdweb-dev/sdk@3.10.50
|
||||
|
||||
## 1.1.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1477](https://github.com/thirdweb-dev/js/pull/1477) [`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Pass unauth'd RPCs to wallets when adding new chains
|
||||
|
||||
- Updated dependencies [[`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696)]:
|
||||
- @thirdweb-dev/sdk@3.10.49
|
||||
|
||||
## 1.1.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/wallets",
|
||||
"version": "1.1.12",
|
||||
"version": "1.1.14",
|
||||
"main": "dist/thirdweb-dev-wallets.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets.esm.js",
|
||||
"types": "dist/thirdweb-dev-wallets.cjs.d.ts",
|
||||
|
||||
@@ -15,7 +15,8 @@ import type {
|
||||
import BloctoSDK from "@blocto/sdk";
|
||||
import { providers, utils } from "ethers";
|
||||
import { walletIds } from "../../constants/walletIds";
|
||||
import { Chain } from "@thirdweb-dev/chains";
|
||||
import type { Chain } from "@thirdweb-dev/chains";
|
||||
import { getValidPublicRPCUrl } from "../../utils/url";
|
||||
|
||||
type BloctoSigner = providers.JsonRpcSigner;
|
||||
type BloctoOptions = Partial<EthereumProviderConfig>;
|
||||
@@ -165,7 +166,12 @@ export class BloctoConnector extends WagmiConnector<
|
||||
try {
|
||||
await provider.request({
|
||||
method: "wallet_addEthereumChain",
|
||||
params: [{ chainId: id, rpcUrls: [chain?.rpc[0] ?? ""] }],
|
||||
params: [
|
||||
{
|
||||
chainId: id,
|
||||
rpcUrls: getValidPublicRPCUrl(chain), // no client id on purpose here
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await provider.request({
|
||||
|
||||
@@ -15,6 +15,7 @@ import type { CoinbaseWalletSDKOptions } from "@coinbase/wallet-sdk/dist/Coinbas
|
||||
import type { Chain } from "@thirdweb-dev/chains";
|
||||
import { providers, utils } from "ethers";
|
||||
import { walletIds } from "../../constants/walletIds";
|
||||
import { getValidPublicRPCUrl } from "../../utils/url";
|
||||
|
||||
type Options = CoinbaseWalletSDKOptions & {
|
||||
/**
|
||||
@@ -231,7 +232,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
|
||||
chainId: id,
|
||||
chainName: chain.name,
|
||||
nativeCurrency: chain.nativeCurrency,
|
||||
rpcUrls: chain.rpc,
|
||||
rpcUrls: getValidPublicRPCUrl(chain), // no client id on purpose here
|
||||
blockExplorerUrls: this.getBlockExplorerUrls(chain),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from "../../../lib/wagmi-core";
|
||||
import { Ethereum } from "../injected/types";
|
||||
import { AsyncStorage } from "../../../core";
|
||||
import { getValidPublicRPCUrl } from "../../utils/url";
|
||||
|
||||
export type FrameConnectorOptions = {
|
||||
/**
|
||||
@@ -241,7 +242,7 @@ export class FrameConnector extends WagmiConnector<
|
||||
chainId: chainIdHex,
|
||||
chainName: chain.name,
|
||||
nativeCurrency: chain.nativeCurrency,
|
||||
rpc: [chain.rpc[0] ?? ""],
|
||||
rpcUrls: getValidPublicRPCUrl(chain), // no client id on purpose here
|
||||
blockExplorerUrls: this.getBlockExplorerUrls(chain),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -13,8 +13,9 @@ import {
|
||||
} from "../../../lib/wagmi-core";
|
||||
import { assertWindowEthereum } from "../../utils/assertWindowEthereum";
|
||||
import { getInjectedName } from "../../utils/getInjectedName";
|
||||
import { getValidPublicRPCUrl } from "../../utils/url";
|
||||
import { Ethereum } from "./types";
|
||||
import type { Chain } from "@thirdweb-dev/chains";
|
||||
import { getValidChainRPCs, type Chain } from "@thirdweb-dev/chains";
|
||||
import { utils, providers } from "ethers";
|
||||
|
||||
export type InjectedConnectorOptions = {
|
||||
@@ -340,6 +341,7 @@ export class InjectedConnector extends WagmiConnector<
|
||||
?.originalError?.code === 4902
|
||||
) {
|
||||
try {
|
||||
console.log("adding chain", getValidChainRPCs(chain));
|
||||
// request provider to add chain
|
||||
await provider.request({
|
||||
method: "wallet_addEthereumChain",
|
||||
@@ -348,7 +350,7 @@ export class InjectedConnector extends WagmiConnector<
|
||||
chainId: chainIdHex,
|
||||
chainName: chain.name,
|
||||
nativeCurrency: chain.nativeCurrency,
|
||||
rpcUrls: chain.rpc as string[],
|
||||
rpcUrls: getValidPublicRPCUrl(chain), // no client id on purpose here
|
||||
blockExplorerUrls: this.getBlockExplorerUrls(chain),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -18,7 +18,10 @@ import {
|
||||
CeloAlfajoresTestnet,
|
||||
CeloBaklavaTestnet,
|
||||
Celo,
|
||||
Mumbai,
|
||||
Polygon,
|
||||
} from "@thirdweb-dev/chains";
|
||||
import { getPolygonGasPriorityFee } from "@thirdweb-dev/sdk";
|
||||
|
||||
export interface BaseApiParams {
|
||||
provider: providers.Provider;
|
||||
@@ -291,6 +294,9 @@ export abstract class BaseAccountAPI {
|
||||
let { maxFeePerGas, maxPriorityFeePerGas } = info;
|
||||
if (!maxFeePerGas || !maxPriorityFeePerGas) {
|
||||
const feeData = await this.provider.getFeeData();
|
||||
if (!maxPriorityFeePerGas) {
|
||||
maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined;
|
||||
}
|
||||
if (!maxFeePerGas) {
|
||||
maxFeePerGas = feeData.maxFeePerGas ?? undefined;
|
||||
const network = await this.provider.getNetwork();
|
||||
@@ -303,9 +309,22 @@ export abstract class BaseAccountAPI {
|
||||
) {
|
||||
maxPriorityFeePerGas = maxFeePerGas;
|
||||
}
|
||||
}
|
||||
if (!maxPriorityFeePerGas) {
|
||||
maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined;
|
||||
|
||||
if (
|
||||
maxPriorityFeePerGas &&
|
||||
(chainId === Mumbai.chainId || chainId === Polygon.chainId)
|
||||
) {
|
||||
// for polygon/mumbai, override fee data from gas station
|
||||
const block = await this.provider.getBlock("latest");
|
||||
const baseBlockFee =
|
||||
block && block.baseFeePerGas
|
||||
? block.baseFeePerGas
|
||||
: utils.parseUnits("1", "gwei");
|
||||
maxPriorityFeePerGas = await getPolygonGasPriorityFee(chainId);
|
||||
// See: https://eips.ethereum.org/EIPS/eip-1559 for formula
|
||||
const baseMaxFeePerGas = baseBlockFee.mul(2);
|
||||
maxFeePerGas = baseMaxFeePerGas.add(maxPriorityFeePerGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@ import {
|
||||
UserRejectedRequestError,
|
||||
WagmiConnector,
|
||||
} from "../../../lib/wagmi-core";
|
||||
import type { Chain } from "@thirdweb-dev/chains";
|
||||
import { type Chain } from "@thirdweb-dev/chains";
|
||||
import type WalletConnectProvider from "@walletconnect/ethereum-provider";
|
||||
import { providers, utils } from "ethers";
|
||||
import { walletIds } from "../../constants/walletIds";
|
||||
import { QRModalOptions } from "./qrModalOptions";
|
||||
import { getValidPublicRPCUrl } from "../../utils/url";
|
||||
|
||||
const chainsToRequest = new Set([1, 137, 10, 42161, 56]);
|
||||
|
||||
@@ -291,7 +292,7 @@ export class WalletConnectConnector extends WagmiConnector<
|
||||
chainId: utils.hexValue(chain.chainId),
|
||||
chainName: chain.name,
|
||||
nativeCurrency: chain.nativeCurrency,
|
||||
rpcUrls: [...chain.rpc],
|
||||
rpcUrls: getValidPublicRPCUrl(chain), // no clientId on purpose
|
||||
...blockExplorerUrls,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
import { getValidChainRPCs, type Chain } from "@thirdweb-dev/chains";
|
||||
|
||||
export function isTwUrl(url: string): boolean {
|
||||
const host = new URL(url).hostname;
|
||||
return (
|
||||
host.endsWith(".thirdweb.com") || host === "localhost" || host === "0.0.0.0"
|
||||
);
|
||||
}
|
||||
|
||||
export function getValidPublicRPCUrl(chain: Chain) {
|
||||
return getValidChainRPCs(chain).map((rpc) => {
|
||||
try {
|
||||
const url = new URL(rpc);
|
||||
// remove client id from url
|
||||
if (url.hostname.endsWith(".thirdweb.com")) {
|
||||
url.pathname = "";
|
||||
url.search = "";
|
||||
}
|
||||
return url.toString();
|
||||
} catch (e) {
|
||||
return rpc;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user