Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
125c836d88 | ||
|
|
ec36b13a30 | ||
|
|
52b1a0fcac | ||
|
|
a6c36724eb | ||
|
|
6abb845971 | ||
|
|
dc38a243c2 | ||
|
|
03a03a196d | ||
|
|
931270479e | ||
|
|
6f4c5cd746 | ||
|
|
d3c8626a5a | ||
|
|
ffb1bb0a60 | ||
|
|
9bd01de5f9 | ||
|
|
f5caf6a350 | ||
|
|
46672a490a | ||
|
|
f35fbec1be |
@@ -1,5 +1,19 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 3.2.48
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies []:
|
||||
- @thirdweb-dev/wallets@1.3.5
|
||||
|
||||
## 3.2.47
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101)]:
|
||||
- @thirdweb-dev/wallets@1.3.4
|
||||
|
||||
## 3.2.46
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "3.2.46",
|
||||
"version": "3.2.48",
|
||||
"main": "dist/thirdweb-dev-auth.cjs.js",
|
||||
"module": "dist/thirdweb-dev-auth.esm.js",
|
||||
"browser": {
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# thirdweb
|
||||
|
||||
## 0.12.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]:
|
||||
- @thirdweb-dev/sdk@3.10.67
|
||||
- @thirdweb-dev/wallets@1.3.5
|
||||
- @thirdweb-dev/auth@3.2.48
|
||||
|
||||
## 0.12.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101), [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c)]:
|
||||
- @thirdweb-dev/wallets@1.3.4
|
||||
- @thirdweb-dev/sdk@3.10.66
|
||||
- @thirdweb-dev/auth@3.2.47
|
||||
|
||||
## 0.12.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.12.8",
|
||||
"version": "0.12.10",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/contracts-js",
|
||||
"dependencies": {
|
||||
"@thirdweb-dev/contracts": "3.10.0"
|
||||
"@thirdweb-dev/contracts": "3.10.1-0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.22.9",
|
||||
|
||||
@@ -1,5 +1,65 @@
|
||||
# @thirdweb-dev/react-core
|
||||
|
||||
## 3.16.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]:
|
||||
- @thirdweb-dev/sdk@3.10.67
|
||||
- @thirdweb-dev/wallets@1.3.5
|
||||
- @thirdweb-dev/auth@3.2.48
|
||||
|
||||
## 3.16.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1719](https://github.com/thirdweb-dev/js/pull/1719) [`93127047`](https://github.com/thirdweb-dev/js/commit/931270479ef227556a1077357a8c000b08de6e8d) Thanks [@MananTank](https://github.com/MananTank)! - Type refactor for useWallet hook
|
||||
|
||||
- [#1716](https://github.com/thirdweb-dev/js/pull/1716) [`d3c8626a`](https://github.com/thirdweb-dev/js/commit/d3c8626a5a8def882c1592b236048ebe88e85d49) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Better `useWallet()` API to obtain specific wallet instances.
|
||||
|
||||
```
|
||||
const smartWallet = useWallet("smartWallet"); // returns a SmartWallet instance
|
||||
const embeddedWallet = useWallet("embeddedWallet"); // returns a EmbeddedWallet instance
|
||||
```
|
||||
|
||||
- [#1712](https://github.com/thirdweb-dev/js/pull/1712) [`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Add `useCreateSessionKey` and `useRevokeSessionKey` hooks
|
||||
|
||||
```
|
||||
const Component = () => {
|
||||
const {
|
||||
mutate: createSessionKey,
|
||||
isLoading,
|
||||
error,
|
||||
} = useCreateSessionKey();
|
||||
|
||||
if (error) {
|
||||
console.error("failed to create session key", error);
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
disabled={isLoading}
|
||||
onClick={() => createSessionKey(
|
||||
"0x...",
|
||||
{
|
||||
approvedCallTargets: ["0x..."], // the addresses of contracts that the session key can call
|
||||
nativeTokenLimitPerTransaction: 0.1, // the maximum amount of native token (in ETH) that the session key can spend per transaction
|
||||
startDate: new Date(), // the date when the session key becomes active
|
||||
expirationDate = new Date(Date.now() + 24 * 60 * 60 * 1000); // the date when the session key expires
|
||||
}
|
||||
)}
|
||||
>
|
||||
Create Session Key
|
||||
</button>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101), [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c)]:
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
- @thirdweb-dev/[email protected]
|
||||
|
||||
## 3.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-core",
|
||||
"version": "3.16.3",
|
||||
"version": "3.16.5",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,16 +1,74 @@
|
||||
import { useWalletContext } from "../providers/thirdweb-wallet-provider";
|
||||
import invariant from "tiny-invariant";
|
||||
import type {
|
||||
BloctoWallet,
|
||||
CoinbaseWallet,
|
||||
EmbeddedWallet,
|
||||
FrameWallet,
|
||||
LocalWallet,
|
||||
MagicLink,
|
||||
MetaMaskWallet,
|
||||
PaperWallet,
|
||||
PhantomWallet,
|
||||
RainbowWallet,
|
||||
SafeWallet,
|
||||
SmartWallet,
|
||||
TrustWallet,
|
||||
WalletConnect,
|
||||
walletIds,
|
||||
} from "@thirdweb-dev/wallets";
|
||||
import { WalletInstance } from "../types/wallet";
|
||||
|
||||
export type WalletId = (typeof walletIds)[keyof typeof walletIds];
|
||||
|
||||
type WalletIdToWalletTypeMap = {
|
||||
metamask: MetaMaskWallet;
|
||||
coinbase: CoinbaseWallet;
|
||||
rainbowWallet: RainbowWallet;
|
||||
blocto: BloctoWallet;
|
||||
frame: FrameWallet;
|
||||
localWallet: LocalWallet;
|
||||
magicLink: MagicLink;
|
||||
paper: PaperWallet;
|
||||
smartWallet: SmartWallet;
|
||||
safe: SafeWallet;
|
||||
trust: TrustWallet;
|
||||
embeddedWallet: EmbeddedWallet;
|
||||
walletConnect: WalletConnect;
|
||||
phantom: PhantomWallet;
|
||||
walletConnectV1: WalletConnect;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns the current active wallet instance
|
||||
*/
|
||||
export function useWallet() {
|
||||
export function useWallet<T extends WalletId>(
|
||||
walletId: T,
|
||||
): WalletIdToWalletTypeMap[T] | undefined;
|
||||
export function useWallet(): WalletInstance | undefined;
|
||||
export function useWallet<T extends WalletId>(walletId?: T) {
|
||||
const context = useWalletContext();
|
||||
invariant(
|
||||
context,
|
||||
"useWallet() hook must be used within a <ThirdwebProvider/>",
|
||||
);
|
||||
return context.activeWallet;
|
||||
|
||||
const activeWallet = context.activeWallet;
|
||||
|
||||
if (!activeWallet) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// if walletId is provided, return the wallet instance only if it matches the walletId
|
||||
if (walletId) {
|
||||
if (activeWallet.walletId === walletId) {
|
||||
return activeWallet as WalletIdToWalletTypeMap[T];
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return activeWallet;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
import type { providers } from "ethers";
|
||||
import {
|
||||
RequiredParam,
|
||||
requiredParamInvariant,
|
||||
} from "../../../core/query-utils/required-param";
|
||||
import { useSDKChainId } from "../useSDK";
|
||||
import {
|
||||
cacheKeys,
|
||||
invalidateContractAndBalances,
|
||||
} from "../../utils/cache-keys";
|
||||
import { cacheKeys } from "../../utils/cache-keys";
|
||||
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
|
||||
import {
|
||||
useMutation,
|
||||
UseMutationResult,
|
||||
useQueryClient,
|
||||
UseQueryResult,
|
||||
} from "@tanstack/react-query";
|
||||
import type {
|
||||
SignerWithPermissions,
|
||||
PermissionSnapshotInput,
|
||||
SmartContract,
|
||||
SignerPermissionsInput,
|
||||
TransactionResult,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import invariant from "tiny-invariant";
|
||||
import { WalletAddress } from "../../types";
|
||||
import { useWallet } from "../../../core/hooks/wallet-hooks";
|
||||
|
||||
/** **********************/
|
||||
/** READ HOOKS **/
|
||||
@@ -127,65 +123,180 @@ export function useAccountAdminsAndSigners(
|
||||
/** **********************/
|
||||
/** WRITE HOOKS **/
|
||||
/** **********************/
|
||||
|
||||
export type CreateSessionKeyInput = {
|
||||
keyAddress: string;
|
||||
permissions: SignerPermissionsInput;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the account's entire snapshot of permissions
|
||||
* Create and add a session key for the smart wallet
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* const Component = () => {
|
||||
* const { contract } = useContract("{{contract_address}}");
|
||||
* const {
|
||||
* mutate: setAccountSigners,
|
||||
* mutate: createSessionKey,
|
||||
* isLoading,
|
||||
* error,
|
||||
* } = useSetAccountSigners(contract);
|
||||
* } = useCreateSessionKey();
|
||||
*
|
||||
* if (error) {
|
||||
* console.error("failed to set account signers", error);
|
||||
* console.error("failed to create session key", error);
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <button
|
||||
* disabled={isLoading}
|
||||
* onClick={() => setAccountSigners("0x...")}
|
||||
* onClick={() => createSessionKey(
|
||||
* "0x...",
|
||||
* {
|
||||
* approvedCallTargets: ["0x..."], // the addresses of contracts that the session key can call
|
||||
* nativeTokenLimitPerTransaction: 0.1, // the maximum amount of native token (in ETH) that the session key can spend per transaction
|
||||
* startDate: new Date(), // the date when the session key becomes active
|
||||
* expirationDate = new Date(Date.now() + 24 * 60 * 60 * 1000); // the date when the session key expires
|
||||
* }
|
||||
* )}
|
||||
* >
|
||||
* Set Account Signers
|
||||
* Create Session Key
|
||||
* </button>
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @param contract - an instance of a account contract
|
||||
* @returns a mutation object that can be used to set the account signers
|
||||
* @twfeature Account
|
||||
* @see {@link https://portal.thirdweb.com/react/react.usesetaccountsigners?utm_source=sdk | Documentation}
|
||||
* @beta
|
||||
*/
|
||||
export function useSetAccountSigners(
|
||||
contract: RequiredParam<SmartContract>,
|
||||
): UseMutationResult<
|
||||
{ receipt: providers.TransactionReceipt },
|
||||
export function useCreateSessionKey(): UseMutationResult<
|
||||
TransactionResult,
|
||||
unknown,
|
||||
PermissionSnapshotInput,
|
||||
unknown
|
||||
CreateSessionKeyInput
|
||||
> {
|
||||
const activeChainId = useSDKChainId();
|
||||
const contractAddress = contract?.getAddress();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
async (permissionsSnapshot: PermissionSnapshotInput) => {
|
||||
requiredParamInvariant(contract, "contract is undefined");
|
||||
|
||||
return contract.account.resetAllPermissions(permissionsSnapshot);
|
||||
},
|
||||
{
|
||||
onSettled: () =>
|
||||
invalidateContractAndBalances(
|
||||
queryClient,
|
||||
contractAddress,
|
||||
activeChainId,
|
||||
),
|
||||
},
|
||||
);
|
||||
const smartWallet = useWallet("smartWallet");
|
||||
return useMutation(async (args: CreateSessionKeyInput) => {
|
||||
requiredParamInvariant(smartWallet, "wallet is not connected");
|
||||
return smartWallet.createSessionKey(args.keyAddress, args.permissions);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke a session key on the smart wallet
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* const Component = () => {
|
||||
* const {
|
||||
* mutate: revokeSessionKey,
|
||||
* isLoading,
|
||||
* error,
|
||||
* } = useRevokeSessionKey();
|
||||
*
|
||||
* if (error) {
|
||||
* console.error("failed to revoke session key", error);
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <button
|
||||
* disabled={isLoading}
|
||||
* onClick={() => revokeSessionKey("0x...")}
|
||||
* >
|
||||
* Revoke Session Key
|
||||
* </button>
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
*/
|
||||
export function useRevokeSessionKey(): UseMutationResult<
|
||||
TransactionResult,
|
||||
unknown,
|
||||
string
|
||||
> {
|
||||
const smartWallet = useWallet("smartWallet");
|
||||
return useMutation(async (keyAddress: string) => {
|
||||
requiredParamInvariant(smartWallet, "wallet is not connected");
|
||||
return smartWallet.revokeSessionKey(keyAddress);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an additional admin on the smart wallet
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* const Component = () => {
|
||||
* const {
|
||||
* mutate: addAdmin,
|
||||
* isLoading,
|
||||
* error,
|
||||
* } = useAddAdmin();
|
||||
*
|
||||
* if (error) {
|
||||
* console.error("failed to add admin", error);
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <button
|
||||
* disabled={isLoading}
|
||||
* onClick={() => addAdmin("0x...")}
|
||||
* >
|
||||
* Add admin
|
||||
* </button>
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
*/
|
||||
export function useAddAdmin(): UseMutationResult<
|
||||
TransactionResult,
|
||||
unknown,
|
||||
string
|
||||
> {
|
||||
const smartWallet = useWallet("smartWallet");
|
||||
return useMutation(async (adminAddress: string) => {
|
||||
requiredParamInvariant(smartWallet, "wallet is not connected");
|
||||
return smartWallet.addAdmin(adminAddress);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an additional admin on the smart wallet
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
* const Component = () => {
|
||||
* const {
|
||||
* mutate: removeAdmin,
|
||||
* isLoading,
|
||||
* error,
|
||||
* } = useRemoveAdmin();
|
||||
*
|
||||
* if (error) {
|
||||
* console.error("failed to remove admin", error);
|
||||
* }
|
||||
*
|
||||
* return (
|
||||
* <button
|
||||
* disabled={isLoading}
|
||||
* onClick={() => removeAdmin("0x...")}
|
||||
* >
|
||||
* Remove admin
|
||||
* </button>
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @twfeature Account
|
||||
*/
|
||||
export function useRemoveAdmin(): UseMutationResult<
|
||||
TransactionResult,
|
||||
unknown,
|
||||
string
|
||||
> {
|
||||
const smartWallet = useWallet("smartWallet");
|
||||
return useMutation(async (adminAddress: string) => {
|
||||
requiredParamInvariant(smartWallet, "wallet is not connected");
|
||||
return smartWallet.removeAdmin(adminAddress);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -205,7 +205,10 @@ export {
|
||||
// account
|
||||
export {
|
||||
useAccountSigners,
|
||||
useSetAccountSigners,
|
||||
useAddAdmin,
|
||||
useRemoveAdmin,
|
||||
useCreateSessionKey,
|
||||
useRevokeSessionKey,
|
||||
useAccountAdmins,
|
||||
useAccountAdminsAndSigners,
|
||||
} from "./hooks/async/account";
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# @thirdweb-dev/react-native-compat
|
||||
|
||||
## 0.3.4
|
||||
|
||||
## 0.3.3
|
||||
|
||||
## 0.3.2
|
||||
|
||||
## 0.3.1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-native-compat",
|
||||
"description": "Shims for Thirdweb in React Native Projects",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.4",
|
||||
"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.3.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]:
|
||||
- @thirdweb-dev/sdk@3.10.67
|
||||
- @thirdweb-dev/react-core@3.16.5
|
||||
- @thirdweb-dev/wallets@1.3.5
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1715](https://github.com/thirdweb-dev/js/pull/1715) [`f5caf6a3`](https://github.com/thirdweb-dev/js/commit/f5caf6a3502905666ae57f3304bb9074e09c9366) Thanks [@iketw](https://github.com/iketw)! - Fix encryption/decryption bug in embeddedWallet for React Native
|
||||
|
||||
- [#1709](https://github.com/thirdweb-dev/js/pull/1709) [`6f4c5cd7`](https://github.com/thirdweb-dev/js/commit/6f4c5cd746caf07f057e272aec9f204ba5905cd3) Thanks [@ElasticBottle](https://github.com/ElasticBottle)! - feat(react-native): update iteration count for embedded-wallet for better speed and future updates
|
||||
|
||||
- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101), [`93127047`](https://github.com/thirdweb-dev/js/commit/931270479ef227556a1077357a8c000b08de6e8d), [`d3c8626a`](https://github.com/thirdweb-dev/js/commit/d3c8626a5a8def882c1592b236048ebe88e85d49), [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c), [`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101)]:
|
||||
- @thirdweb-dev/wallets@1.3.4
|
||||
- @thirdweb-dev/react-core@3.16.4
|
||||
- @thirdweb-dev/sdk@3.10.66
|
||||
|
||||
## 0.3.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-native",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.4",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
+73
-15
@@ -1,19 +1,21 @@
|
||||
import AesGcmCrypto from "react-native-aes-gcm-crypto";
|
||||
import { getRandomValues } from "../getRandomValues";
|
||||
import QuickCrypto from "react-native-quick-crypto";
|
||||
import { getRandomValues } from "../getRandomValues";
|
||||
|
||||
const ENCRYPTION_SEPARATOR = ":";
|
||||
const KEY_ITERATION_COUNT = 5000000;
|
||||
const DEPRECATED_KEY_ITERATION_COUNT = 5000000;
|
||||
const CURRENT_KEY_ITERATION_COUNT = 650000;
|
||||
const KEY_LENGTH = 256;
|
||||
|
||||
export async function getEncryptionKey(
|
||||
pwd: string,
|
||||
salt: ArrayBuffer,
|
||||
iterationCounts: number,
|
||||
): Promise<string> {
|
||||
const key = QuickCrypto.pbkdf2Sync(
|
||||
pwd,
|
||||
salt,
|
||||
KEY_ITERATION_COUNT,
|
||||
iterationCounts,
|
||||
KEY_LENGTH,
|
||||
"sha256",
|
||||
);
|
||||
@@ -40,7 +42,9 @@ export async function encryptShareWeb(
|
||||
pwd: string,
|
||||
): Promise<string> {
|
||||
const salt = getRandomValues(new Uint8Array(16));
|
||||
const keyBase64 = await getEncryptionKey(pwd, salt);
|
||||
const iterationCount = CURRENT_KEY_ITERATION_COUNT;
|
||||
|
||||
const keyBase64 = await getEncryptionKey(pwd, salt, iterationCount);
|
||||
|
||||
let encryptedValue;
|
||||
try {
|
||||
@@ -49,11 +53,23 @@ export async function encryptShareWeb(
|
||||
throw new Error(`Error encrypting share: ${error}`);
|
||||
}
|
||||
|
||||
const returnValue = `${encryptedValue.content}${ENCRYPTION_SEPARATOR}${
|
||||
encryptedValue.iv
|
||||
}${ENCRYPTION_SEPARATOR}${bufferToBase64(salt)}${ENCRYPTION_SEPARATOR}${
|
||||
encryptedValue.tag
|
||||
}`;
|
||||
// Ref: https://github.com/craftzdog/react-native-aes-gcm-crypto/blob/master/android/src/main/java/com/reactnativeaesgcmcrypto/AesGcmCryptoModule.kt#L111
|
||||
// Cipher text is a base64 string
|
||||
const cipherTextBase64Buffer = Buffer.from(encryptedValue.content, "base64");
|
||||
// Tag is a hex string
|
||||
const tagHexBuffer = Buffer.from(encryptedValue.tag, "hex");
|
||||
const cipherTextWithTag = Buffer.concat([
|
||||
cipherTextBase64Buffer,
|
||||
tagHexBuffer,
|
||||
]);
|
||||
// iv is a hex string
|
||||
const ivBase64 = Buffer.from(encryptedValue.iv, "hex").toString("base64");
|
||||
|
||||
const returnValue = `${bufferToBase64(
|
||||
cipherTextWithTag,
|
||||
)}${ENCRYPTION_SEPARATOR}${ivBase64}${ENCRYPTION_SEPARATOR}${bufferToBase64(
|
||||
salt,
|
||||
)}${ENCRYPTION_SEPARATOR}${iterationCount}`;
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
@@ -62,16 +78,58 @@ export async function decryptShareWeb(
|
||||
encryptedShareDetails: string,
|
||||
pwd: string,
|
||||
): Promise<string> {
|
||||
const [encryptedShare, iv, salt, tag] =
|
||||
encryptedShareDetails.split(ENCRYPTION_SEPARATOR);
|
||||
const [
|
||||
encryptedShareWithTagBase64,
|
||||
ivBase64,
|
||||
saltBase64,
|
||||
maybeIterationCount,
|
||||
] = encryptedShareDetails.split(ENCRYPTION_SEPARATOR);
|
||||
|
||||
const key = await getEncryptionKey(pwd, base64ToBuffer(salt));
|
||||
let iterationCount = maybeIterationCount
|
||||
? parseInt(maybeIterationCount)
|
||||
: undefined;
|
||||
if (!iterationCount) {
|
||||
iterationCount = DEPRECATED_KEY_ITERATION_COUNT;
|
||||
}
|
||||
|
||||
const key = await getEncryptionKey(
|
||||
pwd,
|
||||
base64ToBuffer(saltBase64),
|
||||
iterationCount,
|
||||
);
|
||||
|
||||
const encryptedShareWithTagBuffer = Buffer.from(
|
||||
encryptedShareWithTagBase64,
|
||||
"base64",
|
||||
);
|
||||
|
||||
// The tag is a 16 bytes long hex string
|
||||
const tagBytesLength = 16;
|
||||
const cipherTextBufferLength =
|
||||
encryptedShareWithTagBuffer.length - tagBytesLength;
|
||||
|
||||
// Get cipherText and tag from encryptedShareWithTagBuffer
|
||||
const cipherTextBuffer = Buffer.from(
|
||||
encryptedShareWithTagBuffer.subarray(0, cipherTextBufferLength),
|
||||
);
|
||||
const tagBuffer = Buffer.from(
|
||||
encryptedShareWithTagBuffer.subarray(cipherTextBufferLength),
|
||||
);
|
||||
|
||||
const originalBase64CipherText = cipherTextBuffer.toString("base64");
|
||||
// converting to hex since the decrypt function expects a hex string
|
||||
// Ref: https://github.com/craftzdog/react-native-aes-gcm-crypto/blob/master/android/src/main/java/com/reactnativeaesgcmcrypto/AesGcmCryptoModule.kt#L111
|
||||
const hexStringTag = tagBuffer.toString("hex");
|
||||
|
||||
// converting to hex since the decrypt function expects a hex string
|
||||
// Ref: https://github.com/craftzdog/react-native-aes-gcm-crypto/blob/master/android/src/main/java/com/reactnativeaesgcmcrypto/AesGcmCryptoModule.kt#L111
|
||||
const ivBufferHex = Buffer.from(ivBase64, "base64").toString("hex");
|
||||
|
||||
const normalizedShare = await AesGcmCrypto.decrypt(
|
||||
encryptedShare,
|
||||
originalBase64CipherText,
|
||||
key,
|
||||
iv,
|
||||
tag,
|
||||
ivBufferHex,
|
||||
hexStringTag,
|
||||
false,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 3.16.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]:
|
||||
- @thirdweb-dev/sdk@3.10.67
|
||||
- @thirdweb-dev/react-core@3.16.5
|
||||
- @thirdweb-dev/wallets@1.3.5
|
||||
|
||||
## 3.16.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1713](https://github.com/thirdweb-dev/js/pull/1713) [`46672a49`](https://github.com/thirdweb-dev/js/commit/46672a490a82924e3c4a003419e3eaad10d6dd8b) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Update upsell link
|
||||
|
||||
- [#1717](https://github.com/thirdweb-dev/js/pull/1717) [`ffb1bb0a`](https://github.com/thirdweb-dev/js/commit/ffb1bb0a603a69c22eb8b6429326c23a2977e5a6) Thanks [@MananTank](https://github.com/MananTank)! - Add spinner in the Google Sign in popup instead of blank page
|
||||
|
||||
- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101), [`93127047`](https://github.com/thirdweb-dev/js/commit/931270479ef227556a1077357a8c000b08de6e8d), [`d3c8626a`](https://github.com/thirdweb-dev/js/commit/d3c8626a5a8def882c1592b236048ebe88e85d49), [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c), [`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101)]:
|
||||
- @thirdweb-dev/wallets@1.3.4
|
||||
- @thirdweb-dev/react-core@3.16.4
|
||||
- @thirdweb-dev/sdk@3.10.66
|
||||
|
||||
## 3.16.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.16.3",
|
||||
"version": "3.16.5",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -43,3 +43,23 @@ export {
|
||||
|
||||
// react-core
|
||||
export * from "@thirdweb-dev/react-core";
|
||||
// wallets
|
||||
export {
|
||||
LocalWallet,
|
||||
EmbeddedWallet,
|
||||
SmartWallet,
|
||||
SafeWallet,
|
||||
WalletConnect,
|
||||
PhantomWallet,
|
||||
RainbowWallet,
|
||||
MetaMaskWallet,
|
||||
TrustWallet,
|
||||
CoinbaseWallet,
|
||||
BloctoWallet,
|
||||
FrameWallet,
|
||||
PaperWallet,
|
||||
ZerionWallet,
|
||||
MagicLink,
|
||||
SignerWallet,
|
||||
InjectedWallet,
|
||||
} from "@thirdweb-dev/wallets";
|
||||
|
||||
@@ -106,7 +106,7 @@ export const WalletSelector: React.FC<{
|
||||
color="primaryText"
|
||||
hoverColor="accentText"
|
||||
target="_blank"
|
||||
href="https://thirdweb.com/dashboard/wallets/connect"
|
||||
href="https://thirdweb.com/connect?utm_source=cw"
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
export function openGoogleSignInWindow() {
|
||||
const win = window.open("", undefined, "width=350, height=500");
|
||||
if (win) {
|
||||
win.document.title = "Sign In - Google Accounts";
|
||||
win.document.body.innerHTML = spinnerWindowHtml;
|
||||
}
|
||||
return win;
|
||||
}
|
||||
|
||||
const spinnerWindowHtml = `
|
||||
<svg class="loader" viewBox="0 0 50 50">
|
||||
<circle
|
||||
cx="25"
|
||||
cy="25"
|
||||
r="20"
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="4"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loader {
|
||||
width: 15vw;
|
||||
height: 15vw;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
.loader circle {
|
||||
animation: loading 1.5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
stroke-dasharray: 1, 150;
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
50% {
|
||||
stroke-dasharray: 90, 150;
|
||||
stroke-dashoffset: -35;
|
||||
}
|
||||
100% {
|
||||
stroke-dasharray: 90, 150;
|
||||
stroke-dashoffset: -124;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
@@ -14,6 +14,7 @@ import { GoogleIcon } from "../../ConnectWallet/icons/GoogleIcon";
|
||||
import { InputSelectionUI } from "../InputSelectionUI";
|
||||
import type { EmbeddedWalletLoginType } from "./types";
|
||||
import { TextDivider } from "../../../components/TextDivider";
|
||||
import { openGoogleSignInWindow } from "../../utils/openGoogleSignInWindow";
|
||||
|
||||
export const EmbeddedWalletFormUI = (props: {
|
||||
onSelect: (loginType: EmbeddedWalletLoginType) => void;
|
||||
@@ -29,7 +30,8 @@ export const EmbeddedWalletFormUI = (props: {
|
||||
try {
|
||||
const embeddedWallet = createWalletInstance(props.walletConfig);
|
||||
setConnectionStatus("connecting");
|
||||
const googleWindow = window.open("", "Login", "width=350, height=500");
|
||||
|
||||
const googleWindow = openGoogleSignInWindow();
|
||||
if (!googleWindow) {
|
||||
throw new Error("Failed to open google login window");
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ModalTitle } from "../../../components/modalElements";
|
||||
import { Text } from "../../../components/text";
|
||||
import { iconSize } from "../../../design-system";
|
||||
import { GoogleIcon } from "../../ConnectWallet/icons/GoogleIcon";
|
||||
import { openGoogleSignInWindow } from "../../utils/openGoogleSignInWindow";
|
||||
|
||||
export const EmbeddedWalletGoogleLogin = (
|
||||
props: ConnectUIProps<EmbeddedWallet>,
|
||||
@@ -29,7 +30,7 @@ export const EmbeddedWalletGoogleLogin = (
|
||||
try {
|
||||
const embeddedWallet = createWalletInstance(props.walletConfig);
|
||||
setConnectionStatus("connecting");
|
||||
const googleWindow = window.open("", "Login", "width=350, height=500");
|
||||
const googleWindow = openGoogleSignInWindow();
|
||||
if (!googleWindow) {
|
||||
throw new Error("Failed to open google login window");
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import { GoogleIcon } from "../../ConnectWallet/icons/GoogleIcon";
|
||||
import { InputSelectionUI } from "../InputSelectionUI";
|
||||
import { PaperLoginType } from "./types";
|
||||
import { TextDivider } from "../../../components/TextDivider";
|
||||
import { openGoogleSignInWindow } from "../../utils/openGoogleSignInWindow";
|
||||
|
||||
export const PaperFormUI = (props: {
|
||||
onSelect: (loginType: PaperLoginType) => void;
|
||||
@@ -30,7 +31,7 @@ export const PaperFormUI = (props: {
|
||||
try {
|
||||
const paperWallet = createWalletInstance(props.walletConfig);
|
||||
setConnectionStatus("connecting");
|
||||
const googleWindow = window.open("", "Login", "width=350, height=500");
|
||||
const googleWindow = openGoogleSignInWindow();
|
||||
if (!googleWindow) {
|
||||
throw new Error("Failed to open google login window");
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ModalTitle } from "../../../components/modalElements";
|
||||
import { Text } from "../../../components/text";
|
||||
import { iconSize } from "../../../design-system";
|
||||
import { GoogleIcon } from "../../ConnectWallet/icons/GoogleIcon";
|
||||
import { openGoogleSignInWindow } from "../../utils/openGoogleSignInWindow";
|
||||
|
||||
export const PaperGoogleLogin = (props: ConnectUIProps<PaperWallet>) => {
|
||||
const { goBack, modalSize } = props;
|
||||
@@ -29,7 +30,7 @@ export const PaperGoogleLogin = (props: ConnectUIProps<PaperWallet>) => {
|
||||
try {
|
||||
const paperWallet = createWalletInstance(props.walletConfig);
|
||||
setConnectionStatus("connecting");
|
||||
const googleWindow = window.open("", "Login", "width=350, height=500");
|
||||
const googleWindow = openGoogleSignInWindow();
|
||||
if (!googleWindow) {
|
||||
throw new Error("Failed to open google login window");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 3.10.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1731](https://github.com/thirdweb-dev/js/pull/1731) [`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Improve add/removeContract methods
|
||||
|
||||
- [#1727](https://github.com/thirdweb-dev/js/pull/1727) [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae) Thanks [@kien-ngo](https://github.com/kien-ngo)! - [SDK] Improve concurrent requests on NFT-related code
|
||||
|
||||
- [#1728](https://github.com/thirdweb-dev/js/pull/1728) [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48) Thanks [@kien-ngo](https://github.com/kien-ngo)! - [SDK] Improve transfer function for user wallet
|
||||
|
||||
## 3.10.66
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1308](https://github.com/thirdweb-dev/js/pull/1308) [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Base Router for dynamic contracts
|
||||
|
||||
## 3.10.65
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "3.10.65",
|
||||
"version": "3.10.67",
|
||||
"description": "The main thirdweb SDK.",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -252,7 +252,7 @@ export class AuctionHasNotEndedError extends Error {
|
||||
export class ExtensionNotImplementedError extends Error {
|
||||
constructor(feature: Feature) {
|
||||
super(
|
||||
`This functionality is not available because the contract does not implement the '${feature.docLinks.contracts}' Extension. Learn how to unlock this functionality at https://portal.thirdweb.com/extensions `,
|
||||
`This functionality is not available because the contract does not implement the '${feature.name}' Extension. Learn how to unlock this functionality at https://portal.thirdweb.com/extensions `,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,8 +114,10 @@ export async function fetchTokenMetadataForContract(
|
||||
ERC165MetadataAbi,
|
||||
provider,
|
||||
) as IERC165;
|
||||
const isERC721 = await erc165.supportsInterface(InterfaceId_IERC721);
|
||||
const isERC1155 = await erc165.supportsInterface(InterfaceId_IERC1155);
|
||||
const [isERC721, isERC1155] = await Promise.all([
|
||||
erc165.supportsInterface(InterfaceId_IERC721),
|
||||
erc165.supportsInterface(InterfaceId_IERC1155),
|
||||
]);
|
||||
if (isERC721) {
|
||||
const erc721 = new Contract(
|
||||
contractAddress,
|
||||
|
||||
@@ -43,7 +43,7 @@ export function generatePluginFunctions(
|
||||
}
|
||||
|
||||
export function generateExtensionFunctions(
|
||||
extensionAbi: any,
|
||||
extensionAbi: Abi,
|
||||
): ExtensionFunction[] {
|
||||
const extensionInterface = new utils.Interface(extensionAbi);
|
||||
const extensionFunctions: ExtensionFunction[] = [];
|
||||
|
||||
@@ -205,7 +205,7 @@ export const FEATURE_GASLESS = {
|
||||
docLinks: {
|
||||
sdk: "sdk.gaslesstransaction",
|
||||
// TODO add the correct name for this once it's added to portal
|
||||
contracts: "",
|
||||
contracts: "IERC2771Context",
|
||||
},
|
||||
abis: [IERC2771ContextAbi],
|
||||
features: {},
|
||||
@@ -217,7 +217,7 @@ export const FEATURE_PACK_VRF = {
|
||||
docLinks: {
|
||||
sdk: "sdk.packvrf",
|
||||
//TODO
|
||||
contracts: "",
|
||||
contracts: "IPackVRF",
|
||||
},
|
||||
abis: [IPackVRFAbi],
|
||||
features: {},
|
||||
@@ -229,7 +229,7 @@ export const FEATURE_PLUGIN_ROUTER = {
|
||||
docLinks: {
|
||||
sdk: "sdk.pluginrouter",
|
||||
//TODO
|
||||
contracts: "",
|
||||
contracts: "PluginRouter",
|
||||
},
|
||||
abis: [getAllPluginsAbi],
|
||||
features: {},
|
||||
@@ -241,7 +241,7 @@ export const FEATURE_DYNAMIC_CONTRACT = {
|
||||
docLinks: {
|
||||
sdk: "",
|
||||
//TODO
|
||||
contracts: "",
|
||||
contracts: "IExtensionsManager",
|
||||
},
|
||||
abis: [getAllExtensionsAbi],
|
||||
features: {},
|
||||
@@ -253,7 +253,7 @@ export const FEATURE_DIRECT_LISTINGS = {
|
||||
docLinks: {
|
||||
// TODO
|
||||
sdk: "",
|
||||
contracts: "",
|
||||
contracts: "IDirectListings",
|
||||
},
|
||||
abis: [IDirectListingsAbi],
|
||||
features: {},
|
||||
@@ -265,7 +265,7 @@ export const FEATURE_ENGLISH_AUCTIONS = {
|
||||
docLinks: {
|
||||
// TODO
|
||||
sdk: "",
|
||||
contracts: "",
|
||||
contracts: "IEnglishAuctions",
|
||||
},
|
||||
abis: [IEnglishAuctionsAbi],
|
||||
features: {},
|
||||
@@ -277,7 +277,7 @@ export const FEATURE_OFFERS = {
|
||||
docLinks: {
|
||||
// TODO
|
||||
sdk: "",
|
||||
contracts: "",
|
||||
contracts: "IOffers",
|
||||
},
|
||||
abis: [IOffersAbi],
|
||||
features: {},
|
||||
@@ -289,7 +289,7 @@ export const FEATURE_ACCOUNT_FACTORY = {
|
||||
docLinks: {
|
||||
// TODO
|
||||
sdk: "sdk.accountFactory",
|
||||
contracts: "",
|
||||
contracts: "IAccountFactory",
|
||||
},
|
||||
abis: [IAccountFactoryCore],
|
||||
features: {},
|
||||
@@ -301,7 +301,7 @@ export const FEATURE_ACCOUNT_PERMISSIONS = {
|
||||
docLinks: {
|
||||
// TODO
|
||||
sdk: "sdk.account",
|
||||
contracts: "",
|
||||
contracts: "IAccountPermissions",
|
||||
},
|
||||
abis: [IAccountPermissions],
|
||||
features: {},
|
||||
@@ -313,7 +313,7 @@ export const FEATURE_ACCOUNT = {
|
||||
docLinks: {
|
||||
// TODO
|
||||
sdk: "sdk.account",
|
||||
contracts: "",
|
||||
contracts: "IAccount",
|
||||
},
|
||||
abis: [IAccount],
|
||||
features: { [FEATURE_ACCOUNT_PERMISSIONS.name]: FEATURE_ACCOUNT_PERMISSIONS },
|
||||
@@ -325,7 +325,7 @@ export const FEATURE_AIRDROP_ERC20 = {
|
||||
docLinks: {
|
||||
// TODO
|
||||
sdk: "",
|
||||
contracts: "",
|
||||
contracts: "IAirdropERC20",
|
||||
},
|
||||
abis: [IAirdropERC20],
|
||||
features: {},
|
||||
@@ -337,7 +337,7 @@ export const FEATURE_AIRDROP_ERC721 = {
|
||||
docLinks: {
|
||||
// TODO
|
||||
sdk: "",
|
||||
contracts: "",
|
||||
contracts: "IAirdropERC721",
|
||||
},
|
||||
abis: [IAirdropERC721],
|
||||
features: {},
|
||||
@@ -349,7 +349,7 @@ export const FEATURE_AIRDROP_ERC1155 = {
|
||||
docLinks: {
|
||||
// TODO
|
||||
sdk: "",
|
||||
contracts: "",
|
||||
contracts: "IAirdropERC1155",
|
||||
},
|
||||
abis: [IAirdropERC1155],
|
||||
features: {},
|
||||
|
||||
@@ -226,8 +226,10 @@ export class NFTDrop extends StandardErc721<PrebuiltNFTDrop> {
|
||||
* Get the total count NFTs in this drop contract, both claimed and unclaimed
|
||||
*/
|
||||
override async totalSupply() {
|
||||
const claimed = await this.totalClaimedSupply();
|
||||
const unclaimed = await this.totalUnclaimedSupply();
|
||||
const [claimed, unclaimed] = await Promise.all([
|
||||
this.totalClaimedSupply(),
|
||||
this.totalUnclaimedSupply(),
|
||||
]);
|
||||
return claimed.add(unclaimed);
|
||||
}
|
||||
|
||||
|
||||
@@ -249,8 +249,10 @@ export class SignatureDrop extends StandardErc721<SignatureDropContract> {
|
||||
* Get the total count NFTs in this drop contract, both claimed and unclaimed
|
||||
*/
|
||||
override async totalSupply() {
|
||||
const claimed = await this.totalClaimedSupply();
|
||||
const unclaimed = await this.totalUnclaimedSupply();
|
||||
const [claimed, unclaimed] = await Promise.all([
|
||||
this.totalClaimedSupply(),
|
||||
this.totalUnclaimedSupply(),
|
||||
]);
|
||||
return claimed.add(unclaimed);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import type {
|
||||
IRoyalty,
|
||||
OffersLogic,
|
||||
Ownable,
|
||||
BaseRouter,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { ThirdwebStorage } from "@thirdweb-dev/storage";
|
||||
import { BaseContract, CallOverrides } from "ethers";
|
||||
@@ -38,6 +39,7 @@ import {
|
||||
FEATURE_PLATFORM_FEE,
|
||||
FEATURE_PRIMARY_SALE,
|
||||
FEATURE_ROYALTY,
|
||||
FEATURE_DYNAMIC_CONTRACT,
|
||||
} from "../constants/thirdweb-features";
|
||||
import { Account } from "../core/classes/account";
|
||||
import { AccountFactory } from "../core/classes/account-factory";
|
||||
@@ -76,6 +78,7 @@ import { SDKOptions } from "../schema/sdk-options";
|
||||
import { Address } from "../schema/shared/Address";
|
||||
import { BaseContractInterface } from "../types/contract";
|
||||
import { BaseERC1155, BaseERC20, BaseERC721 } from "../types/eips";
|
||||
import { ExtensionManager } from "../core/classes/extension-manager";
|
||||
|
||||
/**
|
||||
* Custom contract dynamic class with feature detection
|
||||
@@ -116,9 +119,12 @@ export class SmartContract<
|
||||
public encoder: ContractEncoder<TContract>;
|
||||
public estimator: GasCostEstimator<TContract>;
|
||||
public publishedMetadata: ContractPublishedMetadata<TContract>;
|
||||
public abi: Abi;
|
||||
public metadata: ContractMetadata<BaseContract, typeof CustomContractSchema>;
|
||||
|
||||
get abi(): Abi {
|
||||
return AbiSchema.parse(this.contractWrapper.abi || []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle royalties
|
||||
*/
|
||||
@@ -355,6 +361,10 @@ export class SmartContract<
|
||||
return assertEnabled(this.detectAccount(), FEATURE_ACCOUNT);
|
||||
}
|
||||
|
||||
get extensions(): ExtensionManager {
|
||||
return assertEnabled(this.detectBaseRouter(), FEATURE_DYNAMIC_CONTRACT);
|
||||
}
|
||||
|
||||
private _chainId: number;
|
||||
get chainId() {
|
||||
return this._chainId;
|
||||
@@ -378,7 +388,6 @@ export class SmartContract<
|
||||
this._chainId = chainId;
|
||||
this.storage = storage;
|
||||
this.contractWrapper = contractWrapper;
|
||||
this.abi = AbiSchema.parse(abi || []);
|
||||
|
||||
this.events = new ContractEvents(this.contractWrapper);
|
||||
this.encoder = new ContractEncoder(this.contractWrapper);
|
||||
@@ -583,6 +592,18 @@ export class SmartContract<
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private detectBaseRouter() {
|
||||
if (
|
||||
detectContractFeature<BaseRouter>(
|
||||
this.contractWrapper,
|
||||
FEATURE_DYNAMIC_CONTRACT.name,
|
||||
)
|
||||
) {
|
||||
return new ExtensionManager(this.contractWrapper);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private detectAirdrop20() {
|
||||
if (
|
||||
detectContractFeature<AirdropERC20>(this.contractWrapper, "AirdropERC20")
|
||||
|
||||
@@ -409,7 +409,7 @@ export class AccountPermissions implements DetectableFeature {
|
||||
*
|
||||
* @example
|
||||
* ```javascript
|
||||
* const tx = await contract.account.updateAccess(signer, restrictions);
|
||||
* const tx = await contract.account.updatePermissions(signer, permissions);
|
||||
* const receipt = tx.receipt();
|
||||
* ```
|
||||
*
|
||||
|
||||
@@ -99,6 +99,22 @@ export class ContractWrapper<
|
||||
) as TContract;
|
||||
}
|
||||
|
||||
public updateAbi(updatedAbi: ContractInterface): void {
|
||||
// re-connect the contract with the new signer / provider
|
||||
this.writeContract = new Contract(
|
||||
this.address,
|
||||
updatedAbi,
|
||||
this.getSignerOrProvider(),
|
||||
) as TContract;
|
||||
|
||||
// setup the read only contract
|
||||
this.readContract = this.writeContract.connect(
|
||||
this.getProvider(),
|
||||
) as TContract;
|
||||
|
||||
this.abi = AbiSchema.parse(updatedAbi);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,522 @@
|
||||
import type { BaseRouter } from "@thirdweb-dev/contracts-js";
|
||||
import { DetectableFeature } from "../interfaces/DetectableFeature";
|
||||
import { ContractWrapper } from "./contract-wrapper";
|
||||
import { buildTransactionFunction } from "../../common/transactions";
|
||||
import { Transaction } from "./transactions";
|
||||
import {
|
||||
Extension,
|
||||
ExtensionFunction,
|
||||
ExtensionMetadata,
|
||||
FunctionInput,
|
||||
} from "../../types/extension";
|
||||
import { FEATURE_DYNAMIC_CONTRACT } from "../../constants/thirdweb-features";
|
||||
import { ContractInterface } from "ethers";
|
||||
import { generateExtensionFunctions } from "../../common/plugin/generatePluginFunctions";
|
||||
import { Abi, AbiSchema, CommonContractSchema } from "../../schema";
|
||||
import { utils } from "ethers";
|
||||
import invariant from "tiny-invariant";
|
||||
import { ExtensionAddedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/CoreRouter";
|
||||
import {
|
||||
THIRDWEB_DEPLOYER,
|
||||
deployContractDeterministic,
|
||||
deployWithThrowawayDeployer,
|
||||
fetchContractMetadataFromAddress,
|
||||
fetchPublishedContractFromPolygon,
|
||||
getDeploymentInfo,
|
||||
} from "../../common";
|
||||
import { joinABIs } from "../../common/plugin/joinABIs";
|
||||
import { TransactionReceipt } from "@ethersproject/abstract-provider";
|
||||
import { ExtensionRemovedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/CoreRouter";
|
||||
import { ExtensionReplacedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/BaseRouter";
|
||||
import { DynamicContractExtensionMetadataOrUri } from "../../types";
|
||||
|
||||
export class ExtensionManager implements DetectableFeature {
|
||||
featureName = FEATURE_DYNAMIC_CONTRACT.name;
|
||||
|
||||
private contractWrapper: ContractWrapper<BaseRouter>;
|
||||
|
||||
constructor(contractWrapper: ContractWrapper<BaseRouter>) {
|
||||
this.contractWrapper = contractWrapper;
|
||||
}
|
||||
|
||||
getAddress(): string {
|
||||
return this.contractWrapper.readContract.address;
|
||||
}
|
||||
|
||||
/** ******************************
|
||||
* READ FUNCTIONS
|
||||
*******************************/
|
||||
|
||||
public async getAll(): Promise<Extension[]> {
|
||||
const extensions: Extension[] =
|
||||
await this.contractWrapper.readContract.getAllExtensions();
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
public async get(extensionName: string): Promise<Extension> {
|
||||
const extension: Extension =
|
||||
await this.contractWrapper.readContract.getExtension(extensionName);
|
||||
|
||||
return extension;
|
||||
}
|
||||
|
||||
public async getExtensionAddress(extensionName: string): Promise<string> {
|
||||
const extension = await this.get(extensionName);
|
||||
return extension.metadata.implementation;
|
||||
}
|
||||
|
||||
public async getAllFunctions(
|
||||
extensionName: string,
|
||||
): Promise<ExtensionFunction[]> {
|
||||
const extension = await this.get(extensionName);
|
||||
return extension.functions;
|
||||
}
|
||||
|
||||
public async getExtensionForFunction(
|
||||
functionInput: FunctionInput,
|
||||
): Promise<ExtensionMetadata> {
|
||||
let selector = functionInput.functionSelector;
|
||||
if (!selector) {
|
||||
invariant(
|
||||
functionInput.functionSignature,
|
||||
"Atleast one of function selector and signature must be provided",
|
||||
);
|
||||
|
||||
selector = utils.id(functionInput.functionSignature).substring(0, 10);
|
||||
}
|
||||
|
||||
const extensionMetadata: ExtensionMetadata =
|
||||
await this.contractWrapper.readContract.getMetadataForFunction(selector);
|
||||
|
||||
return extensionMetadata;
|
||||
}
|
||||
|
||||
public async getExtensionAddressForFunction(
|
||||
functionInput: FunctionInput,
|
||||
): Promise<string> {
|
||||
const extensionMetadata = await this.getExtensionForFunction(functionInput);
|
||||
return extensionMetadata.implementation;
|
||||
}
|
||||
|
||||
/** ******************************
|
||||
* WRITE FUNCTIONS
|
||||
*******************************/
|
||||
|
||||
/**
|
||||
* Adds an extension to the contract
|
||||
*/
|
||||
add = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (inputArgs: {
|
||||
extension: Extension;
|
||||
extensionAbi?: ContractInterface;
|
||||
}): Promise<Transaction<Promise<TransactionReceipt>>> => {
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "addExtension",
|
||||
args: [inputArgs.extension],
|
||||
|
||||
parse: async (receipt) => {
|
||||
const events = this.contractWrapper.parseLogs<ExtensionAddedEvent>(
|
||||
"ExtensionAdded",
|
||||
receipt.logs,
|
||||
);
|
||||
|
||||
if (events.length < 1) {
|
||||
throw new Error("No ExtensionAdded event found");
|
||||
}
|
||||
|
||||
const extensionAbi = inputArgs.extensionAbi
|
||||
? AbiSchema.parse(inputArgs.extensionAbi)
|
||||
: (
|
||||
await fetchContractMetadataFromAddress(
|
||||
inputArgs.extension.metadata.implementation,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.contractWrapper.storage,
|
||||
)
|
||||
).abi;
|
||||
|
||||
const abiToAdd = this.filterAbiForAdd(
|
||||
extensionAbi,
|
||||
inputArgs.extension,
|
||||
);
|
||||
|
||||
const updatedAbi = joinABIs([
|
||||
AbiSchema.parse(this.contractWrapper.abi),
|
||||
abiToAdd,
|
||||
]);
|
||||
|
||||
this.contractWrapper.updateAbi(updatedAbi);
|
||||
|
||||
return receipt;
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a deployed extension to the contract
|
||||
*/
|
||||
addDeployed = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (inputArgs: {
|
||||
extensionName: string;
|
||||
extensionAddress: string;
|
||||
extensionMetadata?: DynamicContractExtensionMetadataOrUri;
|
||||
extensionAbi?: ContractInterface;
|
||||
}): Promise<Transaction<Promise<TransactionReceipt>>> => {
|
||||
let extensionAbi = inputArgs.extensionAbi;
|
||||
if (!extensionAbi) {
|
||||
const metadata = await fetchContractMetadataFromAddress(
|
||||
inputArgs.extensionAddress,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.contractWrapper.storage,
|
||||
this.contractWrapper.options,
|
||||
);
|
||||
|
||||
extensionAbi = metadata.abi;
|
||||
}
|
||||
|
||||
invariant(extensionAbi, "Require extension ABI");
|
||||
|
||||
let extensionMetadataUri = "";
|
||||
if (inputArgs.extensionMetadata) {
|
||||
if (typeof inputArgs.extensionMetadata === "string") {
|
||||
extensionMetadataUri = inputArgs.extensionMetadata;
|
||||
} else {
|
||||
const parsedMetadata = await CommonContractSchema.parseAsync(
|
||||
inputArgs.extensionMetadata,
|
||||
);
|
||||
extensionMetadataUri = await this.contractWrapper.storage.upload(
|
||||
parsedMetadata,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const extensionFunctions: ExtensionFunction[] =
|
||||
generateExtensionFunctions(AbiSchema.parse(extensionAbi));
|
||||
|
||||
const extension: Extension = {
|
||||
metadata: {
|
||||
name: inputArgs.extensionName,
|
||||
metadataURI: extensionMetadataUri,
|
||||
implementation: inputArgs.extensionAddress,
|
||||
},
|
||||
functions: extensionFunctions,
|
||||
};
|
||||
|
||||
return this.add.prepare({ extension, extensionAbi });
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds a published extension to the contract, and deploys it deterministically if necessary
|
||||
*/
|
||||
addPublished = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (inputArgs: {
|
||||
extensionName: string;
|
||||
publisherAddress?: string;
|
||||
version?: string;
|
||||
extensionMetadataOverride?: DynamicContractExtensionMetadataOrUri;
|
||||
}): Promise<Transaction<Promise<TransactionReceipt>>> => {
|
||||
const version = inputArgs.version || "latest";
|
||||
|
||||
const { deployedExtensionAddress, extensionMetadata } =
|
||||
await this.deployExtension(
|
||||
inputArgs.extensionName,
|
||||
inputArgs.publisherAddress || THIRDWEB_DEPLOYER,
|
||||
version,
|
||||
);
|
||||
|
||||
return this.addDeployed.prepare({
|
||||
extensionName: inputArgs.extensionName,
|
||||
extensionAddress: deployedExtensionAddress,
|
||||
extensionMetadata:
|
||||
inputArgs.extensionMetadataOverride || extensionMetadata,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
replace = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (inputArgs: {
|
||||
extension: Extension;
|
||||
extensionAbi?: ContractInterface;
|
||||
}): Promise<Transaction<Promise<TransactionReceipt>>> => {
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "replaceExtension",
|
||||
args: [inputArgs.extension],
|
||||
|
||||
parse: async (receipt) => {
|
||||
const events = this.contractWrapper.parseLogs<ExtensionReplacedEvent>(
|
||||
"ExtensionReplaced",
|
||||
receipt.logs,
|
||||
);
|
||||
|
||||
if (events.length < 1) {
|
||||
throw new Error("No ExtensionReplaced event found");
|
||||
}
|
||||
|
||||
const extensionAbi = inputArgs.extensionAbi
|
||||
? AbiSchema.parse(inputArgs.extensionAbi)
|
||||
: (
|
||||
await fetchContractMetadataFromAddress(
|
||||
inputArgs.extension.metadata.implementation,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.contractWrapper.storage,
|
||||
)
|
||||
).abi;
|
||||
|
||||
const contractAbi = this.filterAbiForRemove(
|
||||
AbiSchema.parse(this.contractWrapper.abi),
|
||||
extensionAbi,
|
||||
);
|
||||
const abiToAdd = this.filterAbiForAdd(
|
||||
extensionAbi,
|
||||
inputArgs.extension,
|
||||
);
|
||||
const updatedAbi = joinABIs([contractAbi, abiToAdd]);
|
||||
|
||||
this.contractWrapper.updateAbi(updatedAbi);
|
||||
|
||||
return receipt;
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
replaceDeployed = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (inputArgs: {
|
||||
extensionName: string;
|
||||
extensionAddress: string;
|
||||
extensionMetadata?: DynamicContractExtensionMetadataOrUri;
|
||||
extensionAbi?: ContractInterface;
|
||||
}): Promise<Transaction<Promise<TransactionReceipt>>> => {
|
||||
let extensionAbi = inputArgs.extensionAbi;
|
||||
if (!extensionAbi) {
|
||||
const metadata = await fetchContractMetadataFromAddress(
|
||||
inputArgs.extensionAddress,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.contractWrapper.storage,
|
||||
this.contractWrapper.options,
|
||||
);
|
||||
|
||||
extensionAbi = metadata.abi;
|
||||
}
|
||||
|
||||
invariant(extensionAbi, "Require extension ABI");
|
||||
|
||||
let extensionMetadataUri = "";
|
||||
if (inputArgs.extensionMetadata) {
|
||||
if (typeof inputArgs.extensionMetadata === "string") {
|
||||
extensionMetadataUri = inputArgs.extensionMetadata;
|
||||
} else {
|
||||
const parsedMetadata = await CommonContractSchema.parseAsync(
|
||||
inputArgs.extensionMetadata,
|
||||
);
|
||||
extensionMetadataUri = await this.contractWrapper.storage.upload(
|
||||
parsedMetadata,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const extensionFunctions: ExtensionFunction[] =
|
||||
generateExtensionFunctions(AbiSchema.parse(extensionAbi));
|
||||
|
||||
const extension: Extension = {
|
||||
metadata: {
|
||||
name: inputArgs.extensionName,
|
||||
metadataURI: extensionMetadataUri,
|
||||
implementation: inputArgs.extensionAddress,
|
||||
},
|
||||
functions: extensionFunctions,
|
||||
};
|
||||
|
||||
return this.replace.prepare({ extension, extensionAbi });
|
||||
},
|
||||
);
|
||||
|
||||
replacePublished = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (inputArgs: {
|
||||
extensionName: string;
|
||||
publisherAddress?: string;
|
||||
version?: string;
|
||||
extensionMetadataOverride?: DynamicContractExtensionMetadataOrUri;
|
||||
}): Promise<Transaction<Promise<TransactionReceipt>>> => {
|
||||
const version = inputArgs.version || "latest";
|
||||
|
||||
const { deployedExtensionAddress, extensionMetadata } =
|
||||
await this.deployExtension(
|
||||
inputArgs.extensionName,
|
||||
inputArgs.publisherAddress || THIRDWEB_DEPLOYER,
|
||||
version,
|
||||
);
|
||||
|
||||
return this.replaceDeployed.prepare({
|
||||
extensionName: inputArgs.extensionName,
|
||||
extensionAddress: deployedExtensionAddress,
|
||||
extensionMetadata:
|
||||
inputArgs.extensionMetadataOverride || extensionMetadata,
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
remove = /* @__PURE__ */ buildTransactionFunction(
|
||||
async (inputArgs: {
|
||||
extensionName: string;
|
||||
}): Promise<Transaction<Promise<TransactionReceipt>>> => {
|
||||
const extensionAddress = await this.getExtensionAddress(
|
||||
inputArgs.extensionName,
|
||||
);
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this.contractWrapper,
|
||||
method: "removeExtension",
|
||||
args: [inputArgs.extensionName],
|
||||
|
||||
parse: async (receipt) => {
|
||||
const events = this.contractWrapper.parseLogs<ExtensionRemovedEvent>(
|
||||
"ExtensionRemoved",
|
||||
receipt.logs,
|
||||
);
|
||||
|
||||
if (events.length < 1) {
|
||||
throw new Error("No ExtensionRemoved event found");
|
||||
}
|
||||
|
||||
const extensionAbi = (
|
||||
await fetchContractMetadataFromAddress(
|
||||
extensionAddress,
|
||||
this.contractWrapper.getProvider(),
|
||||
this.contractWrapper.storage,
|
||||
)
|
||||
).abi;
|
||||
|
||||
const updatedAbi = this.filterAbiForRemove(
|
||||
AbiSchema.parse(this.contractWrapper.abi),
|
||||
extensionAbi,
|
||||
);
|
||||
|
||||
this.contractWrapper.updateAbi(updatedAbi);
|
||||
|
||||
return receipt;
|
||||
},
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
/** ******************************
|
||||
* Internal / private
|
||||
*******************************/
|
||||
|
||||
private filterAbiForAdd(extensionAbi: Abi, extension: Extension) {
|
||||
const extensionAbiInterface = new utils.Interface(extensionAbi);
|
||||
const extensionFunctionSelectors = extension.functions.map(
|
||||
(fn) => fn.functionSelector,
|
||||
);
|
||||
const filtered = extensionAbi.filter((item) => {
|
||||
const fnFragment = Object.values(new utils.Interface([item]).functions);
|
||||
if (fnFragment.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const fnSigHash = extensionAbiInterface.getSighash(fnFragment[0]);
|
||||
|
||||
return extensionFunctionSelectors.includes(fnSigHash);
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
private filterAbiForRemove(fullAbi: Abi, abiToRemove: Abi) {
|
||||
const fullAbiInterface = new utils.Interface(fullAbi);
|
||||
const interfaceToRemove = new utils.Interface(abiToRemove);
|
||||
const functionsToRemove = Object.values(interfaceToRemove.functions).map(
|
||||
(fn) => interfaceToRemove.getSighash(fn),
|
||||
);
|
||||
|
||||
const filtered = fullAbi.filter((item) => {
|
||||
const fnFragment = Object.values(new utils.Interface([item]).functions);
|
||||
if (fnFragment.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const fnSigHash = fullAbiInterface.getSighash(fnFragment[0]);
|
||||
|
||||
return !functionsToRemove.includes(fnSigHash);
|
||||
});
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
private async deployExtension(
|
||||
extensionName: string,
|
||||
publisherAddress: string,
|
||||
version: string = "latest",
|
||||
): Promise<{ deployedExtensionAddress: string; extensionMetadata: string }> {
|
||||
const published = await fetchPublishedContractFromPolygon(
|
||||
publisherAddress,
|
||||
extensionName,
|
||||
version,
|
||||
this.contractWrapper.storage,
|
||||
this.contractWrapper.options.clientId,
|
||||
this.contractWrapper.options.secretKey,
|
||||
);
|
||||
|
||||
const deploymentInfo = await getDeploymentInfo(
|
||||
published.metadataUri,
|
||||
this.contractWrapper.storage,
|
||||
this.contractWrapper.getProvider(),
|
||||
"",
|
||||
this.contractWrapper.options.clientId,
|
||||
this.contractWrapper.options.secretKey,
|
||||
);
|
||||
|
||||
const implementationAddress = deploymentInfo.find(
|
||||
(i) => i.type === "implementation",
|
||||
)?.transaction.predictedAddress as string;
|
||||
|
||||
// deploy infra + plugins + implementation using a throwaway Deployer contract
|
||||
|
||||
// filter out already deployed contracts (data is empty)
|
||||
const transactionsToSend = deploymentInfo.filter(
|
||||
(i) => i.transaction.data && i.transaction.data.length > 0,
|
||||
);
|
||||
const transactionsforDirectDeploy = transactionsToSend
|
||||
.filter((i) => {
|
||||
return i.type !== "infra";
|
||||
})
|
||||
.map((i) => i.transaction);
|
||||
const transactionsForThrowawayDeployer = transactionsToSend
|
||||
.filter((i) => {
|
||||
return i.type === "infra";
|
||||
})
|
||||
.map((i) => i.transaction);
|
||||
|
||||
const signer = this.contractWrapper.getSigner();
|
||||
invariant(signer, "Signer is required");
|
||||
|
||||
// deploy via throwaway deployer, multiple infra contracts in one transaction
|
||||
await deployWithThrowawayDeployer(
|
||||
signer,
|
||||
transactionsForThrowawayDeployer,
|
||||
{},
|
||||
);
|
||||
|
||||
// send each transaction directly to Create2 factory
|
||||
// process txns one at a time
|
||||
for (const tx of transactionsforDirectDeploy) {
|
||||
try {
|
||||
await deployContractDeterministic(signer, tx);
|
||||
} catch (e) {
|
||||
console.debug(
|
||||
`Error deploying contract at ${tx.predictedAddress}`,
|
||||
(e as any)?.message,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
deployedExtensionAddress: implementationAddress,
|
||||
extensionMetadata: published.metadataUri,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -48,15 +48,11 @@ export class ContractRegistry extends ContractWrapper<TWRegistry> {
|
||||
): Promise<Transaction<TransactionResult>> => {
|
||||
const deployerAddress = await this.getSignerAddress();
|
||||
const contractEncoder = new ContractEncoder(this);
|
||||
const encoded: string[] = await Promise.all(
|
||||
contractAddresses.map(async (address) =>
|
||||
contractEncoder.encode("add", [
|
||||
deployerAddress,
|
||||
await resolveAddress(address),
|
||||
]),
|
||||
),
|
||||
const encoded = (
|
||||
await Promise.all(contractAddresses.map((addr) => resolveAddress(addr)))
|
||||
).map((address) =>
|
||||
contractEncoder.encode("add", [deployerAddress, address]),
|
||||
);
|
||||
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this,
|
||||
method: "multicall",
|
||||
@@ -79,15 +75,11 @@ export class ContractRegistry extends ContractWrapper<TWRegistry> {
|
||||
): Promise<Transaction<TransactionResult>> => {
|
||||
const deployerAddress = await this.getSignerAddress();
|
||||
const contractEncoder = new ContractEncoder(this);
|
||||
const encoded: string[] = await Promise.all(
|
||||
contractAddresses.map(async (address) =>
|
||||
contractEncoder.encode("remove", [
|
||||
deployerAddress,
|
||||
await resolveAddress(address),
|
||||
]),
|
||||
),
|
||||
const encoded = (
|
||||
await Promise.all(contractAddresses.map((addr) => resolveAddress(addr)))
|
||||
).map((address) =>
|
||||
contractEncoder.encode("remove", [deployerAddress, address]),
|
||||
);
|
||||
|
||||
return Transaction.fromContractWrapper({
|
||||
contractWrapper: this,
|
||||
method: "multicall",
|
||||
|
||||
@@ -1103,7 +1103,7 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
parsedMetadata.seller_fee_basis_points,
|
||||
];
|
||||
|
||||
return await this.deployReleasedContract.prepare(
|
||||
return await this.deployPublishedContract.prepare(
|
||||
THIRDWEB_DEPLOYER,
|
||||
"OpenEditionERC721",
|
||||
deployArgs,
|
||||
|
||||
@@ -98,15 +98,17 @@ export class UserWallet {
|
||||
amount: Amount,
|
||||
currencyAddress: AddressOrEns = NATIVE_TOKEN_ADDRESS,
|
||||
): Promise<TransactionResult> {
|
||||
const resolvedTo = await resolveAddress(to);
|
||||
const resolvedCurrency = await resolveAddress(currencyAddress);
|
||||
const [resolvedTo, resolvedCurrency, amountInWei] = await Promise.all([
|
||||
resolveAddress(to),
|
||||
resolveAddress(currencyAddress),
|
||||
normalizePriceValue(
|
||||
this.connection.getProvider(),
|
||||
amount,
|
||||
currencyAddress,
|
||||
),
|
||||
]);
|
||||
|
||||
const signer = this.requireWallet();
|
||||
const amountInWei = await normalizePriceValue(
|
||||
this.connection.getProvider(),
|
||||
amount,
|
||||
currencyAddress,
|
||||
);
|
||||
if (isNativeToken(resolvedCurrency)) {
|
||||
// native token transfer
|
||||
const from = await signer.getAddress();
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { BytesLikeSchema } from "../../../../core/schema/shared";
|
||||
import { AddressSchema } from "../../shared/AddressSchema";
|
||||
import { z } from "zod";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const ExtensionMetadataInput = /* @__PURE__ */ (() =>
|
||||
z.object({
|
||||
name: z.string(),
|
||||
metadataURI: z.string(),
|
||||
implementation: AddressSchema,
|
||||
}))();
|
||||
|
||||
/**
|
||||
* @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),
|
||||
}))();
|
||||
@@ -10,14 +10,16 @@ import { z } from "zod";
|
||||
* @internal
|
||||
*/
|
||||
export const CommonContractSchema = /* @__PURE__ */ (() =>
|
||||
z.object({
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
image: FileOrBufferOrStringSchema.optional(),
|
||||
external_link: z.string().optional(),
|
||||
app_uri: z.string().optional(),
|
||||
social_urls: z.record(z.string()).optional(),
|
||||
}))();
|
||||
z
|
||||
.object({
|
||||
name: z.string(),
|
||||
description: z.string().optional(),
|
||||
image: FileOrBufferOrStringSchema.optional(),
|
||||
external_link: z.string().optional(),
|
||||
app_uri: z.string().optional(),
|
||||
social_urls: z.record(z.string()).optional(),
|
||||
})
|
||||
.catchall(z.unknown()))();
|
||||
|
||||
export type CommonContractSchemaInput = z.input<typeof CommonContractSchema>;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { AddressOrEns } from "../../schema/shared/AddressOrEnsSchema";
|
||||
import { FileOrBufferOrString } from "@thirdweb-dev/storage";
|
||||
import type { BigNumberish, Bytes } from "ethers";
|
||||
import { CommonContractSchemaInput } from "../../schema";
|
||||
|
||||
/**
|
||||
* Options for deploying an NFT contract
|
||||
@@ -407,3 +408,10 @@ export interface AirdropContractDeployMetadata {
|
||||
*/
|
||||
app_uri?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type DynamicContractExtensionMetadataOrUri =
|
||||
| CommonContractSchemaInput
|
||||
| string;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import {
|
||||
ExtensionFunctionInput,
|
||||
ExtensionInput,
|
||||
ExtensionMetadataInput,
|
||||
} from "../schema/contracts/common/extension";
|
||||
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>;
|
||||
|
||||
export type FunctionInput = {
|
||||
functionSelector?: string;
|
||||
functionSignature?: string;
|
||||
};
|
||||
@@ -268,7 +268,7 @@ describe("Custom Contracts", async () => {
|
||||
try {
|
||||
c.roles.get("admin");
|
||||
} catch (e) {
|
||||
expectError(e, "contract does not implement the 'permissions' Extension");
|
||||
expectError(e, "contract does not implement the 'Permissions' Extension");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
import { assert } from "chai";
|
||||
import { AbiSchema, SmartContract, isExtensionEnabled } from "../../src/evm";
|
||||
import { jsonProvider, sdk, signers } from "./before-setup";
|
||||
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
|
||||
import {
|
||||
CoreRouter__factory,
|
||||
OffersLogic__factory,
|
||||
} from "@thirdweb-dev/contracts-js";
|
||||
import { deployContractAndUploadMetadata } from "./utils";
|
||||
import { Extension, ExtensionFunction } from "../../src/evm/types/extension";
|
||||
import { generateExtensionFunctions } from "../../src/evm/common/plugin/generatePluginFunctions";
|
||||
import invariant from "tiny-invariant";
|
||||
|
||||
describe("Base Router for Dynamic Contracts", async () => {
|
||||
let adminWallet: SignerWithAddress;
|
||||
let coreRouterAddress: string;
|
||||
let coreRouter: SmartContract;
|
||||
|
||||
beforeEach(async () => {
|
||||
await jsonProvider.send("hardhat_reset", []);
|
||||
[adminWallet] = signers;
|
||||
|
||||
// deploy base router
|
||||
coreRouterAddress = await deployContractAndUploadMetadata(
|
||||
CoreRouter__factory.abi,
|
||||
CoreRouter__factory.bytecode,
|
||||
adminWallet,
|
||||
[adminWallet.address, []],
|
||||
);
|
||||
|
||||
// instantiate custom contract
|
||||
coreRouter = await sdk.getContract(coreRouterAddress);
|
||||
});
|
||||
|
||||
it("should detect base router in a dynamic contract", async () => {
|
||||
const contract = await sdk.getContract(coreRouterAddress);
|
||||
assert(isExtensionEnabled(contract.abi, "DynamicContract"));
|
||||
});
|
||||
|
||||
it("should add extensions", async () => {
|
||||
// deploy an extension contract
|
||||
// Offers
|
||||
const offersLogicAddress = await deployContractAndUploadMetadata(
|
||||
OffersLogic__factory.abi,
|
||||
OffersLogic__factory.bytecode,
|
||||
adminWallet,
|
||||
[],
|
||||
);
|
||||
|
||||
// generate functions array for the extension
|
||||
const extensionFunctions: ExtensionFunction[] = generateExtensionFunctions(
|
||||
AbiSchema.parse(OffersLogic__factory.abi),
|
||||
);
|
||||
|
||||
const routerInput: Extension = {
|
||||
metadata: {
|
||||
name: "OffersLogic",
|
||||
metadataURI: "",
|
||||
implementation: offersLogicAddress,
|
||||
},
|
||||
functions: extensionFunctions,
|
||||
};
|
||||
|
||||
// add extension
|
||||
await coreRouter.extensions.add({ extension: routerInput });
|
||||
|
||||
// read extension
|
||||
const extensions = await coreRouter.extensions.getAll();
|
||||
assert(extensions.length === 1);
|
||||
assert(extensions[0].metadata.name === "OffersLogic");
|
||||
|
||||
// call function on extension
|
||||
const totalOffers = await coreRouter.offers.getTotalCount();
|
||||
assert(totalOffers.eq(0));
|
||||
});
|
||||
|
||||
it("should add extensions with abi", async () => {
|
||||
// deploy an extension contract
|
||||
// Offers
|
||||
const offersLogicAddress = await deployContractAndUploadMetadata(
|
||||
OffersLogic__factory.abi,
|
||||
OffersLogic__factory.bytecode,
|
||||
adminWallet,
|
||||
[],
|
||||
);
|
||||
|
||||
// add extension
|
||||
await coreRouter.extensions.addDeployed({
|
||||
extensionName: "OffersLogic",
|
||||
extensionAddress: offersLogicAddress,
|
||||
extensionAbi: OffersLogic__factory.abi,
|
||||
});
|
||||
|
||||
// read extension
|
||||
const extensions = await coreRouter.extensions.getAll();
|
||||
assert(extensions.length === 1);
|
||||
assert(extensions[0].metadata.name === "OffersLogic");
|
||||
});
|
||||
|
||||
it("should replace extensions", async () => {
|
||||
// deploy an extension contract
|
||||
// Offers
|
||||
const offersLogicAddress = await deployContractAndUploadMetadata(
|
||||
OffersLogic__factory.abi,
|
||||
OffersLogic__factory.bytecode,
|
||||
adminWallet,
|
||||
[],
|
||||
);
|
||||
|
||||
// add extension
|
||||
await coreRouter.extensions.addDeployed({
|
||||
extensionName: "OffersLogic",
|
||||
extensionAddress: offersLogicAddress,
|
||||
extensionAbi: OffersLogic__factory.abi,
|
||||
});
|
||||
|
||||
// read extension
|
||||
let extensions = await coreRouter.extensions.getAll();
|
||||
assert(extensions.length === 1);
|
||||
assert(extensions[0].metadata.name === "OffersLogic");
|
||||
assert(isExtensionEnabled(coreRouter.abi, "Offers"));
|
||||
|
||||
// replace extension
|
||||
const newAddress = await deployContractAndUploadMetadata(
|
||||
OffersLogic__factory.abi,
|
||||
OffersLogic__factory.bytecode,
|
||||
adminWallet,
|
||||
[],
|
||||
);
|
||||
const newFunctions: ExtensionFunction[] = generateExtensionFunctions(
|
||||
AbiSchema.parse(OffersLogic__factory.abi),
|
||||
);
|
||||
|
||||
invariant(newFunctions, "");
|
||||
const routerInput: Extension = {
|
||||
metadata: {
|
||||
name: "OffersLogic",
|
||||
metadataURI: "",
|
||||
implementation: newAddress,
|
||||
},
|
||||
functions: newFunctions,
|
||||
};
|
||||
await coreRouter.extensions.replace({ extension: routerInput });
|
||||
|
||||
// read extension
|
||||
extensions = await coreRouter.extensions.getAll();
|
||||
assert(extensions.length === 1);
|
||||
assert(extensions[0].metadata.name === "OffersLogic");
|
||||
assert(extensions[0].metadata.implementation === newAddress);
|
||||
|
||||
// call function on extension
|
||||
const totalOffers = await coreRouter.offers.getTotalCount();
|
||||
assert(totalOffers.eq(0));
|
||||
});
|
||||
|
||||
it("should update extensions with abi", async () => {
|
||||
// deploy an extension contract
|
||||
// Offers
|
||||
const offersLogicAddress = await deployContractAndUploadMetadata(
|
||||
OffersLogic__factory.abi,
|
||||
OffersLogic__factory.bytecode,
|
||||
adminWallet,
|
||||
[],
|
||||
);
|
||||
|
||||
// add extension
|
||||
await coreRouter.extensions.addDeployed({
|
||||
extensionName: "OffersLogic",
|
||||
extensionAddress: offersLogicAddress,
|
||||
extensionAbi: OffersLogic__factory.abi,
|
||||
});
|
||||
|
||||
// read extension
|
||||
let extensions = await coreRouter.extensions.getAll();
|
||||
const oldURI = extensions[0].metadata.metadataURI;
|
||||
assert(extensions.length === 1);
|
||||
assert(extensions[0].metadata.name === "OffersLogic");
|
||||
assert(isExtensionEnabled(coreRouter.abi, "Offers"));
|
||||
|
||||
// update extension
|
||||
const newAddress = await deployContractAndUploadMetadata(
|
||||
OffersLogic__factory.abi,
|
||||
OffersLogic__factory.bytecode,
|
||||
adminWallet,
|
||||
[],
|
||||
);
|
||||
await coreRouter.extensions.replaceDeployed({
|
||||
extensionName: "OffersLogic",
|
||||
extensionAddress: newAddress,
|
||||
extensionMetadata: { name: "OffersLogicV2" },
|
||||
});
|
||||
|
||||
// read extension
|
||||
extensions = await coreRouter.extensions.getAll();
|
||||
assert(extensions.length === 1);
|
||||
assert(extensions[0].metadata.name === "OffersLogic");
|
||||
assert(extensions[0].metadata.implementation === newAddress);
|
||||
assert(extensions[0].metadata.metadataURI !== oldURI);
|
||||
assert(isExtensionEnabled(coreRouter.abi, "Offers"));
|
||||
});
|
||||
|
||||
it("should remove extensions", async () => {
|
||||
// deploy an extension contract
|
||||
// Offers
|
||||
const offersLogicAddress = await deployContractAndUploadMetadata(
|
||||
OffersLogic__factory.abi,
|
||||
OffersLogic__factory.bytecode,
|
||||
adminWallet,
|
||||
[],
|
||||
);
|
||||
|
||||
const extensionFunctions: ExtensionFunction[] = generateExtensionFunctions(
|
||||
AbiSchema.parse(OffersLogic__factory.abi),
|
||||
);
|
||||
|
||||
const routerInput: Extension = {
|
||||
metadata: {
|
||||
name: "OffersLogic",
|
||||
metadataURI: "",
|
||||
implementation: offersLogicAddress,
|
||||
},
|
||||
functions: extensionFunctions,
|
||||
};
|
||||
|
||||
// add extension
|
||||
await coreRouter.extensions.add({ extension: routerInput });
|
||||
|
||||
// read extension
|
||||
let extensions = await coreRouter.extensions.getAll();
|
||||
assert(extensions.length === 1);
|
||||
assert(extensions[0].metadata.name === "OffersLogic");
|
||||
assert(isExtensionEnabled(coreRouter.abi, "Offers"));
|
||||
|
||||
// remove extension
|
||||
await coreRouter.extensions.remove({
|
||||
extensionName: routerInput.metadata.name,
|
||||
});
|
||||
|
||||
// read extension
|
||||
extensions = await coreRouter.extensions.getAll();
|
||||
assert(extensions.length === 0);
|
||||
|
||||
// call function on extension
|
||||
assert(!isExtensionEnabled(coreRouter.abi, "Offers"));
|
||||
});
|
||||
|
||||
it("should get extension for function", async () => {
|
||||
// deploy an extension contract
|
||||
// Offers
|
||||
const offersLogicAddress = await deployContractAndUploadMetadata(
|
||||
OffersLogic__factory.abi,
|
||||
OffersLogic__factory.bytecode,
|
||||
adminWallet,
|
||||
[],
|
||||
);
|
||||
|
||||
// generate functions array for the extension
|
||||
const extensionFunctions: ExtensionFunction[] = generateExtensionFunctions(
|
||||
AbiSchema.parse(OffersLogic__factory.abi),
|
||||
);
|
||||
|
||||
const routerInput: Extension = {
|
||||
metadata: {
|
||||
name: "OffersLogic",
|
||||
metadataURI: "",
|
||||
implementation: offersLogicAddress,
|
||||
},
|
||||
functions: extensionFunctions,
|
||||
};
|
||||
|
||||
// add extension
|
||||
await coreRouter.extensions.add({ extension: routerInput });
|
||||
|
||||
// read extension
|
||||
const extension = await coreRouter.extensions.getExtensionForFunction({
|
||||
functionSignature: "acceptOffer(uint256)",
|
||||
});
|
||||
assert(extension.implementation === offersLogicAddress);
|
||||
assert(extension.name === "OffersLogic");
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,11 @@
|
||||
# @thirdweb-dev/service-utils
|
||||
|
||||
## 0.4.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1729](https://github.com/thirdweb-dev/js/pull/1729) [`52b1a0fc`](https://github.com/thirdweb-dev/js/commit/52b1a0fcacb136e8da417d883e5365f2ab558d59) Thanks [@arcoraven](https://github.com/arcoraven)! - Make accountId optional in publishUsageEvents. This will be hydrated internally.
|
||||
|
||||
## 0.4.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/service-utils",
|
||||
"version": "0.4.5",
|
||||
"version": "0.4.6",
|
||||
"main": "dist/thirdweb-dev-service-utils.cjs.js",
|
||||
"module": "dist/thirdweb-dev-service-utils.esm.js",
|
||||
"exports": {
|
||||
|
||||
@@ -21,11 +21,16 @@ const usageEventSchema = z.object({
|
||||
"bundler",
|
||||
"paymaster",
|
||||
"relayer",
|
||||
"connectWallet",
|
||||
]),
|
||||
action: z.string(),
|
||||
accountId: z.string(),
|
||||
|
||||
// Optional
|
||||
/**
|
||||
* The following fields are optional.
|
||||
*/
|
||||
|
||||
accountId: z.string().optional(),
|
||||
isClientEvent: z.boolean().optional(),
|
||||
apiKeyId: z.string().optional(),
|
||||
creatorWalletAddress: z.string().optional(),
|
||||
clientId: z.string().optional(),
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# @thirdweb-dev/unity-js-bridge
|
||||
|
||||
## 0.2.85
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1725](https://github.com/thirdweb-dev/js/pull/1725) [`dc38a243`](https://github.com/thirdweb-dev/js/commit/dc38a243c201d0070d75dd36b1a4f8d1eecbc438) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - [Unity] Fix google popup blocker
|
||||
|
||||
- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]:
|
||||
- @thirdweb-dev/sdk@3.10.67
|
||||
- @thirdweb-dev/wallets@1.3.5
|
||||
- @thirdweb-dev/auth@3.2.48
|
||||
|
||||
## 0.2.84
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101), [`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c)]:
|
||||
- @thirdweb-dev/wallets@1.3.4
|
||||
- @thirdweb-dev/sdk@3.10.66
|
||||
- @thirdweb-dev/auth@3.2.47
|
||||
|
||||
## 0.2.83
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/unity-js-bridge",
|
||||
"version": "0.2.83",
|
||||
"version": "0.2.85",
|
||||
"main": "dist/thirdweb-unity-bridge.js",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -300,10 +300,17 @@ class ThirdwebBridge implements TWBridge {
|
||||
const embeddedWallet = walletInstance as EmbeddedWallet;
|
||||
|
||||
if (useGoogle?.toLowerCase() === "true") {
|
||||
console.log("Using Google OAuth");
|
||||
const googleWindow = this.openGoogleSignInWindow();
|
||||
if (!googleWindow) {
|
||||
throw new Error("Failed to open google login window");
|
||||
}
|
||||
await embeddedWallet.connect({
|
||||
chainId: chainIdNumber,
|
||||
loginType: "headless_google_oauth",
|
||||
openedWindow: googleWindow,
|
||||
closeOpenedWindow: (openedWindow) => {
|
||||
openedWindow.close();
|
||||
},
|
||||
});
|
||||
} else {
|
||||
if (!email) {
|
||||
@@ -634,6 +641,72 @@ class ThirdwebBridge implements TWBridge {
|
||||
});
|
||||
return localWallet;
|
||||
}
|
||||
|
||||
public openGoogleSignInWindow() {
|
||||
const win = window.open("", undefined, "width=350, height=500");
|
||||
if (win) {
|
||||
win.document.title = "Sign In - Google Accounts";
|
||||
win.document.body.innerHTML = `
|
||||
<svg class="loader" viewBox="0 0 50 50">
|
||||
<circle
|
||||
cx="25"
|
||||
cy="25"
|
||||
r="20"
|
||||
fill="none"
|
||||
stroke="#000"
|
||||
stroke-width="4"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loader {
|
||||
width: 15vw;
|
||||
height: 15vw;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
.loader circle {
|
||||
animation: loading 1.5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
stroke-dasharray: 1, 150;
|
||||
stroke-dashoffset: 0;
|
||||
}
|
||||
50% {
|
||||
stroke-dasharray: 90, 150;
|
||||
stroke-dashoffset: -35;
|
||||
}
|
||||
100% {
|
||||
stroke-dasharray: 90, 150;
|
||||
stroke-dashoffset: -124;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
}
|
||||
return win;
|
||||
}
|
||||
}
|
||||
|
||||
// add the bridge to the window object type
|
||||
|
||||
@@ -1,5 +1,34 @@
|
||||
# @thirdweb-dev/wallets
|
||||
|
||||
## 1.3.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`ec36b13a`](https://github.com/thirdweb-dev/js/commit/ec36b13a30e0071548df0b7a6eb5299e2e65e4f9), [`6abb8459`](https://github.com/thirdweb-dev/js/commit/6abb8459712e387b6d8b2edf7eb16fb906c05dae), [`a6c36724`](https://github.com/thirdweb-dev/js/commit/a6c36724eb930ee0abbce876bb7847c859c6fb48)]:
|
||||
- @thirdweb-dev/sdk@3.10.67
|
||||
|
||||
## 1.3.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#1712](https://github.com/thirdweb-dev/js/pull/1712) [`9bd01de5`](https://github.com/thirdweb-dev/js/commit/9bd01de5f9c388e758fba9af7899dc4a9c5a0101) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - New SmartWallet API for session keys
|
||||
|
||||
You can now add admins and scoped session keys to smart wallets directly with a simple API:
|
||||
|
||||
```
|
||||
const smartWallet = new SmartWallet(config);
|
||||
await smartWallet.connect({ personalWallet })
|
||||
|
||||
await smartWallet.createSessionKey(keyAddress, permissions);
|
||||
await smartWallet.revokeSessionKey(keyAddress);
|
||||
|
||||
await smartWallet.addAdmin(adminAddress);
|
||||
await smartWallet.removeAdmin(adminAddress);
|
||||
```
|
||||
|
||||
- Updated dependencies [[`f35fbec1`](https://github.com/thirdweb-dev/js/commit/f35fbec1be14332d06e73b5f44f66975ef311d6c)]:
|
||||
- @thirdweb-dev/sdk@3.10.66
|
||||
|
||||
## 1.3.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/wallets",
|
||||
"version": "1.3.3",
|
||||
"version": "1.3.5",
|
||||
"main": "dist/thirdweb-dev-wallets.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets.esm.js",
|
||||
"types": "dist/thirdweb-dev-wallets.cjs.d.ts",
|
||||
|
||||
@@ -17,6 +17,8 @@ import { BigNumber, ethers, providers } from "ethers";
|
||||
import {
|
||||
ChainOrRpcUrl,
|
||||
getChainProvider,
|
||||
SignerPermissionsInput,
|
||||
SignerWithPermissions,
|
||||
SmartContract,
|
||||
ThirdwebSDK,
|
||||
Transaction,
|
||||
@@ -224,10 +226,13 @@ export class SmartWalletConnector extends Connector<SmartWalletConnectionArgs> {
|
||||
throw new Error("Smart wallet already deployed");
|
||||
}
|
||||
const signer = await this.getSigner();
|
||||
const tx = await signer.sendTransaction({
|
||||
to: await signer.getAddress(),
|
||||
data: "0x",
|
||||
});
|
||||
const tx = await signer.sendTransaction(
|
||||
{
|
||||
to: await signer.getAddress(),
|
||||
data: "0x",
|
||||
},
|
||||
true, // batched tx flag to avoid hitting the Router fallback method
|
||||
);
|
||||
const receipt = await tx.wait();
|
||||
return { receipt };
|
||||
}
|
||||
@@ -243,6 +248,65 @@ export class SmartWalletConnector extends Connector<SmartWalletConnectionArgs> {
|
||||
return await this.accountApi.isAcountDeployed();
|
||||
}
|
||||
|
||||
async deployIfNeeded(): Promise<void> {
|
||||
const isDeployed = await this.isDeployed();
|
||||
if (!isDeployed) {
|
||||
await this.deploy();
|
||||
}
|
||||
}
|
||||
|
||||
async grantPermissions(
|
||||
target: string,
|
||||
permissions: SignerPermissionsInput,
|
||||
): Promise<TransactionResult> {
|
||||
await this.deployIfNeeded();
|
||||
const accountContract = await this.getAccountContract();
|
||||
return accountContract.account.grantPermissions(target, permissions);
|
||||
}
|
||||
|
||||
async revokePermissions(target: string): Promise<TransactionResult> {
|
||||
await this.deployIfNeeded();
|
||||
const accountContract = await this.getAccountContract();
|
||||
return accountContract.account.revokeAccess(target);
|
||||
}
|
||||
|
||||
async addAdmin(target: string): Promise<TransactionResult> {
|
||||
await this.deployIfNeeded();
|
||||
const accountContract = await this.getAccountContract();
|
||||
return accountContract.account.grantAdminPermissions(target);
|
||||
}
|
||||
|
||||
async removeAdmin(target: string): Promise<TransactionResult> {
|
||||
await this.deployIfNeeded();
|
||||
const accountContract = await this.getAccountContract();
|
||||
return accountContract.account.revokeAdminPermissions(target);
|
||||
}
|
||||
|
||||
async getAllActiveSigners(): Promise<SignerWithPermissions[]> {
|
||||
const isDeployed = await this.isDeployed();
|
||||
if (isDeployed) {
|
||||
const accountContract = await this.getAccountContract();
|
||||
return accountContract.account.getAllAdminsAndSigners();
|
||||
} else {
|
||||
const personalWallet = await this.personalWallet?.getSigner();
|
||||
if (!personalWallet) {
|
||||
throw new Error("Personal wallet not connected");
|
||||
}
|
||||
return [
|
||||
{
|
||||
isAdmin: true,
|
||||
signer: await personalWallet.getAddress(),
|
||||
permissions: {
|
||||
startDate: new Date(0),
|
||||
expirationDate: new Date(0),
|
||||
nativeTokenLimitPerTransaction: BigNumber.from(0),
|
||||
approvedCallTargets: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying account contract of the smart wallet.
|
||||
* @returns the account contract of the smart wallet.
|
||||
|
||||
@@ -2,7 +2,7 @@ import { LOCAL_NODE_PKEY, SmartContract, ThirdwebSDK } from "@thirdweb-dev/sdk";
|
||||
import { BigNumberish, BigNumber, ethers, utils } from "ethers";
|
||||
import { AccountApiParams } from "../types";
|
||||
import { BaseAccountAPI } from "./base-api";
|
||||
import { MINIMAL_ACCOUNT_ABI } from "./constants";
|
||||
import { ACCOUNT_CORE_ABI } from "./constants";
|
||||
|
||||
export class AccountAPI extends BaseAccountAPI {
|
||||
sdk: ThirdwebSDK;
|
||||
@@ -42,7 +42,7 @@ export class AccountAPI extends BaseAccountAPI {
|
||||
} else {
|
||||
this.accountContract = await this.sdk.getContract(
|
||||
await this.getAccountAddress(),
|
||||
MINIMAL_ACCOUNT_ABI,
|
||||
ACCOUNT_CORE_ABI,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,4 +15,4 @@ export const walletIds = {
|
||||
walletConnect: "walletConnect",
|
||||
phantom: "phantom",
|
||||
// add new ids sorted alphabetically
|
||||
};
|
||||
} as const;
|
||||
|
||||
@@ -12,7 +12,7 @@ export class BloctoWallet extends AbstractClientWallet<BloctoOptions> {
|
||||
connector?: Connector;
|
||||
name: string = "Blocto";
|
||||
|
||||
static id = walletIds.blocto;
|
||||
static id = walletIds.blocto as string;
|
||||
static meta: WalletMeta = {
|
||||
name: "Blocto",
|
||||
iconURL:
|
||||
|
||||
@@ -31,7 +31,7 @@ export class CoinbaseWallet extends AbstractClientWallet {
|
||||
},
|
||||
};
|
||||
|
||||
static id = walletIds.coinbase;
|
||||
static id= walletIds.coinbase as string;
|
||||
public get walletName() {
|
||||
return "Coinbase Wallet" as const;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ export class EmbeddedWallet extends AbstractClientWallet<
|
||||
> {
|
||||
connector?: Connector;
|
||||
|
||||
static id = walletIds.embeddedWallet;
|
||||
static id = walletIds.embeddedWallet as string;
|
||||
|
||||
static meta = {
|
||||
name: "Embedded Wallet",
|
||||
|
||||
@@ -5,7 +5,7 @@ import { AbstractClientWallet, WalletOptions } from "./base";
|
||||
export class FrameWallet extends AbstractClientWallet {
|
||||
connector?: Connector;
|
||||
|
||||
static id = walletIds.frame;
|
||||
static id = walletIds.frame as string;
|
||||
public get walletName() {
|
||||
return "Frame Wallet";
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AbstractClientWallet, WalletOptions } from "./base";
|
||||
export class InjectedWallet extends AbstractClientWallet {
|
||||
connector?: Connector;
|
||||
|
||||
static id = "injected" as const;
|
||||
static id = "injected" as string;
|
||||
public get walletName() {
|
||||
return "Injected Wallet";
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export class LocalWallet extends AbstractClientWallet<
|
||||
ethersWallet?: Wallet;
|
||||
#storage: AsyncStorage;
|
||||
|
||||
static id = walletIds.localWallet;
|
||||
static id = walletIds.localWallet as string;
|
||||
|
||||
static meta = {
|
||||
name: "Local Wallet",
|
||||
|
||||
@@ -30,7 +30,7 @@ export class MagicLink extends AbstractClientWallet<
|
||||
name: "Magic Link",
|
||||
};
|
||||
|
||||
static id = walletIds.magicLink;
|
||||
static id = walletIds.magicLink as string;
|
||||
|
||||
public get walletName() {
|
||||
return "Magic Link" as const;
|
||||
|
||||
@@ -55,7 +55,7 @@ export class MetaMaskWallet extends AbstractClientWallet<MetamaskAdditionalOptio
|
||||
},
|
||||
};
|
||||
|
||||
static id = walletIds.metamask;
|
||||
static id = walletIds.metamask as string;
|
||||
|
||||
public get walletName() {
|
||||
return "MetaMask" as const;
|
||||
|
||||
@@ -17,7 +17,7 @@ export class PaperWallet extends AbstractClientWallet<
|
||||
> {
|
||||
connector?: Connector;
|
||||
|
||||
static id = walletIds.paper;
|
||||
static id = walletIds.paper as string;
|
||||
|
||||
static meta = {
|
||||
name: "Paper Wallet",
|
||||
|
||||
@@ -26,7 +26,7 @@ export class PhantomWallet extends AbstractClientWallet {
|
||||
},
|
||||
};
|
||||
|
||||
static id = walletIds.phantom;
|
||||
static id = walletIds.phantom as string;
|
||||
|
||||
public get walletName() {
|
||||
return "Phantom" as const;
|
||||
|
||||
@@ -55,7 +55,7 @@ export class RainbowWallet extends AbstractClientWallet<RainbowAdditionalOptions
|
||||
},
|
||||
};
|
||||
|
||||
static id = walletIds.rainbow;
|
||||
static id = walletIds.rainbow as string;
|
||||
|
||||
public get walletName() {
|
||||
return "Rainbow Wallet" as const;
|
||||
|
||||
@@ -23,7 +23,7 @@ export class SafeWallet extends AbstractClientWallet<
|
||||
"ipfs://QmbbyxDDmmLQh8DzzeUR6X6B75bESsNUFmbdvS3ZsQ2pN1/SafeToken.svg",
|
||||
};
|
||||
|
||||
static id = walletIds.safe;
|
||||
static id = walletIds.safe as string;
|
||||
public get walletName() {
|
||||
return "Safe Wallet" as const;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
Transaction,
|
||||
TransactionResult,
|
||||
SmartContract,
|
||||
SignerPermissionsInput,
|
||||
SignerWithPermissions,
|
||||
} from "@thirdweb-dev/sdk";
|
||||
import { walletIds } from "../constants/walletIds";
|
||||
import {
|
||||
@@ -41,7 +43,7 @@ export class SmartWallet
|
||||
"ipfs://QmeAJVqn17aDNQhjEU3kcWVZCFBrfta8LzaDGkS8Egdiyk/smart-wallet.svg",
|
||||
};
|
||||
|
||||
static id = walletIds.smartWallet;
|
||||
static id = walletIds.smartWallet as string;
|
||||
public get walletName() {
|
||||
return "Smart Wallet" as const;
|
||||
}
|
||||
@@ -123,6 +125,16 @@ export class SmartWallet
|
||||
return connector.deploy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually deploy the smart wallet contract. If already deployed this will do nothing.
|
||||
* Note that this is not necessary as the smart wallet will be deployed automatically on the first transaction the user makes.
|
||||
* @returns the transaction receipt
|
||||
*/
|
||||
async deployIfNeeded(): Promise<void> {
|
||||
const connector = await this.getConnector();
|
||||
return connector.deployIfNeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the smart wallet contract is deployed
|
||||
* @returns true if the smart wallet contract is deployed
|
||||
@@ -132,6 +144,54 @@ export class SmartWallet
|
||||
return connector.isDeployed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and add a session key to the smart wallet.
|
||||
* @param keyAddress the address of the session key to add.
|
||||
* @param permissions the permissions to grant to the session key.
|
||||
*/
|
||||
async createSessionKey(
|
||||
keyAddress: string,
|
||||
permissions: SignerPermissionsInput,
|
||||
): Promise<TransactionResult> {
|
||||
const connector = await this.getConnector();
|
||||
return connector.grantPermissions(keyAddress, permissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a session key from the smart wallet.
|
||||
* @param keyAddress the address of the session key to remove.
|
||||
*/
|
||||
async revokeSessionKey(keyAddress: string): Promise<TransactionResult> {
|
||||
const connector = await this.getConnector();
|
||||
return connector.revokePermissions(keyAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another admin to the smart wallet.
|
||||
* @param adminAddress the address of the admin to add.
|
||||
*/
|
||||
async addAdmin(adminAddress: string): Promise<TransactionResult> {
|
||||
const connector = await this.getConnector();
|
||||
return connector.addAdmin(adminAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an admin from the smart wallet.
|
||||
* @param adminAddress the address of the admin to remove.
|
||||
*/
|
||||
async removeAdmin(adminAddress: string): Promise<TransactionResult> {
|
||||
const connector = await this.getConnector();
|
||||
return connector.removeAdmin(adminAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the admins and session keys active on the smart wallet.
|
||||
*/
|
||||
async getAllActiveSigners(): Promise<SignerWithPermissions[]> {
|
||||
const connector = await this.getConnector();
|
||||
return connector.getAllActiveSigners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying account contract of the smart wallet.
|
||||
* @returns the account contract of the smart wallet.
|
||||
|
||||
@@ -56,7 +56,7 @@ export class TrustWallet extends AbstractClientWallet<TrustAdditionalOptions> {
|
||||
},
|
||||
};
|
||||
|
||||
static id = walletIds.trust;
|
||||
static id = walletIds.trust as string;
|
||||
|
||||
public get walletName() {
|
||||
return "Trust Wallet" as const;
|
||||
|
||||
@@ -44,7 +44,7 @@ export class WalletConnect extends AbstractClientWallet<WalletConnectOptions> {
|
||||
|
||||
connector?: Connector;
|
||||
|
||||
static id = walletIds.walletConnect;
|
||||
static id = walletIds.walletConnect as string;
|
||||
|
||||
static meta = {
|
||||
name: "WalletConnect",
|
||||
|
||||
@@ -40,7 +40,7 @@ export class ZerionWallet extends AbstractClientWallet<ZerionAdditionalOptions>
|
||||
walletConnectConnector?: WalletConnectConnectorType;
|
||||
isInjected: boolean;
|
||||
|
||||
static id = "zerion" as const;
|
||||
static id = "zerion" as string;
|
||||
static meta = {
|
||||
name: "Zerion Wallet",
|
||||
iconURL: "ipfs://Qmb1LhNtMUkzbgk1V8ZiUSRXjMJGRkS5HH3R71KyRgjdBG/zerion.png",
|
||||
|
||||
Generated
+10776
-21533
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user