Compare commits

...
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
4e5eb13b82 Version Packages (#1266)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-29 19:11:03 -07:00
2b11353909 [SDK] remove ethers/lib/utils dependency (#1277)
Co-authored-by: Jonas Daniels <[email protected]>
2023-06-30 01:10:47 +00:00
Manan TankandGitHub 5882091eab [chains] Fix RPC override issue (#1276) 2023-06-29 22:27:59 +00:00
Mariano FuentesandGitHub 8fc5f204b0 Fix bugs with cli deploy (#1274) 2023-06-29 18:11:25 +00:00
nkrishangandGitHub c953571588 mass rename smart-wallet -> account (#1271) 2023-06-29 12:26:48 +00:00
Manan TankandGitHub edcd22d612 [react-core] Add autoConnectTimeout (#1273) 2023-06-29 12:19:43 +00:00
iketwandGitHub 0d5503aae9 [RN] Only handle link if coinbase in supportedWallets (#1270) 2023-06-28 21:56:12 +00:00
Manan TankandGitHub 998e521733 [wallets, react] expose advancedOptions in paper (#1269) 2023-06-28 21:08:34 +00:00
FirekeeperandGitHub be606dd3c9 [paper] set signer null on disconnect (#1267) 2023-06-28 17:26:35 +00:00
d794b8f9f4 update: Add zerion to useInstalledWallets (#1210)
Co-authored-by: Jonas Daniels <[email protected]>
2023-06-28 16:50:36 +00:00
YashandGitHub 21b29490d4 [SDK] Tests for new publish+deploy flow (#1244) 2023-06-28 16:23:36 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
7b1d62b323 Version Packages (#1262)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-28 08:07:57 -07:00
Manan TankandGitHub e78da6b36c [wallets] Wallet connect v2 corrections (#1265) 2023-06-28 13:34:42 +00:00
ed711c8cb2 [Wallets] Use min number of chains if user passes more than 50 chains (#1263)
Co-authored-by: Manan Tank <[email protected]>
2023-06-28 12:04:28 +00:00
05a7495a13 [Wallets] handle passing RPC urls to SmartWallet config (#1255)
Co-authored-by: Jonas Daniels <[email protected]>
2023-06-28 07:20:04 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
11bb053246 Version Packages (#1261)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-27 22:16:09 -07:00
efb3546ec8 [Wallets] Fix. Respect dev passed chains. (#1259)
Co-authored-by: Manan Tank <[email protected]>
2023-06-28 05:07:24 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
0a5d2b5bdd Version Packages (#1247)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-27 17:36:21 -07:00
FirekeeperandGitHub ac3e019cd1 [unity-js-bridge] wcv2 and paper support (#1256) 2023-06-28 00:28:16 +00:00
3c5dc4804a Migration to WalletConnectV2 (#1215)
Co-authored-by: Manan Tank <[email protected]>
2023-06-28 00:04:07 +00:00
Joaquim VergesandGitHub 30ac3aef84 [SDK] Handle base64 encoded NFTs (#1254) 2023-06-27 23:31:52 +00:00
YashandGitHub ef4cb09219 [SDK] Any EVM deployments: update EIP155 error substrings (#1249) 2023-06-27 22:55:16 +00:00
ad7dae3b16 [React] Stop using useActiveChain deprecated hook (#1253)
Co-authored-by: Nacho Iacovino <[email protected]>
2023-06-27 21:50:28 +00:00
0fd8aa04a9 fix(chains): Celo Alfajores Explorer URL (#1243)
Co-authored-by: Joaquim Verges <[email protected]>
Co-authored-by: Jonas Daniels <[email protected]>
2023-06-27 19:37:23 +00:00
620e89dc25 [React] Export smart wallet hooks (#1248)
Co-authored-by: Jonas Daniels <[email protected]>
2023-06-27 19:01:45 +00:00
Nacho IacovinoandGitHub 86d33c4b3e [React] Fix marketplace v3 cache keys (#1251) 2023-06-27 18:06:30 +00:00
Adam MajmudarandGitHub d43f6171d9 Allow setting login payload duration (#1250) 2023-06-27 18:04:33 +00:00
06ca1c407d [SDK] Add tests for SmartWallet and SmartWalletFactory; add smartWallet.setAccess and smartWallet.isAdmin (#1227)
Co-authored-by: Nacho Iacovino <[email protected]>
Co-authored-by: Nacho Iacovino <[email protected]>
2023-06-27 01:57:33 +00:00
132 changed files with 40223 additions and 2770 deletions
+30
View File
@@ -1,5 +1,35 @@
# @thirdweb-dev/auth
## 3.2.7
### Patch Changes
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`be606dd3`](https://github.com/thirdweb-dev/js/commit/be606dd3c93c1514834c1d970e864d7f949a07ab), [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae)]:
- @thirdweb-dev/wallets@1.0.3
## 3.2.6
### Patch Changes
- Updated dependencies [[`ed711c8c`](https://github.com/thirdweb-dev/js/commit/ed711c8cb2ed6a0deb1b2a5eeec06df1d4edc5e8), [`05a7495a`](https://github.com/thirdweb-dev/js/commit/05a7495a132aabab5f48abbdb80a468ff6f65df8), [`e78da6b3`](https://github.com/thirdweb-dev/js/commit/e78da6b36cfa6d954234820563201bf760186ed1)]:
- @thirdweb-dev/wallets@1.0.2
## 3.2.5
### Patch Changes
- Updated dependencies [[`efb3546e`](https://github.com/thirdweb-dev/js/commit/efb3546ec8268156be00301af28b9d83ecd5ab08)]:
- @thirdweb-dev/wallets@1.0.1
## 3.2.4
### Patch Changes
- [#1250](https://github.com/thirdweb-dev/js/pull/1250) [`d43f6171`](https://github.com/thirdweb-dev/js/commit/d43f6171d910e31b748b171b012174390aac3fff) Thanks [@adam-maj](https://github.com/adam-maj)! - Allow setting login payload validity duration
- Updated dependencies [[`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e), [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc)]:
- @thirdweb-dev/wallets@1.0.0
## 3.2.3
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "3.2.3",
"version": "3.2.7",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
@@ -25,6 +25,11 @@ export default async function handler(
version: ctx.authOptions?.version,
chainId: parsedPayload.data.chainId || ctx.authOptions?.chainId,
resources: ctx.authOptions?.resources,
expirationTime: ctx.authOptions?.loginPayloadDurationInSeconds
? new Date(
Date.now() + 1000 * ctx.authOptions.loginPayloadDurationInSeconds,
)
: undefined,
});
return res.status(200).json({ payload });
+1
View File
@@ -44,6 +44,7 @@ export type ThirdwebAuthConfig<
validateTokenId?:
| ((tokenId: string) => void)
| ((tokenId: string) => Promise<void>);
loginPayloadDurationInSeconds?: number;
tokenDurationInSeconds?: number;
refreshIntervalInSeconds?: number;
};
+5
View File
@@ -25,6 +25,11 @@ export default async function handler(
version: ctx.authOptions?.version,
chainId: parsedPayload.data.chainId || ctx.authOptions?.chainId,
resources: ctx.authOptions?.resources,
expirationTime: ctx.authOptions?.loginPayloadDurationInSeconds
? new Date(
Date.now() + 1000 * ctx.authOptions.loginPayloadDurationInSeconds,
)
: undefined,
});
return res.status(200).json({ payload });
+1
View File
@@ -55,6 +55,7 @@ export type ThirdwebAuthConfig<
validateTokenId?:
| ((tokenId: string) => void)
| ((tokenId: string) => Promise<void>);
loginPayloadDurationInSeconds?: number;
tokenDurationInSeconds?: number;
refreshIntervalInSeconds?: number;
};
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/chains
## 0.1.26
### Patch Changes
- [#1276](https://github.com/thirdweb-dev/js/pull/1276) [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad) Thanks [@MananTank](https://github.com/MananTank)! - Do not change order or RPCs in `getChainRPCs` to Fix RPC override issue
## 0.1.25
### Patch Changes
- [#1248](https://github.com/thirdweb-dev/js/pull/1248) [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - update chains
- [#1243](https://github.com/thirdweb-dev/js/pull/1243) [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19) Thanks [@therealharpaljadeja](https://github.com/therealharpaljadeja)! - update chains
## 0.1.24
### Patch Changes
+1 -1
View File
@@ -10,9 +10,9 @@ export default {
},
"rpc": [
"https://ethereum.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://mainnet.infura.io/v3/${INFURA_API_KEY}",
"wss://mainnet.infura.io/ws/v3/${INFURA_API_KEY}",
"https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com"
+2 -2
View File
@@ -1,11 +1,11 @@
import type { Chain } from "../src/types";
export default {
"name": "Optimism",
"name": "OP Mainnet",
"chain": "ETH",
"rpc": [
"https://optimism.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://opt-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://optimism-mainnet.infura.io/v3/${INFURA_API_KEY}",
"https://opt-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://mainnet.optimism.io/"
],
"faucets": [],
+1 -1
View File
@@ -10,8 +10,8 @@ export default {
},
"rpc": [
"https://polygon.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://polygon-mainnet.infura.io/v3/${INFURA_API_KEY}",
"https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://polygon-rpc.com/",
"https://rpc-mainnet.matic.network",
"https://matic-mainnet.chainstacklabs.com",
+37
View File
@@ -0,0 +1,37 @@
import type { Chain } from "../src/types";
export default {
"name": "Pego Network",
"chain": "PEGO",
"rpc": [
"https://pego-network.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://pegorpc.com",
"https://node1.pegorpc.com",
"https://node2.pegorpc.com",
"https://node3.pegorpc.com"
],
"faucets": [],
"nativeCurrency": {
"name": "Pego Native Token",
"symbol": "PG",
"decimals": 18
},
"infoURL": "https://pego.network",
"shortName": "pg",
"chainId": 20201022,
"networkId": 20201022,
"icon": {
"url": "ipfs://QmVf1afskRHuZjFSLCZH8397KrVNAoYgyAePX9VMBrPVtx",
"width": 246,
"height": 247,
"format": "png"
},
"explorers": [
{
"name": "Pego Network Explorer",
"url": "https://scan.pego.network",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "pego-network"
} as const satisfies Chain;
+1 -1
View File
@@ -4,8 +4,8 @@ export default {
"chain": "ETH",
"rpc": [
"https://optimism-goerli.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://opt-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://optimism-goerli.infura.io/v3/${INFURA_API_KEY}",
"https://opt-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://goerli.optimism.io/"
],
"faucets": [
+1 -1
View File
@@ -13,8 +13,8 @@ export default {
},
"rpc": [
"https://arbitrum-goerli.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://arb-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://arbitrum-goerli.infura.io/v3/${INFURA_API_KEY}",
"https://arb-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://goerli-rollup.arbitrum.io/rpc/"
],
"faucets": [],
+9 -4
View File
@@ -12,15 +12,20 @@ export default {
},
"rpc": [
"https://celo-alfajores-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://alfajores-forno.celo-testnet.org",
"wss://alfajores-forno.celo-testnet.org/ws"
"wss://alfajores-forno.celo-testnet.org/ws",
"https://alfajores-forno.celo-testnet.org"
],
"faucets": [
"https://celo.org/developers/faucet",
"https://cauldron.pretoriaresearchlab.io/alfajores-faucet"
"https://cauldron.pretoriaresearchlab.io/alfajores-faucet",
"https://celo.org/developers/faucet"
],
"infoURL": "https://docs.celo.org/",
"explorers": [
{
"name": "Celoscan",
"url": "https://alfajores.celoscan.io/",
"standard": "EIP3091"
},
{
"name": "Celoscan",
"url": "https://celoscan.io",
+1 -1
View File
@@ -5,9 +5,9 @@ export default {
"chain": "ETH",
"rpc": [
"https://goerli.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://goerli.infura.io/v3/${INFURA_API_KEY}",
"wss://goerli.infura.io/v3/${INFURA_API_KEY}",
"https://eth-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://rpc.goerli.mudit.blog/",
"https://ethereum-goerli.publicnode.com"
],
+2 -1
View File
@@ -5,7 +5,8 @@ export default {
"rpc": [
"https://rollux.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.rollux.com",
"wss://rpc.rollux.com/wss"
"wss://rpc.rollux.com/wss",
"https://rpc.ankr.com/rollux/${ANKR_API_KEY}"
],
"faucets": [
"https://rollux.id/faucetapp"
+3 -3
View File
@@ -5,10 +5,10 @@ export default {
"chain": "ETH",
"rpc": [
"https://linea-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.goerli.linea.build",
"wss://rpc.goerli.linea.build",
"https://linea-goerli.infura.io/v3/${INFURA_API_KEY}",
"wss://linea-goerli.infura.io/v3/${INFURA_API_KEY}"
"wss://linea-goerli.infura.io/v3/${INFURA_API_KEY}",
"https://rpc.goerli.linea.build",
"wss://rpc.goerli.linea.build"
],
"faucets": [
"https://faucetlink.to/goerli"
+1 -1
View File
@@ -4,7 +4,7 @@ export default {
"chain": "ETC",
"rpc": [
"https://ethereum-classic.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://www.ethercluster.com/etc"
"https://etc.rivet.link"
],
"faucets": [
"https://free-online-app.com/faucet-for-eth-evm-chains/?"
+1 -1
View File
@@ -4,7 +4,7 @@ export default {
"chain": "ETC",
"rpc": [
"https://ethereum-classic-testnet-mordor.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://www.ethercluster.com/mordor"
"https://rpc.mordor.etccooperative.org"
],
"faucets": [],
"nativeCurrency": {
+1 -1
View File
@@ -11,8 +11,8 @@ export default {
},
"rpc": [
"https://mumbai.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://polygon-mumbai.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://polygon-mumbai.infura.io/v3/${INFURA_API_KEY}",
"https://polygon-mumbai.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://matic-mumbai.chainstacklabs.com",
"https://rpc-mumbai.maticvigil.com",
"https://matic-testnet-archive-rpc.bwarelabs.com",
+13
View File
@@ -0,0 +1,13 @@
// @ts-check
/** @typedef { import("../../src/types").Chain } Chain */
/** @type {Partial<Chain>} */
export default {
explorers: [
{
name: "Celoscan",
url: "https://alfajores.celoscan.io/",
standard: "EIP3091",
},
],
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/chains",
"version": "0.1.24",
"version": "0.1.26",
"main": "dist/thirdweb-dev-chains.cjs.js",
"module": "dist/thirdweb-dev-chains.esm.js",
"browser": {
@@ -80,6 +80,33 @@ chains = chains
};
});
/**
* Sort RPCs in chain
* @param {Chain} chain
*/
function sortRPCs(chain) {
const thirdwebRPCs = [];
const alchemyRPCs = [];
const infuraRPCs = [];
const otherRPCs = [];
chain.rpc.forEach((rpc) => {
if (rpc.includes("${THIRDWEB_API_KEY}")) {
thirdwebRPCs.push(rpc);
} else if (rpc.includes("${ALCHEMY_API_KEY}")) {
alchemyRPCs.push(rpc);
} else if (rpc.includes("${INFURA_API_KEY}")) {
infuraRPCs.push(rpc);
} else {
otherRPCs.push(rpc);
}
});
chain.rpc = [...thirdwebRPCs, ...infuraRPCs, ...alchemyRPCs, ...otherRPCs];
}
chains.forEach(sortRPCs);
const imports = [];
const exports = [];
const exportNames = [];
@@ -146,6 +173,10 @@ function findSlug(chain) {
if (slug === "base-goerli-testnet") {
slug = "base-goerli";
}
// optimisim rename handling
if (slug === "op") {
slug = "optimism";
}
// end special cases
takenSlugs[slug] = true;
File diff suppressed because one or more lines are too long
+30 -44
View File
@@ -28,59 +28,45 @@ export function getChainRPCs(
...options,
};
const modeChains = chain.rpc.filter((rpc) => {
if (rpc.startsWith("http") && mode === "http") {
return true;
}
if (rpc.startsWith("ws") && mode === "ws") {
return true;
const processedRPCs: string[] = [];
chain.rpc.forEach((rpc) => {
// exclude RPC if mode mismatch
if (mode === "http" && !rpc.startsWith("http")) {
return;
}
return false;
if (mode === "ws" && !rpc.startsWith("ws")) {
return;
}
// Replace API_KEY placeholder with value
if (thirdwebApiKey && rpc.includes("${THIRDWEB_API_KEY}")) {
processedRPCs.push(rpc.replace("${THIRDWEB_API_KEY}", thirdwebApiKey));
} else if (infuraApiKey && rpc.includes("${INFURA_API_KEY}")) {
processedRPCs.push(rpc.replace("${INFURA_API_KEY}", infuraApiKey));
} else if (alchemyApiKey && rpc.includes("${ALCHEMY_API_KEY}")) {
processedRPCs.push(rpc.replace("${ALCHEMY_API_KEY}", alchemyApiKey));
}
// exclude RPCs with unknown placeholder
else if (rpc.includes("${")) {
return;
}
// add as is
else {
processedRPCs.push(rpc);
}
});
const thirdwebRPC = modeChains
.filter((rpc) => {
return rpc.includes("${THIRDWEB_API_KEY}") && thirdwebApiKey;
})
.map((rpc) =>
thirdwebApiKey ? rpc.replace("${THIRDWEB_API_KEY}", thirdwebApiKey) : rpc,
);
const alchemyRPC = modeChains
.filter((rpc) => {
return rpc.includes("${ALCHEMY_API_KEY}") && alchemyApiKey;
})
.map((rpc) =>
alchemyApiKey ? rpc.replace("${ALCHEMY_API_KEY}", alchemyApiKey) : rpc,
);
const infuraRPC = modeChains
.filter((rpc) => {
return rpc.includes("${INFURA_API_KEY}") && infuraApiKey;
})
.map((rpc) =>
infuraApiKey ? rpc.replace("${INFURA_API_KEY}", infuraApiKey) : rpc,
);
const allOtherRpcs = modeChains.filter((rpc) => {
return !rpc.includes("${");
});
const orderedRPCs = [
...thirdwebRPC,
...infuraRPC,
...alchemyRPC,
...allOtherRpcs,
];
if (orderedRPCs.length === 0) {
if (processedRPCs.length === 0) {
throw new Error(
`No RPC available for chainId "${chain.chainId}" with mode ${mode}`,
);
}
return orderedRPCs;
return processedRPCs;
}
/**
+18
View File
@@ -1,5 +1,23 @@
# thirdweb
## 0.10.28
### Patch Changes
- [#1274](https://github.com/thirdweb-dev/js/pull/1274) [`8fc5f204`](https://github.com/thirdweb-dev/js/commit/8fc5f204b00733089c02beee92c86b0f1fcac271) Thanks [@Marfuen](https://github.com/Marfuen)! - Added all detections for deploy command and some minor bug fixes
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8), [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad)]:
- @thirdweb-dev/sdk@3.10.27
- @thirdweb-dev/chains@0.1.26
## 0.10.27
### Patch Changes
- Updated dependencies [[`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d), [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/sdk@3.10.26
- @thirdweb-dev/chains@0.1.25
## 0.10.26
### Patch Changes
+1 -1
View File
@@ -2,7 +2,7 @@ import { CREATE_MESSAGES } from "../constants/constants";
import { prepareEnvironment } from "@gmrchk/cli-testing-library";
// this creates an app, can take some time that's fine
jest.setTimeout(120_000);
jest.setTimeout(240_000);
describe("npx thirdweb detect", () => {
it("should detect ERC721Base extensions", async () => {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.10.26",
"version": "0.10.28",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -1,27 +1,28 @@
import inquirer from "inquirer";
import { info } from "../helpers/logger";
import { FrameworkType, PackageManagerType } from "../types/ProjectType";
import inquirer from "inquirer";
import NextDetector from "./frameworks/next";
import CRADetector from "./frameworks/cra";
import RemixDetector from "./frameworks/remix";
import GatsbyDetector from "./frameworks/gatsby";
import VueDetector from "./frameworks/vue";
import ReactNativeCLIDetector from "./frameworks/reactNativeCli";
import DjangoDetector from "./frameworks/django";
import ExpoDetector from "./frameworks/expo";
import FoundryDetector from "./frameworks/foundry";
import HardhatDetector from "./frameworks/hardhat";
import { FrameworkDetector } from "./detector";
import TruffleDetector from "./frameworks/truffle";
import BrownieDetector from "./frameworks/brownie";
import FastAPIDetector from "./frameworks/fastAPI";
import FlaskDetector from "./frameworks/flask";
import PopulusDetector from "./frameworks/populus";
import FastifyDetector from "./frameworks/fastify";
import CRADetector from "./frameworks/cra";
import DjangoDetector from "./frameworks/django";
import EchoDetector from "./frameworks/echo";
import ExpoDetector from "./frameworks/expo";
import FastAPIDetector from "./frameworks/fastAPI";
import FastifyDetector from "./frameworks/fastify";
import FiberDetector from "./frameworks/fiber";
import FlaskDetector from "./frameworks/flask";
import FoundryDetector from "./frameworks/foundry";
import GatsbyDetector from "./frameworks/gatsby";
import GinDetector from "./frameworks/gin";
import HardhatDetector from "./frameworks/hardhat";
import NextDetector from "./frameworks/next";
import PopulusDetector from "./frameworks/populus";
import ReactNativeCLIDetector from "./frameworks/reactNativeCli";
import RemixDetector from "./frameworks/remix";
import RevelDetector from "./frameworks/revel";
import TruffleDetector from "./frameworks/truffle";
import ViteDetector from "./frameworks/vite";
import VueDetector from "./frameworks/vue";
import ZenjectDetector from "./frameworks/zenject";
export default async function detect(
@@ -50,6 +51,7 @@ export default async function detect(
new RevelDetector(),
new TruffleDetector(),
new VueDetector(),
new ViteDetector(),
new ZenjectDetector(),
];
@@ -1,13 +1,12 @@
import inquirer from "inquirer";
import { info } from "../helpers/logger";
import { LibraryType, PackageManagerType } from "../types/ProjectType";
import { LibraryDetector } from "./detector";
import inquirer from "inquirer";
import ReactDetector from "./libraries/react";
import ExpressDetector from "./libraries/express";
import ReactNativeDetector from "./libraries/reactNative";
import ViteDetector from "./libraries/vite";
import Web3PyDetector from "./libraries/web3py";
import PlayMakerDetector from "./libraries/playmaker";
import ReactDetector from "./libraries/react";
import ReactNativeDetector from "./libraries/reactNative";
import Web3PyDetector from "./libraries/web3py";
export default async function detect(
path: string,
@@ -19,7 +18,6 @@ export default async function detect(
new ExpressDetector(),
new ReactDetector(),
new ReactNativeDetector(),
new ViteDetector(),
new Web3PyDetector(),
new PlayMakerDetector(),
];
@@ -1,10 +1,10 @@
import { LibraryDetector } from "../detector";
import { existsSync, readFileSync } from "fs";
import { LibraryType } from "../../types/ProjectType";
import { parsePackageJson } from "../../../lib/utils";
import { FrameworkType } from "../../types/ProjectType";
import { FrameworkDetector } from "../detector";
export default class ViteDetector implements LibraryDetector {
public libraryType: LibraryType = "vite";
export default class ViteDetector implements FrameworkDetector {
public frameworkType: FrameworkType = "vite";
public matches(path: string): boolean {
const packageJsonPath = path + "/package.json";
@@ -16,7 +16,7 @@ export default class ViteDetector implements LibraryDetector {
const packageJson = readFileSync(packageJsonPath);
const { dependencies, devDependencies } = parsePackageJson(packageJson);
const viteDependencyExists =
dependencies["vite"] || devDependencies["vite"] || false;
!!dependencies["vite"] || !!devDependencies["vite"] || false;
return viteDependencyExists;
}
@@ -56,6 +56,7 @@ export type FrameworkType =
| "revel"
| "fiber"
| "zenject"
| "vite"
| "none";
export type LibraryType =
| "react"
+46 -19
View File
@@ -1,30 +1,57 @@
import NPMDetector from "../core/detection/npm";
import ViteDetector from "../core/detection/vite";
import YarnDetector from "../core/detection/yarn";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import detectFramework from "../core/detection/detectFramework";
import detectPackageManager from "../core/detection/detectPackageManager";
import { logger } from "../core/helpers/logger";
import { runCommand } from "../create/helpers/run-command";
import { upload } from "../storage/command";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
export async function deployApp(distPath = "dist", projectPath = ".") {
const storage = new ThirdwebStorage();
const supportedFrameworks = [new ViteDetector()];
const detectedPackageManager = await detectPackageManager(projectPath, {});
const detectedFramework = await detectFramework(projectPath, {}, detectedPackageManager);
const possibleProjects = supportedFrameworks
.filter((detector) => detector.matches(projectPath))
.map((detector) => detector.projectType);
const hasYarn = new YarnDetector().matches(".");
const hasNPM = new NPMDetector().matches(".");
if (possibleProjects.length === 0) {
if (detectedFramework === "none") {
throw new Error("No supported project detected");
}
switch (detectedFramework) {
case "next":
distPath = "out";
break;
case "cra":
distPath = "build";
break;
default:
break;
}
logger.info(`Detected project type: ${detectedFramework}`);
logger.info(`Detected package manager: ${detectedPackageManager}`);
logger.info(`distPath: ${distPath}`);
logger.info(`projectPath: ${projectPath}`);
try {
if (hasYarn) {
await runCommand("yarn", ["build"]);
} else if (hasNPM) {
await runCommand("npm", ["build"]);
switch (detectedPackageManager) {
case "yarn":
await runCommand("yarn", ["build"], true);
if (detectedFramework === "next") {
await runCommand("yarn", ["export"], true);
}
break;
case "npm":
await runCommand("npm", ["run", "build"], true);
if (detectedFramework === "next") {
await runCommand("npm", ["run", "export"], true);
}
break;
case "pnpm":
await runCommand("pnpm", ["build"], true);
if (detectedFramework === "next") {
await runCommand("pnpm", ["export"], true);
}
break;
default:
throw new Error("No supported package manager detected");
}
} catch (err) {
console.error("Can't build project");
@@ -33,9 +60,9 @@ export async function deployApp(distPath = "dist", projectPath = ".") {
try {
const uri = await upload(storage, distPath);
return `https://thirdweb.com/app/deploy/${uri.replace("ipfs://", "")}`;
return `${uri.replace("ipfs://", "https://ipfs-public.thirdwebcdn.com/ipfs/")}`;
} catch (err) {
console.error("Can't upload project");
console.error("Can't upload project", err);
return Promise.reject("Can't upload project");
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { spinner } from "../core/helpers/logger";
import { BufferOrStringWithName, ThirdwebStorage } from "@thirdweb-dev/storage";
import fs from "fs";
import path from "path";
import { spinner } from "../core/helpers/logger";
function recurseFiles(
uploadPath: string,
+42
View File
@@ -1,5 +1,47 @@
# @thirdweb-dev/react-core
## 3.14.7
### Patch Changes
- [#1271](https://github.com/thirdweb-dev/js/pull/1271) [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8) Thanks [@nkrishang](https://github.com/nkrishang)! - Rename smart wallets to account
- [#1273](https://github.com/thirdweb-dev/js/pull/1273) [`edcd22d6`](https://github.com/thirdweb-dev/js/commit/edcd22d61236edb2832f9b2f9796e891d58cb145) Thanks [@MananTank](https://github.com/MananTank)! - Add `autoConnectTimeout` to `ThirdwebProvider` to timeout wallet autoconnect
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8), [`be606dd3`](https://github.com/thirdweb-dev/js/commit/be606dd3c93c1514834c1d970e864d7f949a07ab), [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad), [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae)]:
- @thirdweb-dev/wallets@1.0.3
- @thirdweb-dev/sdk@3.10.27
- @thirdweb-dev/chains@0.1.26
## 3.14.6
### Patch Changes
- Updated dependencies [[`ed711c8c`](https://github.com/thirdweb-dev/js/commit/ed711c8cb2ed6a0deb1b2a5eeec06df1d4edc5e8), [`05a7495a`](https://github.com/thirdweb-dev/js/commit/05a7495a132aabab5f48abbdb80a468ff6f65df8), [`e78da6b3`](https://github.com/thirdweb-dev/js/commit/e78da6b36cfa6d954234820563201bf760186ed1)]:
- @thirdweb-dev/wallets@1.0.2
## 3.14.5
### Patch Changes
- Updated dependencies [[`efb3546e`](https://github.com/thirdweb-dev/js/commit/efb3546ec8268156be00301af28b9d83ecd5ab08)]:
- @thirdweb-dev/wallets@1.0.1
## 3.14.4
### Patch Changes
- [#1227](https://github.com/thirdweb-dev/js/pull/1227) [`06ca1c40`](https://github.com/thirdweb-dev/js/commit/06ca1c407dfee9d3d1fdaed1b6223ad0f9b8857b) Thanks [@nkrishang](https://github.com/nkrishang)! - Add react hooks for smart wallet
- [#1248](https://github.com/thirdweb-dev/js/pull/1248) [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087) Thanks [@nachoiacovino](https://github.com/nachoiacovino)! - Export account hooks
- [#1253](https://github.com/thirdweb-dev/js/pull/1253) [`ad7dae3b`](https://github.com/thirdweb-dev/js/commit/ad7dae3b163e61c7a6eb57f654885a7fdaa4cbb6) Thanks [@rattrayd11](https://github.com/rattrayd11)! - Stop using useActiveChain deprecated hook
- Updated dependencies [[`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d), [`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e), [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354), [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/sdk@3.10.26
- @thirdweb-dev/wallets@1.0.0
- @thirdweb-dev/chains@0.1.25
## 3.14.3
### Patch Changes
+1 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "3.14.3",
"version": "3.14.7",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -113,10 +113,6 @@
"ethers": ">=5.5.1",
"react": ">=18.0.0"
},
"resolutions": {
"ansi-regex": "^6.0.1",
"typescript": "^4.6.2"
},
"peerDependenciesMeta": {
"@solana/web3.js": {
"optional": true
@@ -1,6 +1,6 @@
import { Chain, defaultChains } from "@thirdweb-dev/chains";
import { useCallback, useState } from "react";
import { useActiveChain, useChainId } from "../../evm/hooks/wallet";
import { useChain, useChainId } from "../../evm/hooks/wallet";
import { useSupportedChains } from "../../evm/hooks/useSupportedChains";
import { useSwitchChain, useWallet } from "./wallet-hooks";
import { assertWindowEthereum, walletIds } from "@thirdweb-dev/wallets";
@@ -29,7 +29,7 @@ type NetworkMetadata = {
/**
*
* @deprecated - use `useActiveChain`, `useSwitchChain`, `useChainId` instead
* @deprecated - use `useChain`, `useSwitchChain`, `useChainId` instead
*
* Hook for getting metadata about the network the current wallet is connected to and switching networks
*
@@ -55,7 +55,7 @@ type NetworkMetadata = {
* @public
*/
export function useNetwork(): [NetworkMetadata, SwitchNetwork | undefined] {
const chain = useActiveChain();
const chain = useChain();
const chainId = useChainId();
const switchChain = useSwitchChain();
const supportedChains = useSupportedChains();
@@ -61,6 +61,15 @@ export interface ThirdwebProviderCoreProps<TChains extends Chain[]>
* Whether or not to automatically switch to wallet's network to active chain
*/
autoSwitch?: boolean;
/**
* Timeout for auto-connecting wallet in milliseconds
*
* If wallet fails to connect in this time, it will stop trying to connect and user will have to manually connect
*
* @defaultValue 15000
*/
autoConnectTimeout?: number;
}
export const ThirdwebProviderCore = <TChains extends Chain[]>({
@@ -145,6 +154,7 @@ export const ThirdwebProviderCore = <TChains extends Chain[]>({
dAppMeta={dAppMeta}
activeChain={activeChainWithKey}
autoSwitch={props.autoSwitch}
autoConnectTimeout={props.autoConnectTimeout}
>
<ThirdwebSDKProviderWrapper
queryClient={props.queryClient}
@@ -101,12 +101,15 @@ export function ThirdwebWalletProvider(
dAppMeta?: DAppMetaData;
chains: Chain[];
autoSwitch?: boolean;
autoConnectTimeout?: number;
}>,
) {
const [signer, setSigner] = useState<Signer | undefined>(undefined);
const [connectionStatus, setConnectionStatus] =
useState<ConnectionStatus>("unknown");
const autoConnectTimeout = props.autoConnectTimeout || 15000;
const [activeWallet, setActiveWallet] = useState<
WalletInstance | undefined
>();
@@ -311,8 +314,14 @@ export function ThirdwebWalletProvider(
const personalWalletInstance = createWalletInstance(personalWalleObj);
try {
await personalWalletInstance.autoConnect(
personalWalletInfo.connectParams,
await timeoutPromise(
personalWalletInstance.autoConnect(
personalWalletInfo.connectParams,
),
{
ms: autoConnectTimeout,
message: "Failed to Auto connect. Auto connect timed out.",
},
);
} catch (e) {
console.error(e);
@@ -337,7 +346,10 @@ export function ThirdwebWalletProvider(
try {
setConnectionStatus("connecting");
await wallet.autoConnect(walletInfo.connectParams);
await timeoutPromise(wallet.autoConnect(walletInfo.connectParams), {
ms: autoConnectTimeout,
message: "Failed to Auto connect. Auto connect timed out.",
});
setConnectedWallet(wallet, walletInfo.connectParams, true);
} catch (e) {
console.error(e);
@@ -356,6 +368,7 @@ export function ThirdwebWalletProvider(
props.shouldAutoConnect,
activeWallet,
connectionStatus,
autoConnectTimeout,
]);
const connectWallet = useCallback(
@@ -496,3 +509,32 @@ async function saveLastConnectedWalletInfo(
console.error("Error saving the last connected wallet info", e);
}
}
/**
* Timeout a promise with a given Error message if the promise does not resolve in given time
*
* @param promise - Promise to track for timeout
* @param option - timeout options
* @returns
*/
function timeoutPromise<T>(
promise: Promise<T>,
option: { ms: number; message: string },
) {
return new Promise<T>((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new Error(option.message));
}, option.ms);
promise.then(
(res) => {
clearTimeout(timeoutId);
resolve(res);
},
(err) => {
clearTimeout(timeoutId);
reject(err);
},
);
});
}
@@ -0,0 +1,185 @@
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/thirdweb-sdk-provider";
import { WalletAddress } from "../../types";
import {
cacheKeys,
invalidateContractAndBalances,
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import {
useMutation,
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
import type { AccountEvent, SmartContract } from "@thirdweb-dev/sdk";
import type { BytesLike } from "ethers";
import invariant from "tiny-invariant";
/** **********************/
/** READ HOOKS **/
/** **********************/
/**
* Get all accounts
*
* @example
* ```javascript
* const { data: accounts, isLoading, error } = useAccounts(contract);
* ```
*
* @param contract - an instance of a account factory contract
* @returns a response object that includes an array of all accounts with their associated admin
* @twfeature AccountFactory
* @see {@link https://portal.thirdweb.com/react/react.useaccounts?utm_source=sdk | Documentation}
* @beta
*/
export function useAccounts(
contract: RequiredParam<SmartContract>,
): UseQueryResult<AccountEvent[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.accountFactory.getAll(contractAddress),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.accountFactory.getAllAccounts,
"Contract instance does not support contract.accountFactory.getAllAccounts",
);
return contract.accountFactory.getAllAccounts();
},
{ enabled: !!contract },
);
}
/**
* Get all accounts associated with the provided address
*
* @example
* ```javascript
* const { data: accountsForAddress, isLoading, error } = useAccountsForAddress(contract, "{{account_address}}");
* ```
*
* @param contract - an instance of a account factory contract
* @returns a response object that includes an array of all accounts associated with the adress
* @twfeature AccountFactory
* @see {@link https://portal.thirdweb.com/react/react.useaccountsforaddress?utm_source=sdk | Documentation}
* @beta
*/
export function useAccountsForAddress(
contract: RequiredParam<SmartContract>,
address: RequiredParam<WalletAddress>,
): UseQueryResult<string[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.accountFactory.getAllForAddress(
contractAddress,
address,
),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.accountFactory.getAssociatedAccounts,
"Contract instance does not support contract.accountFactory.getAssociatedAccounts",
);
return contract.accountFactory.getAssociatedAccounts(address || "");
},
{ enabled: !!contract },
);
}
/**
* Check if a account has been deployed for the given admin
*
* @example
* ```javascript
* const { data: isAccountDeployed, isLoading, error } = useIsAccountDeployed(contract);
* ```
*
* @param contract - an instance of a account factory contract
* @returns a boolean indicating if a account has been deployed for the given admin
* @twfeature AccountFactory
* @see {@link https://portal.thirdweb.com/react/react.useisaccountdeployed?utm_source=sdk | Documentation}
* @beta
*/
export function useIsAccountDeployed(
contract: RequiredParam<SmartContract>,
admin: RequiredParam<WalletAddress>,
extraData?: BytesLike,
): UseQueryResult<boolean> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.accountFactory.isAccountDeployed(contractAddress, admin),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.accountFactory.isAccountDeployed,
"Contract instance does not support contract.accountFactory.getAllAccounts",
);
invariant(admin, "No account address provided");
return contract.accountFactory.isAccountDeployed(admin, extraData);
},
{ enabled: !!contract },
);
}
/** **********************/
/** WRITE HOOKS **/
/** **********************/
/**
* Create a account
*
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract("{{contract_address}}");
* const {
* mutate: createAccount,
* isLoading,
* error,
* } = useCreateAccount(contract);
*
* if (error) {
* console.error("failed to create account", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => createAccount("0x...")}
* >
* Create Account
* </button>
* );
* };
* ```
*
* @param contract - an instance of a account factory contract
* @returns a mutation object that can be used to create a account
* @twfeature AccountFactory
* @see {@link https://portal.thirdweb.com/react/react.usecreateaccount?utm_source=sdk | Documentation}
* @beta
*/
export function useCreateAccount(contract: RequiredParam<SmartContract>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
return useMutation(
async (admin: string, extraData?: BytesLike) => {
requiredParamInvariant(contract, "contract is undefined");
return contract.accountFactory.createAccount(admin, extraData);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
@@ -0,0 +1,125 @@
import type { providers } from "ethers";
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/thirdweb-sdk-provider";
import {
cacheKeys,
invalidateContractAndBalances,
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import {
useMutation,
UseMutationResult,
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
import type {
SignerWithRestrictions,
SignerWithRestrictionsBatchInput,
SmartContract,
} from "@thirdweb-dev/sdk";
import invariant from "tiny-invariant";
/** **********************/
/** READ HOOKS **/
/** **********************/
/**
* Get all accounts
*
* @example
* ```javascript
* const { data: accounts, isLoading, error } = useAccountSigners(contract);
* ```
*
* @param contract - an instance of a account
* @returns a response object that includes an array of all signers of the provided account
* @twfeature Account
* @see {@link https://portal.thirdweb.com/react/react.useaccountsigners?utm_source=sdk | Documentation}
* @beta
*/
export function useAccountSigners(
contract: RequiredParam<SmartContract>,
): UseQueryResult<SignerWithRestrictions[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.account.signers(contractAddress),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.account.getSignersWithRestrictions,
"Contract instance does not support contract.account.getSignersWithRestrictions",
);
return contract.account.getSignersWithRestrictions();
},
{ enabled: !!contract },
);
}
/** **********************/
/** WRITE HOOKS **/
/** **********************/
/**
* Set the account's entire snapshot of permissions
*
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract("{{contract_address}}");
* const {
* mutate: setAccountSigners,
* isLoading,
* error,
* } = useSetAccountSigners(contract);
*
* if (error) {
* console.error("failed to set account signers", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => setAccountSigners("0x...")}
* >
* Set Account Signers
* </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 },
unknown,
SignerWithRestrictionsBatchInput,
unknown
> {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
return useMutation(
async (permissionsSnapshot: SignerWithRestrictionsBatchInput) => {
requiredParamInvariant(contract, "contract is undefined");
return contract.account.setAccess(permissionsSnapshot);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
@@ -504,7 +504,7 @@ export function useEnglishAuctionWinningBid(
) {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.marketplace.auction.getWinningBid(
cacheKeys.contract.marketplace.englishAuctions.getWinningBid(
contractAddress,
auctionId,
),
@@ -596,7 +596,7 @@ export function useBidBuffer(
) {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.marketplace.getBidBufferBps(contractAddress),
cacheKeys.contract.marketplace.auction.getBidBufferBps(contractAddress, listingId),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
@@ -647,7 +647,7 @@ export function useMinimumNextBid(
) {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.marketplace.auction.getWinner(
cacheKeys.contract.marketplace.auction.getMinimumNextBid(
contractAddress,
listingId,
),
@@ -1,188 +0,0 @@
import {
RequiredParam,
requiredParamInvariant,
} from "../../../core/query-utils/required-param";
import { useSDKChainId } from "../../providers/thirdweb-sdk-provider";
import { WalletAddress } from "../../types";
import {
cacheKeys,
invalidateContractAndBalances,
} from "../../utils/cache-keys";
import { useQueryWithNetwork } from "../query-utils/useQueryWithNetwork";
import {
useMutation,
useQueryClient,
UseQueryResult,
} from "@tanstack/react-query";
import type { AccountEvent, SmartContract } from "@thirdweb-dev/sdk";
import type { BytesLike } from "ethers";
import invariant from "tiny-invariant";
/** **********************/
/** READ HOOKS **/
/** **********************/
/**
* Get all wallets
*
* @example
* ```javascript
* const { data: smartWallets, isLoading, error } = useSmartWallets(contract);
* ```
*
* @param contract - an instance of a smart wallet factory contract
* @returns a response object that includes an array of all smart wallets with their associated admin
* @twfeature SmartWalletFactory
* @see {@link https://portal.thirdweb.com/react/react.usesmartwallets?utm_source=sdk | Documentation}
* @beta
*/
export function useSmartWallets(
contract: RequiredParam<SmartContract>,
): UseQueryResult<AccountEvent[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.smartWalletFactory.getAll(contractAddress),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.smartWalletFactory.getAllAccounts,
"Contract instance does not support contract.smartWalletFactory.getAllAccounts",
);
return contract.smartWalletFactory.getAllAccounts();
},
{ enabled: !!contract },
);
}
/**
* Get all wallets associated with the provided address
*
* @example
* ```javascript
* const { data: smartWalletsForAddress, isLoading, error } = useSmartWalletsForAddress(contract, "{{wallet_address}}");
* ```
*
* @param contract - an instance of a smart wallet factory contract
* @returns a response object that includes an array of all smart wallets associated with the adress
* @twfeature SmartWalletFactory
* @see {@link https://portal.thirdweb.com/react/react.usesmartwalletsforaddress?utm_source=sdk | Documentation}
* @beta
*/
export function useSmartWalletsForAddress(
contract: RequiredParam<SmartContract>,
address: RequiredParam<WalletAddress>,
): UseQueryResult<string[]> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.smartWalletFactory.getAllForAddress(
contractAddress,
address,
),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.smartWalletFactory.getAssociatedWallets,
"Contract instance does not support contract.smartWalletFactory.getAssociatedWallets",
);
return contract.smartWalletFactory.getAssociatedWallets(address || "");
},
{ enabled: !!contract },
);
}
/**
* Check if a smart wallet has been deployed for the given admin
*
* @example
* ```javascript
* const { data: isSmartWalletDeployed, isLoading, error } = useIsSmartWalletDeployed(contract);
* ```
*
* @param contract - an instance of a smart wallet factory contract
* @returns a boolean indicating if a smart wallet has been deployed for the given admin
* @twfeature SmartWalletFactory
* @see {@link https://portal.thirdweb.com/react/react.useissmartwalletdeployed?utm_source=sdk | Documentation}
* @beta
*/
export function useIsSmartWalletDeployed(
contract: RequiredParam<SmartContract>,
admin: RequiredParam<WalletAddress>,
extraData?: BytesLike,
): UseQueryResult<boolean> {
const contractAddress = contract?.getAddress();
return useQueryWithNetwork(
cacheKeys.contract.smartWalletFactory.isSmartWalletDeployed(
contractAddress,
admin,
),
() => {
requiredParamInvariant(contract, "No Contract instance provided");
invariant(
contract.smartWalletFactory.isWalletDeployed,
"Contract instance does not support contract.smartWalletFactory.getAllWallets",
);
invariant(admin, "No wallet address provided");
return contract.smartWalletFactory.isWalletDeployed(admin, extraData);
},
{ enabled: !!contract },
);
}
/** **********************/
/** WRITE HOOKS **/
/** **********************/
/**
* Create a smart wallet
*
* @example
* ```jsx
* const Component = () => {
* const { contract } = useContract("{{contract_address}}");
* const {
* mutate: createSmartWallet,
* isLoading,
* error,
* } = useCreateSmartWallet(contract);
*
* if (error) {
* console.error("failed to create smart wallet", error);
* }
*
* return (
* <button
* disabled={isLoading}
* onClick={() => createSmartWallet("0x...")}
* >
* Create Smart Wallet
* </button>
* );
* };
* ```
*
* @param contract - an instance of a smart wallet factory contract
* @returns a mutation object that can be used to create a smart wallet
* @twfeature SmartWalletFactory
* @see {@link https://portal.thirdweb.com/react/react.usecreatesmartwallet?utm_source=sdk | Documentation}
* @beta
*/
export function useCreateSmartWallet(contract: RequiredParam<SmartContract>) {
const activeChainId = useSDKChainId();
const contractAddress = contract?.getAddress();
const queryClient = useQueryClient();
return useMutation(
async (admin: string, extraData?: BytesLike) => {
requiredParamInvariant(contract, "contract is undefined");
return contract.smartWalletFactory.createWallet(admin, extraData);
},
{
onSettled: () =>
invalidateContractAndBalances(
queryClient,
contractAddress,
activeChainId,
),
},
);
}
+9 -6
View File
@@ -194,13 +194,16 @@ export {
useBurnToken,
} from "./hooks/async/token";
// smart wallet factory
// account factory
export {
useIsSmartWalletDeployed,
useSmartWallets,
useCreateSmartWallet,
useSmartWalletsForAddress,
} from "./hooks/async/smart-wallet-factory";
useIsAccountDeployed,
useAccounts,
useCreateAccount,
useAccountsForAddress,
} from "./hooks/async/account-factory";
// account
export { useAccountSigners, useSetAccountSigners } from "./hooks/async/account";
// thirdweb hooks (work as long as at least `<ThirdwebSdkProvider>` is used)
+50 -24
View File
@@ -118,15 +118,25 @@ export const cacheKeys = {
overrides,
]),
smartWalletFactory: {
accountFactory: {
getAll: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, ["smartWalletFactory"]),
isSmartWalletDeployed: (contractAdress: RequiredParam<ContractAddress>, admin: RequiredParam<string>) =>
createContractCacheKey(contractAdress, ["isSmartWalletDeployed", admin]),
getAllForAddress: (contractAdress: RequiredParam<ContractAddress>, address: RequiredParam<string>) =>
createContractCacheKey(contractAddress, ["accountFactory"]),
isAccountDeployed: (
contractAdress: RequiredParam<ContractAddress>,
admin: RequiredParam<string>,
) => createContractCacheKey(contractAdress, ["isAccountDeployed", admin]),
getAllForAddress: (
contractAdress: RequiredParam<ContractAddress>,
address: RequiredParam<string>,
) =>
createContractCacheKey(contractAdress, ["getAllForAddress", address]),
},
account: {
signers: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, ["account", "signers"]),
},
app: {
get: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, ["appURI"]),
@@ -193,12 +203,8 @@ export const cacheKeys = {
},
},
sharedMetadata: {
get: (
contractAddress: RequiredParam<ContractAddress>,
) =>
createContractCacheKey(
contractAddress
),
get: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress),
},
drop: {
getAllUnclaimed: (
@@ -273,10 +279,16 @@ export const cacheKeys = {
contractAddress,
params ? ["getActiveListings", params] : ["getActiveListings"],
),
getBidBufferBps: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, ["getBidBufferBps"]),
auction: {
getBidBufferBps: (
contractAddress: RequiredParam<ContractAddress>,
listingId: RequiredParam<BigNumberish>,
) =>
createContractCacheKey(contractAddress, [
"auction",
"getBidBufferBps",
{ listingId },
]),
getWinningBid: (
contractAddress: RequiredParam<ContractAddress>,
listingId: RequiredParam<BigNumberish>,
@@ -286,6 +298,15 @@ export const cacheKeys = {
"getWinningBid",
{ listingId },
]),
getMinimumNextBid: (
contractAddress: RequiredParam<ContractAddress>,
listingId: RequiredParam<BigNumberish>,
) =>
createContractCacheKey(contractAddress, [
"auction",
"getMinimumNextBid",
{ listingId },
]),
getWinner: (
contractAddress: RequiredParam<ContractAddress>,
listingId: RequiredParam<BigNumberish>,
@@ -305,8 +326,8 @@ export const cacheKeys = {
createContractCacheKey(
contractAddress,
params
? ["getAllDirectListings", params]
: ["getAllDirectListings"],
? ["directListings", "getAll", params]
: ["directListings", "getAll"],
),
getAllValid: (
contractAddress: RequiredParam<ContractAddress>,
@@ -315,20 +336,22 @@ export const cacheKeys = {
createContractCacheKey(
contractAddress,
params
? ["getAllValidDirectListings", params]
: ["getAllValidDirectListings"],
? ["directListings", "getAllValid", params]
: ["directListings", "getAllValid"],
),
getListing: (
contractAddress: RequiredParam<ContractAddress>,
listingId: RequiredParam<BigNumberish>,
) =>
createContractCacheKey(contractAddress, [
"directListings",
"getListing",
{ listingId },
]),
getTotalCount: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, [
"getTotalDirectListingsCount",
"directListings",
"getTotalCount",
]),
},
@@ -340,8 +363,8 @@ export const cacheKeys = {
createContractCacheKey(
contractAddress,
params
? ["getAllEnglishAuctions", params]
: ["getAllEnglishAuctions"],
? ["englishAuctions", "getAll", params]
: ["englishAuctions", "getAll"],
),
getAllValid: (
contractAddress: RequiredParam<ContractAddress>,
@@ -350,14 +373,15 @@ export const cacheKeys = {
createContractCacheKey(
contractAddress,
params
? ["getAllEValidEnglishAuctions", params]
: ["getAllEValidEnglishAuctions"],
? ["englishAuctions", "getAllValid", params]
: ["englishAuctions", "getAllValid"],
),
getAuction: (
contractAddress: RequiredParam<ContractAddress>,
auctionId: RequiredParam<BigNumberish>,
) =>
createContractCacheKey(contractAddress, [
"englishAuctions",
"getAuction",
{ auctionId },
]),
@@ -366,12 +390,14 @@ export const cacheKeys = {
auctionId: RequiredParam<BigNumberish>,
) =>
createContractCacheKey(contractAddress, [
"englishAuctions",
"getWinningBid",
{ auctionId },
]),
getTotalCount: (contractAddress: RequiredParam<ContractAddress>) =>
createContractCacheKey(contractAddress, [
"getTotalEnglishAuctionsCount",
"englishAuctions",
"getTotalCount",
]),
},
},
@@ -1,5 +1,13 @@
# @thirdweb-dev/react-native-compat
## 0.2.26
## 0.2.25
## 0.2.24
## 0.2.23
## 0.2.22
## 0.2.21
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@thirdweb-dev/react-native-compat",
"description": "Shims for Thirdweb in React Native Projects",
"version": "0.2.22",
"version": "0.2.26",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+44
View File
@@ -1,5 +1,49 @@
# @thirdweb-dev/react-native
## 0.2.26
### Patch Changes
- [#1270](https://github.com/thirdweb-dev/js/pull/1270) [`0d5503aa`](https://github.com/thirdweb-dev/js/commit/0d5503aae90d9cd7a7adcabad19f7f45d9a37063) Thanks [@iketw](https://github.com/iketw)! - Only handle Coinbase callback URL if Coinbase is added as a supportedWallet
- [#1277](https://github.com/thirdweb-dev/js/pull/1277) [`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - remove ethers/lib/utils dependency
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8), [`be606dd3`](https://github.com/thirdweb-dev/js/commit/be606dd3c93c1514834c1d970e864d7f949a07ab), [`edcd22d6`](https://github.com/thirdweb-dev/js/commit/edcd22d61236edb2832f9b2f9796e891d58cb145), [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad), [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae)]:
- @thirdweb-dev/wallets@1.0.3
- @thirdweb-dev/sdk@3.10.27
- @thirdweb-dev/react-core@3.14.7
- @thirdweb-dev/chains@0.1.26
## 0.2.25
### Patch Changes
- Updated dependencies [[`ed711c8c`](https://github.com/thirdweb-dev/js/commit/ed711c8cb2ed6a0deb1b2a5eeec06df1d4edc5e8), [`05a7495a`](https://github.com/thirdweb-dev/js/commit/05a7495a132aabab5f48abbdb80a468ff6f65df8), [`e78da6b3`](https://github.com/thirdweb-dev/js/commit/e78da6b36cfa6d954234820563201bf760186ed1)]:
- @thirdweb-dev/wallets@1.0.2
- @thirdweb-dev/react-core@3.14.6
## 0.2.24
### Patch Changes
- Updated dependencies [[`efb3546e`](https://github.com/thirdweb-dev/js/commit/efb3546ec8268156be00301af28b9d83ecd5ab08)]:
- @thirdweb-dev/wallets@1.0.1
- @thirdweb-dev/react-core@3.14.5
## 0.2.23
### Patch Changes
- [#1215](https://github.com/thirdweb-dev/js/pull/1215) [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc) Thanks [@iketw](https://github.com/iketw)! - Migrate wallets to WCV2 and remove WCV1 support.
Please, check our changelogs for more info: https://blog.thirdweb.com/changelog/
- Updated dependencies [[`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d), [`06ca1c40`](https://github.com/thirdweb-dev/js/commit/06ca1c407dfee9d3d1fdaed1b6223ad0f9b8857b), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e), [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354), [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc), [`ad7dae3b`](https://github.com/thirdweb-dev/js/commit/ad7dae3b163e61c7a6eb57f654885a7fdaa4cbb6), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/sdk@3.10.26
- @thirdweb-dev/react-core@3.14.4
- @thirdweb-dev/wallets@1.0.0
- @thirdweb-dev/chains@0.1.25
## 0.2.22
### Patch Changes
+1 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.2.22",
"version": "0.2.26",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -44,10 +44,6 @@
"react-native-svg": "^13.8.0",
"tiny-invariant": "^1.2.0"
},
"resolutions": {
"react-native-mmkv": "2.5.1",
"@magic-sdk/provider": "17.2.0"
},
"devDependencies": {
"@babel/core": "^7.19.3",
"@babel/preset-env": "^7.1.6",
-1
View File
@@ -17,7 +17,6 @@ export { localWallet } from "./wallets/wallets/local-wallet";
export { LocalWallet } from "./wallets/wallets/LocalWallet";
export { magicWallet } from "./wallets/wallets/magic-wallet";
export { MagicWallet } from "./wallets/wallets/MagicWallet";
export * from "./wallets/wallets/WalletConnectV1";
export * from "./wallets/wallets/WalletConnectV2";
export type { WCMeta } from "./wallets/types/wc";
@@ -65,7 +65,9 @@ export const ThirdwebProvider = <
theme,
...restProps
}: PropsWithChildren<ThirdwebProviderProps<TChains>>) => {
useCoinbaseWalletListener();
useCoinbaseWalletListener(
!!supportedWallets.find((w) => w.id === walletIds.coinbase),
);
const hasMagicConfig = useMemo(
() => !!supportedWallets.find((wc) => wc.id === walletIds.magicLink),
@@ -17,8 +17,7 @@ import {
WagmiConnector,
} from "@thirdweb-dev/wallets";
import type { Address } from "abitype";
import { providers } from "ethers";
import { getAddress, hexValue } from "ethers/lib/utils.js";
import { providers, utils } from "ethers";
export type CoinbaseWalletConnectorOptions = WalletMobileSDKProviderOptions &
ConfigurationParams & {
@@ -130,7 +129,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
throw new Error("No accounts found");
}
// return checksum address
return getAddress(accounts[0] as string);
return utils.getAddress(accounts[0] as string);
}
async getChainId() {
@@ -193,7 +192,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
async switchChain(chainId: number): Promise<Chain> {
const provider = await this.getProvider();
const id = hexValue(chainId);
const id = utils.hexValue(chainId);
try {
await provider.request({
@@ -279,7 +278,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
if (accounts.length === 0) {
this.emit("disconnect");
} else {
this.emit("change", { account: getAddress(accounts[0] as string) });
this.emit("change", { account: utils.getAddress(accounts[0] as string) });
}
};
@@ -13,7 +13,7 @@ import { useCoinbaseWalletListener } from "./useCoinbaseWalletListener";
*/
export function useCoinbaseWallet(callbackURL: string) {
const connect = useConnect();
useCoinbaseWalletListener();
useCoinbaseWalletListener(true);
return useCallback(
async (connectOptions?: { chainId?: number; callbackURL: string }) => {
@@ -7,12 +7,16 @@ import { Linking } from "react-native";
*
* This hook sets up the UniversalLinks listener.
*/
export function useCoinbaseWalletListener() {
export function useCoinbaseWalletListener(enable: boolean) {
useEffect(() => {
if (!enable) {
return;
}
const sub = Linking.addEventListener("url", ({ url }) => {
// @ts-ignore - Passing a URL object to handleResponse crashes the function
handleResponse(url);
});
return () => sub?.remove();
}, []);
}, [enable]);
}
@@ -1,52 +0,0 @@
import {
WalletOptions,
WalletConnectV1Options,
WalletConnectV1 as WalletConnectV1Wallets,
} from "@thirdweb-dev/wallets";
import { formatWalletConnectDisplayUri } from "../../utils/uri";
import { Linking } from "react-native";
import { ExtraCoreWalletOptions } from "@thirdweb-dev/react-core";
import { WCMeta } from "../types/wc";
import { createAsyncLocalStorage } from "../../../core/AsyncStorage";
export type WC1Options = Omit<
WalletOptions<WalletConnectV1Options>,
"qrcode" | "walletId"
> &
ExtraCoreWalletOptions & {
walletId: NonNullable<WalletOptions["walletId"]>;
};
export abstract class WalletConnectV1 extends WalletConnectV1Wallets {
constructor(options: WC1Options) {
super({
...options,
walletId: options.walletId,
walletStorage:
options.walletStorage || createAsyncLocalStorage(options.walletId),
qrcode: false,
});
this.on("open_wallet", this._onWCOpenWallet);
this.on("disconnect", () => {
this.removeListener("open_wallet", this._onWCOpenWallet);
});
}
abstract getMeta(): WCMeta;
_onWCOpenWallet(uri?: string) {
const links = this.getMeta().links;
if (uri) {
const fullUrl = formatWalletConnectDisplayUri(uri, links);
Linking.openURL(fullUrl);
} else {
const fullUrl = formatWalletConnectDisplayUri("", links);
Linking.openURL(fullUrl);
}
}
}
@@ -1,9 +1,9 @@
import { walletIds } from "@thirdweb-dev/wallets";
import { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { WalletConnectV1 } from "./WalletConnectV1";
import { WalletConnectV2 } from "./WalletConnectV2";
import { WCMeta } from "../types/wc";
export class MetaMaskWallet extends WalletConnectV1 {
export class MetaMaskWallet extends WalletConnectV2 {
static id = walletIds.metamask;
static meta = {
name: "MetaMask",
@@ -20,11 +20,17 @@ export class MetaMaskWallet extends WalletConnectV1 {
}
}
export const metamaskWallet = () => {
type MetaMaskWalletConfig = { projectId?: string };
export const metamaskWallet = (config?: MetaMaskWalletConfig) => {
return {
id: MetaMaskWallet.id,
meta: MetaMaskWallet.meta,
create: (options: WalletOptions) =>
new MetaMaskWallet({ ...options, walletId: walletIds.metamask }),
new MetaMaskWallet({
...options,
walletId: walletIds.metamask,
projectId: config?.projectId,
}),
} satisfies WalletConfig;
};
@@ -2,9 +2,9 @@ import {
WalletOptions as WalletOptionsRC,
WalletConfig,
} from "@thirdweb-dev/react-core";
import { WalletConnectV1 } from "./WalletConnectV1";
import { WalletConnectV2 } from "./WalletConnectV2";
export class RainbowWallet extends WalletConnectV1 {
export class RainbowWallet extends WalletConnectV2 {
static id = "rainbow" as const;
static meta = {
name: "Rainbow",
@@ -21,11 +21,17 @@ export class RainbowWallet extends WalletConnectV1 {
}
}
export const rainbowWallet = () => {
type RainbowWalletConfig = { projectId?: string };
export const rainbowWallet = (config?: RainbowWalletConfig) => {
return {
id: RainbowWallet.id,
meta: RainbowWallet.meta,
create: (options: WalletOptionsRC) =>
new RainbowWallet({ ...options, walletId: RainbowWallet.id }),
new RainbowWallet({
...options,
walletId: RainbowWallet.id,
projectId: config?.projectId,
}),
} satisfies WalletConfig;
};
@@ -1,5 +1,5 @@
import { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { WC2Options, WalletConnectV2 } from "./WalletConnectV2";
import { WalletConnectV2 } from "./WalletConnectV2";
import { WCMeta } from "../types/wc";
export class TrustWallet extends WalletConnectV2 {
@@ -14,10 +14,6 @@ export class TrustWallet extends WalletConnectV2 {
},
};
constructor(options: WC2Options) {
super(options);
}
getMeta(): WCMeta {
return TrustWallet.meta;
}
+46
View File
@@ -1,5 +1,51 @@
# @thirdweb-dev/react
## 3.14.7
### Patch Changes
- [#1210](https://github.com/thirdweb-dev/js/pull/1210) [`d794b8f9`](https://github.com/thirdweb-dev/js/commit/d794b8f9f41dbd7e461ef07d0808c5b6d2f85515) Thanks [@konojunya](https://github.com/konojunya)! - Add zerion to useInstalledWallets
- [#1269](https://github.com/thirdweb-dev/js/pull/1269) [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae) Thanks [@MananTank](https://github.com/MananTank)! - - Expose `advancedOptions` and `styles` for paper wallet
- Allow using "+" in paper email
- Allow using "+" in magic email
- Update Paper SDK version
- Updated dependencies [[`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053), [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8), [`be606dd3`](https://github.com/thirdweb-dev/js/commit/be606dd3c93c1514834c1d970e864d7f949a07ab), [`edcd22d6`](https://github.com/thirdweb-dev/js/commit/edcd22d61236edb2832f9b2f9796e891d58cb145), [`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad), [`998e5217`](https://github.com/thirdweb-dev/js/commit/998e521733efd6a67e42f9bf0beab5fbdccf08ae)]:
- @thirdweb-dev/wallets@1.0.3
- @thirdweb-dev/sdk@3.10.27
- @thirdweb-dev/react-core@3.14.7
- @thirdweb-dev/chains@0.1.26
## 3.14.6
### Patch Changes
- Updated dependencies [[`ed711c8c`](https://github.com/thirdweb-dev/js/commit/ed711c8cb2ed6a0deb1b2a5eeec06df1d4edc5e8), [`05a7495a`](https://github.com/thirdweb-dev/js/commit/05a7495a132aabab5f48abbdb80a468ff6f65df8), [`e78da6b3`](https://github.com/thirdweb-dev/js/commit/e78da6b36cfa6d954234820563201bf760186ed1)]:
- @thirdweb-dev/wallets@1.0.2
- @thirdweb-dev/react-core@3.14.6
## 3.14.5
### Patch Changes
- Updated dependencies [[`efb3546e`](https://github.com/thirdweb-dev/js/commit/efb3546ec8268156be00301af28b9d83ecd5ab08)]:
- @thirdweb-dev/wallets@1.0.1
- @thirdweb-dev/react-core@3.14.5
## 3.14.4
### Patch Changes
- [#1215](https://github.com/thirdweb-dev/js/pull/1215) [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc) Thanks [@iketw](https://github.com/iketw)! - Migrate wallets to WCV2 and remove WCV1 support.
Please, check our changelogs for more info: https://blog.thirdweb.com/changelog/
- Updated dependencies [[`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d), [`06ca1c40`](https://github.com/thirdweb-dev/js/commit/06ca1c407dfee9d3d1fdaed1b6223ad0f9b8857b), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`ac3e019c`](https://github.com/thirdweb-dev/js/commit/ac3e019cd1776dbdb0d06b213420ad17586f678e), [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354), [`3c5dc480`](https://github.com/thirdweb-dev/js/commit/3c5dc4804abc56b933ec45e9e1da11eb182296cc), [`ad7dae3b`](https://github.com/thirdweb-dev/js/commit/ad7dae3b163e61c7a6eb57f654885a7fdaa4cbb6), [`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/sdk@3.10.26
- @thirdweb-dev/react-core@3.14.4
- @thirdweb-dev/wallets@1.0.0
- @thirdweb-dev/chains@0.1.25
## 3.14.3
### Patch Changes
+1 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.14.3",
"version": "3.14.7",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -153,10 +153,6 @@
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
},
"resolutions": {
"ansi-regex": "^6.0.1",
"typescript": "^4.6.2"
},
"peerDependenciesMeta": {
"@solana/wallet-adapter-base": {
"optional": true
@@ -1,5 +1,9 @@
import { useConnect, useWallet } from "@thirdweb-dev/react-core";
import { PaperWallet, walletIds } from "@thirdweb-dev/wallets";
import {
PaperWallet,
walletIds,
PaperWalletAdditionalOptions,
} from "@thirdweb-dev/wallets";
import { useCallback, useEffect } from "react";
import {
useQuery,
@@ -7,10 +11,12 @@ import {
useQueryClient,
} from "@tanstack/react-query";
type PaperConfig = Omit<PaperWalletAdditionalOptions, "chain" | "chains">;
export function usePaperWallet() {
const connect = useConnect();
return useCallback(
async (options: { chainId?: number; clientId: string; email?: string }) => {
async (options: { chainId?: number; email?: string } & PaperConfig) => {
const { paperWallet } = await import(
"../../../wallet/wallets/paperWallet"
);
@@ -18,15 +18,15 @@ interface ThirdwebProviderProps<TChains extends Chain[]>
"createWalletStorage" | "supportedWallets"
> {
/**
* Wallets that will be supported by the dApp
* @defaultValue [metamaskWallet(), coinbaseWallet(), walletConnectV1()]
* Wallets supported by the dApp
* @defaultValue `[ metamaskWallet(), coinbaseWallet(), walletConnect() ]`
*
* @example
* ```jsx
* import { metamaskWallet, coinbaseWallet, walletConnectV1 } from "@thirdweb-dev/react";
* import { metamaskWallet, coinbaseWallet, walletConnect } from "@thirdweb-dev/react";
*
* <ThirdwebProvider
* supportedWallets={[metamaskWallet(), coinbaseWallet(), walletConnectV1()]}
* supportedWallets={[metamaskWallet(), coinbaseWallet(), walletConnect()]}
* />
* ```
*/
@@ -385,6 +385,17 @@ const NetworkList = /* @__PURE__ */ memo(function NetworkList(props: {
const [switchingChainId, setSwitchingChainId] = useState(-1);
const [errorSwitchingChainId, setErrorSwitchingChainId] = useState(-1);
const close = props.close;
useEffect(() => {
// if switching and switched successfully - close modal
if (switchingChainId !== -1 && activeChainId === switchingChainId) {
if (close) {
close();
}
}
}, [switchingChainId, close, activeChainId]);
const handleSwitch = async (chain: Chain) => {
setErrorSwitchingChainId(-1);
setSwitchingChainId(chain.chainId);
@@ -3,22 +3,22 @@ import { assertWindowEthereum } from "@thirdweb-dev/wallets";
export function useInstalledWallets() {
let isMetamaskInstalled = false;
let isCoinbaseWalletInstalled = false;
let isTrustWalletInstalled: boolean | undefined = false;
let isZerionWalletInstalled = false;
let isTrustWalletInstalled = false;
if (assertWindowEthereum(globalThis.window)) {
isMetamaskInstalled = globalThis.window.ethereum?.isMetaMask;
isMetamaskInstalled = !!globalThis.window.ethereum?.isMetaMask;
isCoinbaseWalletInstalled =
globalThis.window.ethereum?.isCoinbaseWallet ||
globalThis.window.ethereum?.providers?.some((p) => p.isCoinbaseWallet) ||
false;
isTrustWalletInstalled = globalThis.window.ethereum?.isTrust;
!!globalThis.window.ethereum?.isCoinbaseWallet ||
!!globalThis.window.ethereum?.providers?.some((p) => p.isCoinbaseWallet);
isZerionWalletInstalled = !!globalThis.window.ethereum?.isZerion;
isTrustWalletInstalled = !!globalThis.window.ethereum?.isTrust;
}
const installedWallets = {
metamask: !!isMetamaskInstalled,
coinbaseWallet: !!isCoinbaseWalletInstalled,
trustWallet: !!isTrustWalletInstalled,
return {
metamask: isMetamaskInstalled,
coinbaseWallet: isCoinbaseWalletInstalled,
trustWallet: isTrustWalletInstalled,
zerionWallet: isZerionWalletInstalled,
};
return installedWallets;
}
@@ -1,10 +1,10 @@
import { WalletConfig } from "@thirdweb-dev/react-core";
import { coinbaseWallet } from "./coinbase/coinbaseWallet";
import { metamaskWallet } from "./metamask/metamaskWallet";
import { walletConnectV1 } from "./walletConnectV1";
import { walletConnect } from "./walletConnect";
export const defaultWallets: WalletConfig<any>[] = /* @__PURE__ */ (() => [
metamaskWallet(),
coinbaseWallet(),
walletConnectV1(),
walletConnect(),
])();
@@ -79,7 +79,7 @@ const MagicSelectionUI: React.FC<
if (isEmail && isEmailEnabled) {
const emailRegex = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,})$/g;
const isValidEmail = emailRegex.test(input);
const isValidEmail = emailRegex.test(input.replace(/\+/g, ""));
if (!isValidEmail) {
return "Invalid email address";
}
@@ -2,12 +2,28 @@ import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { MetaMaskWallet, assertWindowEthereum } from "@thirdweb-dev/wallets";
import { MetamaskConnectUI } from "./MetamaskConnectUI";
export const metamaskWallet = (): WalletConfig<MetaMaskWallet> => {
type MetamaskWalletOptions = {
/**
* When connecting MetaMask using the QR Code - Wallet Connect connector is used which requires a project id.
* This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com.
*
* https://docs.walletconnect.com/2.0/web3modal/options#projectid-required
*/
projectId?: string;
};
export const metamaskWallet = (
options?: MetamaskWalletOptions,
): WalletConfig<MetaMaskWallet> => {
return {
id: MetaMaskWallet.id,
meta: MetaMaskWallet.meta,
create: (options: WalletOptions) => {
return new MetaMaskWallet({ ...options, qrcode: false });
create: (walletOptions: WalletOptions) => {
return new MetaMaskWallet({
...walletOptions,
projectId: options?.projectId,
qrcode: false,
});
},
connectUI: MetamaskConnectUI,
isInstalled() {
@@ -1,4 +1,7 @@
import { PaperWallet } from "@thirdweb-dev/wallets";
import {
PaperWallet,
PaperWalletAdditionalOptions,
} from "@thirdweb-dev/wallets";
import {
WalletConfig,
WalletOptions,
@@ -11,7 +14,7 @@ import { Spinner } from "../../components/Spinner";
import { Flex } from "../../components/basic";
import { InputSelectionUI } from "./InputSelectionUI";
type PaperConfig = { clientId: string };
type PaperConfig = Omit<PaperWalletAdditionalOptions, "chain" | "chains">;
export const paperWallet = (config: PaperConfig): WalletConfig<PaperWallet> => {
return {
@@ -32,7 +35,8 @@ const PaperSelectionUI: React.FC<SelectUIProps<PaperWallet>> = (props) => {
placeholder="Enter your email address"
name="email"
type="email"
errorMessage={(input) => {
errorMessage={(_input) => {
const input = _input.replace(/\+/g, "");
const emailRegex = /^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,})$/g;
const isValidEmail = emailRegex.test(input);
if (!isValidEmail) {
@@ -1,12 +1,9 @@
import { WalletConnectV1 } from "@thirdweb-dev/wallets";
import { WalletConfig, WalletOptions } from "@thirdweb-dev/react-core";
import { walletConnect } from "./walletConnect";
export const walletConnectV1 = (): WalletConfig<WalletConnectV1> => {
return {
id: WalletConnectV1.id,
meta: WalletConnectV1.meta,
create(options: WalletOptions) {
return new WalletConnectV1({ ...options, qrcode: true });
},
};
};
/**
* @deprecated Use `walletConnect` instead
*
* The WalletConnect v1.0 protocol has been shut down and no longer works.
* To avoid breaking change, `walletConnectV1` is still available but is an alias of `walletConnect`.
*/
export const walletConnectV1 = walletConnect;
@@ -2,12 +2,28 @@ import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { ZerionWallet, assertWindowEthereum } from "@thirdweb-dev/wallets";
import { ZerionConnectUI } from "./ZerionConnectUI";
export const zerionWallet = (): WalletConfig<ZerionWallet> => {
type ZerionkWalletOptions = {
/**
* When connecting MetaMask using the QR Code - Wallet Connect connector is used which requires a project id.
* This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com.
*
* https://docs.walletconnect.com/2.0/web3modal/options#projectid-required
*/
projectId?: string;
};
export const zerionWallet = (
options?: ZerionkWalletOptions,
): WalletConfig<ZerionWallet> => {
return {
id: ZerionWallet.id,
meta: ZerionWallet.meta,
create: (options: WalletOptions) => {
return new ZerionWallet(options);
create: (walletOptions: WalletOptions) => {
return new ZerionWallet({
...walletOptions,
projectId: options?.projectId,
qrcode: false,
});
},
connectUI: ZerionConnectUI,
isInstalled() {
+1
View File
@@ -24,6 +24,7 @@ module.exports = {
files: "./test/**/*",
rules: {
"@typescript-eslint/no-restricted-imports": "off",
"better-tree-shaking/no-top-level-side-effects": "off",
},
},
],
+22
View File
@@ -1,5 +1,27 @@
# @thirdweb-dev/sdk
## 3.10.27
### Patch Changes
- [#1277](https://github.com/thirdweb-dev/js/pull/1277) [`2b113539`](https://github.com/thirdweb-dev/js/commit/2b113539098384f910b3c4d54e1fde9d35a6f053) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - remove ethers/lib/utils dependency
- [#1271](https://github.com/thirdweb-dev/js/pull/1271) [`c9535715`](https://github.com/thirdweb-dev/js/commit/c95357158819abd42d4b0900ecc2fa40fcb957f8) Thanks [@nkrishang](https://github.com/nkrishang)! - Rename smart wallets to account
- Updated dependencies [[`5882091e`](https://github.com/thirdweb-dev/js/commit/5882091eab65978009a5a5305701f121851b10ad)]:
- @thirdweb-dev/chains@0.1.26
## 3.10.26
### Patch Changes
- [#1249](https://github.com/thirdweb-dev/js/pull/1249) [`ef4cb092`](https://github.com/thirdweb-dev/js/commit/ef4cb092192c58c9b292b29590b888f45f9fd23d) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Update EIP155 error substrings for any evm deployments
- [#1254](https://github.com/thirdweb-dev/js/pull/1254) [`30ac3aef`](https://github.com/thirdweb-dev/js/commit/30ac3aef840bde51a61acf786c709cffd3c47354) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Fetch base64 NFTs
- Updated dependencies [[`620e89dc`](https://github.com/thirdweb-dev/js/commit/620e89dc25c91557e2164a602c7aedd733525087), [`0fd8aa04`](https://github.com/thirdweb-dev/js/commit/0fd8aa04a9424497758d13a51a72363edcc30e19)]:
- @thirdweb-dev/chains@0.1.25
## 3.10.25
### Patch Changes
+1 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/sdk",
"version": "3.10.25",
"version": "3.10.27",
"description": "The main thirdweb SDK.",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
"license": "Apache-2.0",
@@ -197,12 +197,6 @@
"yaml": "^2.1.1",
"zod": "^3.20.2"
},
"resolutions": {
"bn.js": "^5.2.1",
"nanoid": "^3.1.31",
"typescript": "^4.6.2",
"@microsoft/tsdoc": "^0.14.1"
},
"bugs": {
"url": "https://github.com/thirdweb-dev/js/issues"
},
@@ -10,6 +10,8 @@ export const ERROR_SUBSTRINGS = [
"recovered sender mismatch",
"transaction hash mismatch",
"chainid no support",
"chainid (0)",
"chainid(0)",
];
export const ERROR_SUBSTRINGS_COMPOSITE = [
@@ -1,5 +1,4 @@
import { BigNumber, providers, constants } from "ethers";
import { isAddress } from "ethers/lib/utils";
import { BigNumber, providers, constants, utils } from "ethers";
import { extractIPFSHashFromBytecode } from "./extractIPFSHashFromBytecode";
import { extractMinimalProxyImplementationAddress } from "./extractMinimalProxyImplementationAddress";
@@ -49,7 +48,7 @@ export async function resolveContractUriFromAddress(
);
const implementationAddress = `0x${proxyStorage.slice(-40)}`;
if (
isAddress(implementationAddress) &&
utils.isAddress(implementationAddress) &&
implementationAddress !== constants.AddressZero
) {
return await resolveContractUriFromAddress(
+16
View File
@@ -46,6 +46,22 @@ export async function fetchTokenMetadata(
tokenUri: string,
storage: ThirdwebStorage,
): Promise<NFTMetadata> {
// check for base64 encoded JSON
if (
tokenUri.startsWith("data:application/json;base64") &&
typeof Buffer !== "undefined"
) {
const base64 = tokenUri.split(",")[1];
const jsonMetadata = JSON.parse(
Buffer.from(base64, "base64").toString("utf-8"),
);
return CommonNFTOutput.parse({
...jsonMetadata,
id: BigNumber.from(tokenId).toString(),
uri: tokenUri,
});
}
// handle dynamic id URIs (2 possible formats)
const parsedUri = tokenUri.replace(
"{id}",
utils.hexZeroPad(BigNumber.from(tokenId).toHexString(), 32).slice(2),
@@ -62,8 +62,8 @@ import {
FEATURE_ENGLISH_AUCTIONS,
FEATURE_OFFERS,
FEATURE_EXTENSION_ROUTER,
FEATURE_SMART_WALLET,
FEATURE_SMART_WALLET_FACTORY,
FEATURE_ACCOUNT,
FEATURE_ACCOUNT_FACTORY,
} from "./thirdweb-features";
/**
@@ -127,8 +127,8 @@ export type Feature =
| typeof FEATURE_DIRECT_LISTINGS
| typeof FEATURE_ENGLISH_AUCTIONS
| typeof FEATURE_OFFERS
| typeof FEATURE_SMART_WALLET
| typeof FEATURE_SMART_WALLET_FACTORY;
| typeof FEATURE_ACCOUNT
| typeof FEATURE_ACCOUNT_FACTORY;
export type Extension = Feature;
@@ -170,6 +170,6 @@ export const SUPPORTED_FEATURES: Record<string, Feature> = /* @__PURE__ */ {
[FEATURE_DIRECT_LISTINGS.name]: FEATURE_DIRECT_LISTINGS,
[FEATURE_ENGLISH_AUCTIONS.name]: FEATURE_ENGLISH_AUCTIONS,
[FEATURE_OFFERS.name]: FEATURE_OFFERS,
[FEATURE_SMART_WALLET_FACTORY.name]: FEATURE_SMART_WALLET_FACTORY,
[FEATURE_SMART_WALLET.name]: FEATURE_SMART_WALLET,
[FEATURE_ACCOUNT_FACTORY.name]: FEATURE_ACCOUNT_FACTORY,
[FEATURE_ACCOUNT.name]: FEATURE_ACCOUNT,
};
@@ -279,24 +279,24 @@ export const FEATURE_OFFERS = {
features: {},
} as const;
export const FEATURE_SMART_WALLET_FACTORY = {
name: "SmartWalletFactory",
namespace: "smartwallet.factory",
export const FEATURE_ACCOUNT_FACTORY = {
name: "AccountFactory",
namespace: "accountFactory",
docLinks: {
// TODO
sdk: "sdk.smartWalletFactory",
contracts: "smartWalletFactory",
sdk: "sdk.accountFactory",
contracts: "",
},
abis: [IAccountFactory],
features: {},
} as const;
export const FEATURE_SMART_WALLET = {
name: "SmartWallet",
namespace: "smartwallet",
export const FEATURE_ACCOUNT = {
name: "Account",
namespace: "account",
docLinks: {
// TODO
sdk: "",
sdk: "sdk.account",
contracts: "",
},
abis: [IAccountCore],
@@ -14,8 +14,8 @@ import {
FEATURE_PLATFORM_FEE,
FEATURE_PRIMARY_SALE,
FEATURE_ROYALTY,
FEATURE_SMART_WALLET_FACTORY,
FEATURE_SMART_WALLET,
FEATURE_ACCOUNT_FACTORY,
FEATURE_ACCOUNT,
} from "../constants/thirdweb-features";
import { Transaction } from "../core/classes/transactions";
import { ContractAppURI } from "../core/classes/contract-appuri";
@@ -54,7 +54,7 @@ import type {
EnglishAuctionsLogic,
OffersLogic,
IAccountFactory,
IAccountCore
IAccountCore,
} from "@thirdweb-dev/contracts-js";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { BaseContract, CallOverrides } from "ethers";
@@ -66,8 +66,8 @@ import { ContractOwner } from "../core/classes/contract-owner";
import { MarketplaceV3DirectListings } from "../core/classes/marketplacev3-direct-listings";
import { MarketplaceV3EnglishAuctions } from "../core/classes/marketplacev3-english-auction";
import { MarketplaceV3Offers } from "../core/classes/marketplacev3-offers";
import { SmartWalletFactory } from "../core/classes/smart-wallet-factory";
import { SmartWallet } from "../core/classes/smart-wallet";
import { AccountFactory } from "../core/classes/account-factory";
import { Account } from "../core/classes/account";
/**
* Custom contract dynamic class with feature detection
@@ -299,46 +299,40 @@ export class SmartContract<
}
/**
* Smart Wallet Factory
* Account Factory
*
* @remarks Create smart wallets and fetch data about them.
* @remarks Create accounts and fetch data about them.
* @example
* ```javascript
*
* // Predict the address of the smart wallet that will be created for an admin.
* const deterministicAddress = await contract.smartWalletFactory.predictWalletAddress(admin, extraData);
* // Predict the address of the account that will be created for an admin.
* const deterministicAddress = await contract.accountFactory.predictAccountAddress(admin, extraData);
*
* // Create smart wallets
* const tx = await contract.smartWalletFactory.createWallet(admin, extraData);
* // Create accounts
* const tx = await contract.accountFactory.createAccount(admin, extraData);
* // the same as `deterministicAddress`
* const smartWalletAddress = tx.address;
* const accountAddress = tx.address;
*
* // Get all smart wallets created by the factory
* const allWallets = await contract.smartWalletFactory.getAllWallets();
* // Get all accounts created by the factory
* const allAccounts = await contract.accountFactory.getAllAccounts();
*
* // Get all smart wallets on which a signer has been given authority.
* const associatedWallets = await contract.smartWalletFactory.getAssociatedWallets(signer);
* // Get all accounts on which a signer has been given authority.
* const associatedAccounts = await contract.accountFactory.getAssociatedAccounts(signer);
*
* // Get all signers who have been given authority on a smart wallet.
* const associatedSigners = await contract.smartWalletFactory.getAssociatedSigners(smartWalletAddress);
* // Get all signers who have been given authority on a account.
* const associatedSigners = await contract.accountFactory.getAssociatedSigners(accountAddress);
*
* // Check whether a smart wallet has already been created for a given admin.
* const isWalletDeployed = await contract.smartWalletFactory.isWalletDeployed(admin, extraData);
* // Check whether a account has already been created for a given admin.
* const isAccountDeployed = await contract.accountFactory.isAccountDeployed(admin, extraData);
* ```
*/
get smartWalletFactory(): SmartWalletFactory<IAccountFactory> {
return assertEnabled(
this.detectSmartWalletFactory(),
FEATURE_SMART_WALLET_FACTORY,
);
get accountFactory(): AccountFactory<IAccountFactory> {
return assertEnabled(this.detectAccountFactory(), FEATURE_ACCOUNT_FACTORY);
}
// TODO documentation
get smartWallet(): SmartWallet<IAccountCore> {
return assertEnabled(
this.detectSmartWallet(),
FEATURE_SMART_WALLET,
)
get account(): Account<IAccountCore> {
return assertEnabled(this.detectAccount(), FEATURE_ACCOUNT);
}
private _chainId: number;
@@ -568,25 +562,28 @@ export class SmartContract<
return undefined;
}
// ========== Smart account features ==========
// ========== Account features ==========
private detectSmartWalletFactory() {
private detectAccountFactory() {
if (
detectContractFeature<IAccountFactory>(
this.contractWrapper,
FEATURE_SMART_WALLET_FACTORY.name,
FEATURE_ACCOUNT_FACTORY.name,
)
) {
return new SmartWalletFactory(this.contractWrapper);
return new AccountFactory(this.contractWrapper);
}
return undefined;
}
private detectSmartWallet() {
private detectAccount() {
if (
detectContractFeature<IAccountCore>(this.contractWrapper, FEATURE_SMART_WALLET.name)
detectContractFeature<IAccountCore>(
this.contractWrapper,
FEATURE_ACCOUNT.name,
)
) {
return new SmartWallet(this.contractWrapper);
return new Account(this.contractWrapper);
}
return undefined;
}
@@ -1,6 +1,6 @@
import type { IAccountFactory } from "@thirdweb-dev/contracts-js";
import { DetectableFeature } from "../interfaces/DetectableFeature";
import { FEATURE_SMART_WALLET_FACTORY } from "../../constants/thirdweb-features";
import { FEATURE_ACCOUNT_FACTORY } from "../../constants/thirdweb-features";
import { ContractEvents } from "./contract-events";
import { ContractWrapper } from "./contract-wrapper";
@@ -9,13 +9,13 @@ import { Transaction } from "./transactions";
import { TransactionResultWithAddress } from "../types";
import { type BytesLike, utils } from "ethers";
import { AccountCreatedEvent } from "@thirdweb-dev/contracts-js/dist/declarations/src/AccountFactory";
import type { AccountEvent } from "../../types/smart-wallet";
import type { AccountEvent } from "../../types/account";
import { isContractDeployed } from "../../common/any-evm-utils/isContractDeployed";
export class SmartWalletFactory<TContract extends IAccountFactory>
export class AccountFactory<TContract extends IAccountFactory>
implements DetectableFeature
{
featureName = FEATURE_SMART_WALLET_FACTORY.name;
featureName = FEATURE_ACCOUNT_FACTORY.name;
private contractWrapper: ContractWrapper<IAccountFactory>;
// utilities
@@ -36,19 +36,19 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
*******************************/
/**
* Get the deterministic address of the smart wallet that will be created
* Get the deterministic address of the account that will be created
*
* @example
* ```javascript
* const walletAddress = await contract.smartWalletFactory.predictWalletAddress(admin);
* const accountAddress = await contract.accountFactory.predictAccountAddress(admin);
* ```
* @param admin - The admin of the smart wallet.
* @param extraData - (Optional) Extra data to be passed to the smart wallet on creation.
* @returns the deterministic address of the smart wallet that will be created for the given admin.
* @param admin - The admin of the account.
* @param extraData - (Optional) Extra data to be passed to the account on creation.
* @returns the deterministic address of the account that will be created for the given admin.
*
* @twfeature SmartWalletFactory
* @twfeature AccountFactory
*/
public async predictWalletAddress(
public async predictAccountAddress(
admin: string,
extraData?: BytesLike,
): Promise<string> {
@@ -60,48 +60,48 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
}
/**
* Get all signers who have authority on the given smart wallet
* Get all signers who have authority on the given account
*
* @example
* ```javascript
* const allSigners = await contract.smartWalletFactory.getAssociatedSigners(admin);
* const allSigners = await contract.accountFactory.getAssociatedSigners(admin);
* ```
* @param wallet - The smart wallet address.
* @returns all signers who have authority on the given smart wallet.
* @param account - The account address.
* @returns all signers who have authority on the given account.
*
* @twfeature SmartWalletFactory
* @twfeature AccountFactory
*/
public async getAssociatedSigners(wallet: string): Promise<string[]> {
return this.contractWrapper.readContract.getSignersOfAccount(wallet);
public async getAssociatedSigners(account: string): Promise<string[]> {
return this.contractWrapper.readContract.getSignersOfAccount(account);
}
/**
* Get all wallets on which the given signer has authority
* Get all accounts on which the given signer has authority
*
* @example
* ```javascript
* const allWallets = await contract.smartWalletFactory.getAssociatedWallets(admin);
* const allAccounts = await contract.accountFactory.getAssociatedAccounts(admin);
* ```
* @param signer - The smart wallet address.
* @returns all wallets on which the given signer has authority.
* @param signer - The account address.
* @returns all accounts on which the given signer has authority.
*
* @twfeature SmartWalletFactory
* @twfeature AccountFactory
*/
public async getAssociatedWallets(signer: string): Promise<string[]> {
public async getAssociatedAccounts(signer: string): Promise<string[]> {
return this.contractWrapper.readContract.getAccountsOfSigner(signer);
}
/**
* Get all wallets
* Get all accounts
*
* @example
* ```javascript
* const allWallets = await contract.smartWalletFactory.getAllWallets();
* const allAccounts = await contract.accountFactory.getAllAccounts();
* ```
*
* @returns all wallets created via the smart wallet factory.
* @returns all accounts created via the account factory.
*
* @twfeature SmartWalletFactory
* @twfeature AccountFactory
*/
public async getAllAccounts(): Promise<AccountEvent[]> {
const allAccounts =
@@ -122,17 +122,17 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
}
/**
* Check if a smart wallet has been deployed for the given admin
* Check if a account has been deployed for the given admin
*
* @param admin - The admin of the smart wallet.
* @param extraData - (Optional) Extra data to be passed to the smart wallet on creation.
* @returns whether the smart wallet has been deployed for the given admin.
* @param admin - The admin of the account.
* @param extraData - (Optional) Extra data to be passed to the account on creation.
* @returns whether the account has been deployed for the given admin.
*/
public async isWalletDeployed(
public async isAccountDeployed(
admin: string,
extraData?: BytesLike,
): Promise<boolean> {
const addr = await this.predictWalletAddress(admin, extraData);
const addr = await this.predictAccountAddress(admin, extraData);
return isContractDeployed(addr, this.contractWrapper.getProvider());
}
@@ -141,29 +141,29 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
*******************************/
/**
* Create a smart wallet
* Create a account
*
* @remarks Create a smart wallet for an admin. The admin will have complete authority over the smart wallet.
* @remarks Create a account for an admin. The admin will have complete authority over the account.
*
* @param admin - The admin of the smart wallet.
* @param extraData - (Optional) Extra data to be passed to the smart wallet on creation.
* @param admin - The admin of the account.
* @param extraData - (Optional) Extra data to be passed to the account on creation.
*
* @example
* ```javascript
* const tx = await contract.smartWalletFactory.createWallet(admin, extraData);
* const tx = await contract.accountFactory.createAccount(admin, extraData);
* const receipt = tx.receipt();
* const smartWalletAddress = tx.address;
* const accountAddress = tx.address;
* ```
*
* @twfeature SmartWalletFactory
* @twfeature AccountFactory
*/
createWallet = /* @__PURE__ */ buildTransactionFunction(
createAccount = /* @__PURE__ */ buildTransactionFunction(
async (
walletAdmin: string,
accountAdmin: string,
extraData?: BytesLike,
): Promise<Transaction<TransactionResultWithAddress>> => {
if (await this.isWalletDeployed(walletAdmin, extraData)) {
throw new Error(`Wallet already deployed for admin: ${walletAdmin}`);
if (await this.isAccountDeployed(accountAdmin, extraData)) {
throw new Error(`Account already deployed for admin: ${accountAdmin}`);
}
let data: BytesLike = utils.toUtf8Bytes("");
@@ -174,7 +174,7 @@ export class SmartWalletFactory<TContract extends IAccountFactory>
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "createAccount",
args: [walletAdmin, data],
args: [accountAdmin, data],
parse: (receipt) => {
const event = this.contractWrapper.parseLogs<AccountCreatedEvent>(
"AccountCreated",
@@ -1,6 +1,6 @@
import { DetectableFeature } from "../interfaces/DetectableFeature";
import { ContractWrapper } from "./contract-wrapper";
import { FEATURE_SMART_WALLET } from "../../constants/thirdweb-features";
import { FEATURE_ACCOUNT } from "../../constants/thirdweb-features";
import { utils, BigNumber } from "ethers";
import { Transaction } from "./transactions";
@@ -18,18 +18,19 @@ import {
SignerWithRestrictions,
AccessRestrictionsInput,
AccessRestrictionsSchema,
SignerWithRestrictionsBatchInput,
} from "../../types";
import invariant from "tiny-invariant";
import { buildTransactionFunction } from "../../common/transactions";
import { SmartWalletFactory } from "./smart-wallet-factory";
import { resolveOrGenerateId } from "../../common/signature-minting";
import { AddressOrEns } from "../../schema";
import { resolveAddress } from "../../common";
import { Signer } from "ethers";
export class SmartWallet<TContract extends IAccountCore>
export class Account<TContract extends IAccountCore>
implements DetectableFeature
{
featureName = FEATURE_SMART_WALLET.name;
featureName = FEATURE_ACCOUNT.name;
private contractWrapper: ContractWrapper<IAccountCore>;
private emptyRole: string =
@@ -47,6 +48,20 @@ export class SmartWallet<TContract extends IAccountCore>
* HELPER FUNCTIONS
********************************/
private hasDuplicateSigners(
signers: SignerWithRestrictionsBatchInput,
): boolean {
const encounteredSigners = new Set();
return signers
.map((item) => item.signer)
.some((signer) => {
const isDuplicate = encounteredSigners.has(signer);
encounteredSigners.add(signer);
return isDuplicate;
});
}
/**
* Format the access restrictions for a given role
*
@@ -72,7 +87,7 @@ export class SmartWallet<TContract extends IAccountCore>
}
/**
* Generate and sign a payload to grant or revoke a signer's access to the smart wallet.
* Generate and sign a payload to grant or revoke a signer's access to the account.
*
* @param signer - The address of the signer
* @param roleAction - The address of the signer
@@ -122,12 +137,12 @@ export class SmartWallet<TContract extends IAccountCore>
}
/**
* Get the factory contract which deployed the smart wallet
* Get the factory contract which deployed the account
*
* @returns Returns the address of the factory
*
*/
private async getFactory(): Promise<SmartWalletFactory<IAccountFactory>> {
private async getFactory(): Promise<ContractWrapper<IAccountFactory>> {
// Get factory.
const chainId = await this.contractWrapper.getChainID();
const factoryAddress = await this.getFactoryAddress();
@@ -137,7 +152,8 @@ export class SmartWallet<TContract extends IAccountCore>
IAccountFactoryAbi,
this.contractWrapper.options,
);
return new SmartWalletFactory(wrapper);
wrapper.updateSignerOrProvider(this.contractWrapper.getSigner() as Signer);
return wrapper;
}
/*********************************
@@ -145,16 +161,35 @@ export class SmartWallet<TContract extends IAccountCore>
********************************/
/**
* Get the restrictions under which a given signer can use the smart wallet.
* Get whether a signer is an admin on the account.
*
* @example
* ```javascript
* const restrictionsForSigner = await contract.smartWallet.getAccessRestrictions(signer);
* const isAdmin = await contract.account.isAdmin(signer);
* ```
* @param signer - The address of a signer of the smart wallet.
* @returns the restrictions under which a given signer can use the smart wallet.
* @param signer - The address of a signer of the account.
* @returns whether a signer is an admin on the account.
*
* @twfeature SmartWallet
* @twfeature Account
*/
public async isAdmin(signerAddress: AddressOrEns): Promise<boolean> {
const resolvedSignerAddress = await resolveAddress(signerAddress);
return await this.contractWrapper.readContract.isAdmin(
resolvedSignerAddress,
);
}
/**
* Get the restrictions under which a given signer can use the account.
*
* @example
* ```javascript
* const restrictionsForSigner = await contract.account.getAccessRestrictions(signer);
* ```
* @param signer - The address of a signer of the account.
* @returns the restrictions under which a given signer can use the account.
*
* @twfeature Account
*/
public async getAccessRestrictions(
signerAddress: AddressOrEns,
@@ -168,35 +203,36 @@ export class SmartWallet<TContract extends IAccountCore>
}
/**
* Get the address of the EIP-4337 factory contract which deployed the smart wallet
* Get the deployer of the account
* @remarks Get the address of the EIP-4337 factory contract which deployed the account
*
* @example
* ```javascript
* const factoryAddress = await contract.smartWallet.getFactoryAddress();
* const factoryAddress = await contract.account.getFactoryAddress();
* ```
* @returns the address of the factory which deployed the smart wallet.
* @returns the address of the factory which deployed the account.
*
* @twfeature SmartWallet
* @twfeature Account
*/
public async getFactoryAddress(): Promise<string> {
return this.contractWrapper.readContract.factory();
}
/**
* Get all signers (admin or non-admin) of the smart wallet, along with their access restrictions.
* Get all signers with their access restrictions
*
* @example
* ```javascript
* const allSigners = await contract.smartWallet.getSignersWithRestrictions();
* const allSigners = await contract.account.getSignersWithRestrictions();
* ```
* @returns all signers (admin or non-admin) of the smart wallet, along with their access restrictions.
* @returns all signers (admin or non-admin) of the account, along with their access restrictions.
*
* @twfeature SmartWallet
* @twfeature Account
*/
public async getSignersWithRestrictions(): Promise<SignerWithRestrictions[]> {
// Get all associated signers.
const factory = await this.getFactory();
const signers: string[] = await factory.getAssociatedSigners(
const contract = await this.getFactory();
const signers: string[] = await contract.readContract.getSignersOfAccount(
this.getAddress(),
);
@@ -214,19 +250,19 @@ export class SmartWallet<TContract extends IAccountCore>
********************************/
/**
* Grant an address admin access to the smart wallet.
* Grant an address admin access to the account
*
* @remarks Grants an address admin access to the smart wallet. The admin will have complete authority over the smart wallet.
* @remarks Grants an address admin access to the account. The admin will have complete authority over the account.
*
* @param signer - The address to be granted admin access to the smart wallet.
* @param signer - The address to be granted admin access to the account.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.grantAdminAccess(signer);
* const tx = await contract.account.grantAdminAccess(signer);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
grantAdminAccess = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
@@ -240,19 +276,19 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Revoke an address' admin access to the smart wallet.
* Revoke an address' admin access to the account
*
* @remarks Revokes an address' admin access to the smart wallet.
* @remarks Revokes an address' admin access to the account.
*
* @param signer - The address of an admin of the smart wallet.
* @param signer - The address of an admin of the account.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.revokeAdminAccess(signer);
* const tx = await contract.account.revokeAdminAccess(signer);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
revokeAdminAccess = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
@@ -266,20 +302,20 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Grant an address access to the smart wallet with certain restrictions.
* Grant an address access to the account with restrictions
*
* @remarks Grants an address access to the smart wallet with certain restrictions.
* @remarks Grants an address access to the account with certain restrictions.
*
* @param signer - The address to be granted access to the smart wallet.
* @param restrictions - The restrictions to be applied to the signer's use of the smart wallet.
* @param signer - The address to be granted access to the account.
* @param restrictions - The restrictions to be applied to the signer's use of the account.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.grantAccess(signer, restrictions);
* const tx = await contract.account.grantAccess(signer, restrictions);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
grantAccess = /* @__PURE__ */ buildTransactionFunction(
async (
@@ -312,8 +348,9 @@ export class SmartWallet<TContract extends IAccountCore>
const roleRestrictions: IAccountPermissions.RoleRestrictionsStruct = {
role,
approvedTargets: parsedRestrictions.approvedCallTargets,
maxValuePerTransaction:
maxValuePerTransaction: utils.parseEther(
parsedRestrictions.nativeTokenLimitPerTransaction,
),
startTimestamp: parsedRestrictions.startDate,
endTimestamp: parsedRestrictions.expirationDate,
};
@@ -358,20 +395,20 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Approve an address as a call target for a given signer on the smart wallet.
* Approve an address as a call target for a given signer on the account
*
* @remarks Approves an address as a call target for a given signer on the smart wallet.
* @remarks Approves an address as a call target for a given signer on the account.
*
* @param signer - A signer with restricted access to the smart wallet.
* @param signer - A signer with restricted access to the account.
* @param target - The address to approve as a call target for the signer.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.approveTargetForSigner(signer, target);
* const tx = await contract.account.approveTargetForSigner(signer, target);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
approveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
async (
@@ -407,20 +444,20 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Disapprove an address as a call target for a given signer on the smart wallet.
* Disapprove an address as a call target for a given signer on the account
*
* @remarks Disapprove an address as a call target for a given signer on the smart wallet.
* @remarks Disapprove an address as a call target for a given signer on the account.
*
* @param signer - A signer with restricted access to the smart wallet.
* @param signer - A signer with restricted access to the account.
* @param target - The address to disapprove as a call target for the signer.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.disapproveTargetForSigner(signer, target);
* const tx = await contract.account.disapproveTargetForSigner(signer, target);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
disapproveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
async (
@@ -459,20 +496,20 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Update an address' access to the smart wallet.
* Update the restrictions of an address for using the account
*
* @remarks Updates an address' access to the smart wallet.
* @remarks Updates an address' access to the account.
*
* @param signer - The address whose access to the smart wallet is to be updated.
* @param restrictions - The restrictions to be applied to the signer's use of the smart wallet.
* @param signer - The address whose access to the account is to be updated.
* @param restrictions - The restrictions to be applied to the signer's use of the account.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.updateAccess(signer, restrictions);
* const tx = await contract.account.updateAccess(signer, restrictions);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
updateAccess = /* @__PURE__ */ buildTransactionFunction(
async (
@@ -497,8 +534,9 @@ export class SmartWallet<TContract extends IAccountCore>
const roleRestrictions: IAccountPermissions.RoleRestrictionsStruct = {
role: currentRole,
approvedTargets: parsedRestrictions.approvedCallTargets,
maxValuePerTransaction:
maxValuePerTransaction: utils.parseEther(
parsedRestrictions.nativeTokenLimitPerTransaction,
),
startTimestamp: parsedRestrictions.startDate,
endTimestamp: parsedRestrictions.expirationDate,
};
@@ -512,19 +550,19 @@ export class SmartWallet<TContract extends IAccountCore>
);
/**
* Revoke an address' access to the smart wallet.
* Revoke a scoped access address to the account
*
* @remarks Revokes an address' access to the smart wallet.
* @remarks Revokes an address' access to the account.
*
* @param signer - The address whose access to the smart wallet is to be revoked.
* @param signer - The address whose access to the account is to be revoked.
*
* @example
* ```javascript
* const tx = await contract.smartWallet.revokeAccess(signer);
* const tx = await contract.account.revokeAccess(signer);
* const receipt = tx.receipt();
* ```
*
* @twfeature SmartWallet
* @twfeature Account
*/
revokeAccess = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
@@ -560,4 +598,204 @@ export class SmartWallet<TContract extends IAccountCore>
});
},
);
/**
* Set the account's entire snapshot of permissions
*
* @remarks Sets the account's entire snapshot of permissions.
*
* @param permissionsSnapshot - the snapshot to set as the account's entire permission snapshot.
*
* @example
* ```javascript
* const tx = await contract.account.setAccess(permissionsSnapshot);
* const receipt = tx.receipt();
* ```
*
* @twfeature Account
*/
setAccess = /* @__PURE__ */ buildTransactionFunction(
async (
permissionsSnapshot: SignerWithRestrictionsBatchInput,
): Promise<Transaction> => {
/**
* All cases
*
* - Add new admin :check:
* - Remove current admin :check:
* - Add new scoped :check:
* - Remove current scoped :check:
* - Update current scoped :check:
* - Current admin -> new scoped :check:
* - Current scoped -> new admin :check:
**/
// No duplicate signers in input!
if (this.hasDuplicateSigners(permissionsSnapshot)) {
throw new Error("Duplicate signers found in input.");
}
const currentPermissionsSnapshot =
await this.getSignersWithRestrictions();
// Performing a multicall.
const encoded: string[] = [];
// First make all calls related to admin access.
const allCurrentAdmins = currentPermissionsSnapshot
.filter((result) => result.isAdmin)
.map((result) => result.signer);
const allNewAdmins = permissionsSnapshot
.filter((result) => result.isAdmin)
.map((result) => result.signer);
// All remove-admin actions.
for (const currentAdmin of allCurrentAdmins) {
if (!allNewAdmins.includes(currentAdmin)) {
encoded.push(
this.contractWrapper.readContract.interface.encodeFunctionData(
"setAdmin",
[currentAdmin, false],
),
);
}
}
// All add-admin actions.
const toRemoveAsScoped: Record<string, string> = {};
for (const newAdmin of allNewAdmins) {
if (!allCurrentAdmins.includes(newAdmin)) {
const data =
this.contractWrapper.readContract.interface.encodeFunctionData(
"setAdmin",
[newAdmin, true],
);
// If the new admin is already a scoped account, we need to remove them as a scoped account first.
const currentRole = (
await this.contractWrapper.readContract.getRoleRestrictionsForAccount(
newAdmin,
)
).role;
if (currentRole === this.emptyRole) {
encoded.push(data);
} else {
toRemoveAsScoped[newAdmin] = data;
}
}
}
// All scoped actions.
const allCurrentScoped = currentPermissionsSnapshot
.filter((result) => !result.isAdmin)
.map((result) => result.signer);
const allNewScoped = permissionsSnapshot
.filter((result) => !result.isAdmin)
.map((result) => result.signer);
// All remove-scoped actions.
const newAdminsToRemoveAsScoped = Object.keys(toRemoveAsScoped);
for (const currentScoped of allCurrentScoped) {
if (!allNewScoped.includes(currentScoped)) {
const { payload, signature } = await this.generatePayload(
currentScoped,
RoleAction.REVOKE,
);
encoded.push(
this.contractWrapper.readContract.interface.encodeFunctionData(
"changeRole",
[payload, signature],
),
);
if (newAdminsToRemoveAsScoped.includes(currentScoped)) {
encoded.push(toRemoveAsScoped[currentScoped]);
}
}
}
// All add-scoped actions.
for (const newScoped of allNewScoped) {
if (!allCurrentScoped.includes(newScoped)) {
// Derive role for target signer.
const role = utils.solidityKeccak256(["string"], [newScoped]);
const parsedRestrictions = await AccessRestrictionsSchema.parseAsync(
permissionsSnapshot.find((result) => result.signer === newScoped)
?.restrictions,
);
// Get role restrictions struct.
const roleRestrictions: IAccountPermissions.RoleRestrictionsStruct = {
role,
approvedTargets: parsedRestrictions.approvedCallTargets,
maxValuePerTransaction: utils.parseEther(
parsedRestrictions.nativeTokenLimitPerTransaction,
),
startTimestamp: parsedRestrictions.startDate,
endTimestamp: parsedRestrictions.expirationDate,
};
encoded.push(
this.contractWrapper.readContract.interface.encodeFunctionData(
"setRoleRestrictions",
[roleRestrictions],
),
);
const { payload, signature } = await this.generatePayload(
newScoped,
RoleAction.GRANT,
);
encoded.push(
this.contractWrapper.readContract.interface.encodeFunctionData(
"changeRole",
[payload, signature],
),
);
}
}
// All update-scoped actions.
for (const currentScoped of allCurrentScoped) {
if (allNewScoped.includes(currentScoped)) {
// Derive role for target signer.
const role = utils.solidityKeccak256(["string"], [currentScoped]);
const parsedRestrictions = await AccessRestrictionsSchema.parseAsync(
permissionsSnapshot.find(
(result) => result.signer === currentScoped,
)?.restrictions,
);
// Get role restrictions struct.
const roleRestrictions: IAccountPermissions.RoleRestrictionsStruct = {
role,
approvedTargets: parsedRestrictions.approvedCallTargets,
maxValuePerTransaction: utils.parseEther(
parsedRestrictions.nativeTokenLimitPerTransaction,
),
startTimestamp: parsedRestrictions.startDate,
endTimestamp: parsedRestrictions.expirationDate,
};
encoded.push(
this.contractWrapper.readContract.interface.encodeFunctionData(
"setRoleRestrictions",
[roleRestrictions],
),
);
}
}
// Perform multicall
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "multicall",
args: [encoded],
});
},
);
}
@@ -1,8 +1,7 @@
import { EventType } from "../../constants/events";
import type { ContractEvent, EventQueryOptions } from "../../types/events";
import { ContractWrapper } from "./contract-wrapper";
import type { BaseContract, Event, providers } from "ethers";
import type { EventFragment } from "ethers/lib/utils.js";
import type { BaseContract, Event, providers, utils } from "ethers";
import type { EventEmitter } from "eventemitter3";
/**
@@ -325,7 +324,7 @@ export class ContractEvents<TContract extends BaseContract> {
}
private toContractEvent<TEvent extends Record<string, any>>(
event: EventFragment,
event: utils.EventFragment,
args: ReadonlyArray<any>,
rawLog: providers.Log,
): ContractEvent<TEvent> {
@@ -44,7 +44,6 @@ import {
type Signer,
constants,
} from "ethers";
import { ConnectionInfo } from "ethers/lib/utils.js";
import invariant from "tiny-invariant";
import { CONTRACT_ADDRESSES } from "../../constants/addresses/CONTRACT_ADDRESSES";
import { getContractAddressByChainId } from "../../constants/addresses/getContractAddressByChainId";
@@ -471,7 +470,7 @@ export class ContractWrapper<
callOverrides: CallOverrides,
) {
const provider = this.getProvider() as providers.Provider & {
connection?: ConnectionInfo;
connection?: utils.ConnectionInfo;
};
// Get metadata for transaction to populate into error
+2 -2
View File
@@ -50,5 +50,5 @@ export * from "./contract-published-metadata";
export * from "./contract-owner";
export * from "./transactions";
export * from "./contract-appuri";
export * from "./smart-wallet";
export * from "./smart-wallet-factory";
export * from "./account";
export * from "./account-factory";
@@ -30,8 +30,6 @@ import {
constants,
} from "ethers";
import { BigNumber } from "ethers";
import { FormatTypes } from "ethers/lib/utils.js";
import type { ConnectionInfo } from "ethers/lib/utils.js";
import invariant from "tiny-invariant";
import EventEmitter from "eventemitter3";
import type { DeployEvents } from "../../types/deploy";
@@ -494,7 +492,7 @@ export class Transaction<
try {
// for dynamic contracts, add 30% to the gas limit to account for multiple delegate calls
const abi = JSON.parse(
this.contract.interface.format(FormatTypes.json) as string,
this.contract.interface.format("json") as string,
);
if (isRouterContract(abi)) {
overrides.gasLimit = overrides.gasLimit.mul(110).div(100);
@@ -663,7 +661,7 @@ export class Transaction<
*/
private async transactionError(error: any) {
const provider = this.provider as providers.Provider & {
connection?: ConnectionInfo;
connection?: utils.ConnectionInfo;
};
// Get metadata for transaction to populate into error
@@ -849,7 +847,7 @@ export class DeployTransaction extends TransactionContext {
*/
private async deployError(error: any) {
const provider = this.provider as providers.Provider & {
connection?: ConnectionInfo;
connection?: utils.ConnectionInfo;
};
// Get metadata for transaction to populate into error
@@ -24,7 +24,7 @@ import {
} from "ethers";
import EventEmitter from "eventemitter3";
import invariant from "tiny-invariant";
import { BlockTag } from "@ethersproject/abstract-provider";
import type { BlockTag } from "@ethersproject/abstract-provider";
import { fetchCurrencyValue } from "../../common/currency/fetchCurrencyValue";
import { isNativeToken } from "../../common/currency/isNativeToken";
import { normalizePriceValue } from "../../common/currency/normalizePriceValue";
@@ -32,6 +32,17 @@ export type SignerWithRestrictions = {
restrictions: AccessRestrictions;
}
export const SignerWithRestrictionsSchema = /* @__PURE__ */ z.object({
signer: AddressOrEnsSchema,
isAdmin: /* @__PURE__ */ z.boolean(),
restrictions: AccessRestrictionsSchema,
});
export type SignerWithRestrictionsInput = z.input<typeof SignerWithRestrictionsSchema>;
export const SignerWithRestrictionsBatchSchema = /* @__PURE__ */ z.array(SignerWithRestrictionsSchema);
export type SignerWithRestrictionsBatchInput = z.input<typeof SignerWithRestrictionsBatchSchema>;
export type SignedAccountPermissionsPayload = {
payload: IAccountPermissions.RoleRequestStruct;
signature: BytesLike;
+1 -1
View File
@@ -12,4 +12,4 @@ export * from "./multiwrap";
export * from "./registry";
export * from "./transactions";
export * from "./contract";
export * from "./smart-wallet";
export * from "./account";
+821
View File
@@ -0,0 +1,821 @@
import { sdk, signers } from "./before-setup";
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { assert, expect } from "chai";
import {
SignerWithRestrictions,
AccessRestrictions,
Account,
AccountFactory,
SignerWithRestrictionsInput,
SignerWithRestrictionsBatchInput,
} from "../../src/evm";
import { ContractFactory, utils } from "ethers";
import EntrypointArtifact from "./mock/EntryPoint.json";
import AccountFactoryArtifact from "./mock/AccountFactory.json";
// Target ABIs
import IAccountCoreAbi from "@thirdweb-dev/contracts-js/dist/abis/IAccountCore.json";
import {
deployContractAndUploadMetadata,
mockUploadContractMetadata,
} from "./utils";
import { IAccountCore, IAccountFactory } from "@thirdweb-dev/contracts-js";
global.fetch = /* @__PURE__ */ require("cross-fetch");
/* @__PURE__ */ describe("Accounts with account factory", function () {
let accountFactory: AccountFactory<IAccountFactory>;
let account: Account<IAccountCore>;
let adminWallet: SignerWithAddress;
let signer1Wallet: SignerWithAddress;
let signer2Wallet: SignerWithAddress;
let signer3Wallet: SignerWithAddress;
let signer4Wallet: SignerWithAddress;
let signer5Wallet: SignerWithAddress;
let signer6Wallet: SignerWithAddress;
let signer7Wallet: SignerWithAddress;
let signer8Wallet: SignerWithAddress;
let signer9Wallet: SignerWithAddress;
before(async () => {
[
adminWallet,
signer1Wallet,
signer2Wallet,
signer3Wallet,
signer4Wallet,
signer5Wallet,
signer6Wallet,
signer7Wallet,
signer8Wallet,
signer9Wallet,
] = signers;
});
beforeEach(async () => {
const entrypoint = await new ContractFactory(
EntrypointArtifact.abi,
EntrypointArtifact.bytecode.object,
adminWallet,
).deploy();
const factoryAddress = await deployContractAndUploadMetadata(
AccountFactoryArtifact.abi,
AccountFactoryArtifact.bytecode.object,
adminWallet,
[entrypoint.address],
);
accountFactory = (await sdk.getContract(factoryAddress)).accountFactory;
});
describe("Test: AccountFactory actions", () => {
it("Should create a new account for the admin", async () => {
// Check before creating a account for `adminWallet`
const isDeployed = await accountFactory.isAccountDeployed(
adminWallet.address,
);
assert.isFalse(
isDeployed,
"Account is should not be deployed for admin.",
);
const predictedAccountAddress: string =
await accountFactory.predictAccountAddress(adminWallet.address);
const allAccountsBefore = (await accountFactory.getAllAccounts()).map(
(wallet) => account.account,
);
assert.isFalse(
allAccountsBefore.includes(predictedAccountAddress),
"Account should not be deployed for admin already.",
);
assert.isTrue(
(await accountFactory.getAssociatedSigners(predictedAccountAddress))
.length === 0,
"Wallet should have no associated signers.",
);
assert.isTrue(
(await accountFactory.getAssociatedAccounts(adminWallet.address))
.length === 0,
"Signer should have no associated accounts.",
);
// Create account for `adminWallet`
const tx = await accountFactory.createAccount(adminWallet.address);
// Checks after creating a account for `adminWallet`
assert.strictEqual(
utils.getAddress(tx.address),
utils.getAddress(predictedAccountAddress),
"Account address should match predicted address.",
);
assert.isTrue(
await accountFactory.isAccountDeployed(adminWallet.address),
"Account is should be deployed for admin.",
);
const allAccountsAfter = await accountFactory.getAllAccounts();
assert.isTrue(
allAccountsAfter.length === 1,
"Only one account should be created on the factory.",
);
const { account, admin } = allAccountsAfter.filter(
(wallet) =>
utils.getAddress(wallet.account) ===
utils.getAddress(predictedAccountAddress),
)[0];
assert.strictEqual(
utils.getAddress(account),
utils.getAddress(predictedAccountAddress),
"Stored account address should match predicted address.",
);
assert.strictEqual(
utils.getAddress(admin),
utils.getAddress(adminWallet.address),
"Correct admin for account.",
);
const associatedAccounts = await accountFactory.getAssociatedAccounts(
admin,
);
const associatedSigner = await accountFactory.getAssociatedSigners(
account,
);
assert.isTrue(
associatedAccounts.length === 1,
"Wallet should have only the admin as an associated signer.",
);
assert.strictEqual(
utils.getAddress(account),
utils.getAddress(associatedAccounts[0]),
"Wallet should have only the admin as an associated signer.",
);
assert.isTrue(
associatedSigner.length === 1,
"Signer should have only the created account as an associated account.",
);
assert.strictEqual(
utils.getAddress(admin),
utils.getAddress(associatedSigner[0]),
"Signer should have only the created account as an associated account.",
);
});
it("Should throw if creating account for an admin who already has one", async () => {
// Create a account for admin
await accountFactory.createAccount(adminWallet.address);
// Try creating another account for the same admin
try {
await accountFactory.createAccount(adminWallet.address);
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(
`Account already deployed for admin: ${adminWallet.address}`,
);
}
});
});
describe("Test: Account actions", () => {
beforeEach(async () => {
// Create a account for admin
const tx = await accountFactory.createAccount(adminWallet.address);
const accountAddress = tx.address;
sdk.updateSignerOrProvider(adminWallet);
await mockUploadContractMetadata(
"smart-wallet",
accountAddress,
IAccountCoreAbi,
);
account = (await sdk.getContract(accountAddress)).account;
});
it("Should be able to add another admin to the account.", async () => {
assert.isFalse(
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
await account.grantAdminAccess(signer1Wallet.address);
assert.isTrue(
await account.isAdmin(signer1Wallet.address),
"New signer1 should be an admin on the account.",
);
const isAdmin = (
(await account.getSignersWithRestrictions()).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
) as SignerWithRestrictions
).isAdmin;
assert.isTrue(isAdmin, "New signer1 should be an admin on the account.");
assert.isTrue(
(
await accountFactory.getAssociatedSigners(account.getAddress())
).includes(signer1Wallet.address),
"New signer1 is an associated signer of the account.",
);
assert.isTrue(
(
await accountFactory.getAssociatedAccounts(signer1Wallet.address)
).includes(account.getAddress()),
"Wallet is an associated account of the signer.",
);
});
it("Should be able to remove an admin from the account.", async () => {
await account.grantAdminAccess(signer1Wallet.address);
assert.isTrue(
await account.isAdmin(signer1Wallet.address),
"New signer1 should be an admin on the account.",
);
await account.revokeAdminAccess(signer1Wallet.address);
assert.isFalse(
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
assert.isFalse(
(await account.getSignersWithRestrictions())
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
assert.isFalse(
(
await accountFactory.getAssociatedSigners(account.getAddress())
).includes(signer1Wallet.address),
"New signer1 is not an associated signer of the account.",
);
assert.isFalse(
(
await accountFactory.getAssociatedAccounts(signer1Wallet.address)
).includes(account.getAddress()),
"Wallet is not an associated account of the signer.",
);
});
it("Should be able to grant restricted access to a new signer.", async () => {
const signersWithRestrictions =
await account.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not already have access to the account.",
);
// Grant access
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
assert.isFalse(
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
const newSignerWithRestrictions =
await account.getSignersWithRestrictions();
const restrictions = newSignerWithRestrictions.find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.isTrue(
newSignerWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should be a signer on the account.",
);
assert.strictEqual(
restrictions.nativeTokenLimitPerTransaction.toString(),
utils.parseEther("1").toString(),
"New signer1 should have the expected native token limit.",
);
assert.strictEqual(
restrictions.approvedCallTargets.length,
1,
"New signer1 should have one approved call targets.",
);
assert.strictEqual(
restrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
assert.isTrue(
(
await accountFactory.getAssociatedSigners(account.getAddress())
).includes(signer1Wallet.address),
"New signer1 is an associated signer of the account.",
);
assert.isTrue(
(
await accountFactory.getAssociatedAccounts(signer1Wallet.address)
).includes(account.getAddress()),
"Wallet is an associated account of the signer.",
);
});
it("Should not be able to grant restricted access to a signer who already has access.", async () => {
// Grant access to signer1
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
// Try granting access to signer1 again
try {
await account.grantAccess(signer1Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(`Signer already has access`);
}
});
it("Should be able to revoke restricted access from an authorized signer.", async () => {
// Grant access to signer1
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
// Revoke access
await account.revokeAccess(signer1Wallet.address);
const signersWithRestrictions =
await account.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not have access to the account.",
);
assert.isFalse(
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
assert.isFalse(
(
await accountFactory.getAssociatedSigners(account.getAddress())
).includes(signer1Wallet.address),
"New signer1 is not an associated signer of the account.",
);
assert.isFalse(
(
await accountFactory.getAssociatedAccounts(signer1Wallet.address)
).includes(account.getAddress()),
"Wallet is not an associated account of the signer.",
);
});
it("Should not be able to revoke restricted access from a signer who doesn't have access.", async () => {
const signersWithRestrictions =
await account.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not already have access to the account.",
);
// Try revoking access from signer1
try {
await account.revokeAccess(signer1Wallet.address);
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(`Signer does not have any access`);
}
});
it("Should be able to update access of an authorized signer.", async () => {
// Grant access
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
assert.isFalse(
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
const signerWithRestrictions = await account.getSignersWithRestrictions();
const restrictions = signerWithRestrictions.find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.isTrue(
signerWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should be a signer on the account.",
);
assert.strictEqual(
restrictions.nativeTokenLimitPerTransaction.toString(),
utils.parseEther("1").toString(),
"New signer1 should have the expected native token limit.",
);
assert.strictEqual(
restrictions.approvedCallTargets.length,
1,
"New signer1 should have one approved call targets.",
);
assert.strictEqual(
restrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
// Update access
await account.updateAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "3",
approvedCallTargets: [signer2Wallet.address],
});
assert.isFalse(
await account.isAdmin(signer1Wallet.address),
"New signer1 should not be an admin on the account.",
);
const newSignerWithRestrictions =
await account.getSignersWithRestrictions();
const newRestrictions = newSignerWithRestrictions.find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.isTrue(
newSignerWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer1 should be a signer on the account.",
);
assert.strictEqual(
newRestrictions.nativeTokenLimitPerTransaction.toString(),
utils.parseEther("3").toString(),
"New signer1 should have the expected native token limit.",
);
assert.strictEqual(
newRestrictions.approvedCallTargets.length,
1,
"New signer1 should have one approved call targets.",
);
assert.strictEqual(
newRestrictions.approvedCallTargets[0],
signer2Wallet.address,
"New signer1 should have the expected approved call targets.",
);
});
it("Should not be able to update access of a signer who doesn't have access.", async () => {
const signersWithRestrictions =
await account.getSignersWithRestrictions();
assert.isFalse(
signersWithRestrictions
.map((result) => utils.getAddress(result.signer))
.includes(signer1Wallet.address),
"New signer should not already have access to the account.",
);
// Try updating access of signer1
try {
await account.updateAccess(signer1Wallet.address, {
approvedCallTargets: [signer2Wallet.address],
});
expect.fail();
} catch (err: any) {
expect(err.message).to.equal(`Signer does not have any access`);
}
});
it("Should be able to append an approved target for an authorized signer.", async () => {
// Grant access
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address],
});
const restrictions = (await account.getSignersWithRestrictions()).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.strictEqual(
restrictions.approvedCallTargets.length,
1,
"New signer1 should have one approved call targets.",
);
assert.strictEqual(
restrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
// Append approved target
await account.approveTargetForSigner(
signer1Wallet.address,
signer2Wallet.address,
);
const newRestrictions = (await account.getSignersWithRestrictions()).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.strictEqual(
newRestrictions.approvedCallTargets.length,
2,
"New signer1 should have two approved call targets.",
);
assert.strictEqual(
newRestrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
assert.strictEqual(
newRestrictions.approvedCallTargets[1],
signer2Wallet.address,
"New signer1 should have the expected approved call targets.",
);
});
it("Should be able to remove an approved target for an authorized signer.", async () => {
// Grant access
await account.grantAccess(signer1Wallet.address, {
nativeTokenLimitPerTransaction: "1",
approvedCallTargets: [adminWallet.address, signer2Wallet.address],
});
const restrictions = (await account.getSignersWithRestrictions()).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.strictEqual(
restrictions.approvedCallTargets.length,
2,
"New signer1 should have two approved call targets.",
);
assert.strictEqual(
restrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
assert.strictEqual(
restrictions.approvedCallTargets[1],
signer2Wallet.address,
"New signer1 should have the expected approved call targets.",
);
// Remove approved target
await account.disapproveTargetForSigner(
signer1Wallet.address,
signer2Wallet.address,
);
const newRestrictions = (await account.getSignersWithRestrictions()).find(
(result) =>
utils.getAddress(result.signer) ===
utils.getAddress(signer1Wallet.address),
)?.restrictions as AccessRestrictions;
assert.strictEqual(
newRestrictions.approvedCallTargets.length,
1,
"New signer1 should have one approved call targets.",
);
assert.strictEqual(
newRestrictions.approvedCallTargets[0],
adminWallet.address,
"New signer1 should have the expected approved call targets.",
);
});
it("Should be able to set access for the account in a batch.", async () => {
/**
* All cases to test
*
* - 1. Adding a new admin
* - 2. Removing existing admin
* - 3. Adding a new scoped signer
* - 4. Removing an existing scoped signer
* - 5. Updating the restrictions of an existing scoped signer
* - 6. Existing admin -> new scoped signer (demote)
* - 7. Existing scoped signer -> new admin :check: (promote)
**/
const signersWithRestrictions: SignerWithRestrictionsBatchInput = [];
// Setup
const restrictionsForAdmin: SignerWithRestrictionsInput = {
signer: adminWallet.address,
isAdmin: true,
restrictions: { approvedCallTargets: [] },
};
signersWithRestrictions.push(restrictionsForAdmin);
// Adding a new admin: Signer3
const restrictionsSigner3: SignerWithRestrictionsInput = {
signer: signer3Wallet.address,
isAdmin: true,
restrictions: { approvedCallTargets: [] },
};
signersWithRestrictions.push(restrictionsSigner3);
// Removing existing admin
await account.grantAdminAccess(signer4Wallet.address);
const restrictionsSigner4: SignerWithRestrictionsInput = {
signer: signer4Wallet.address,
isAdmin: false,
restrictions: { approvedCallTargets: [] },
};
signersWithRestrictions.push(restrictionsSigner4);
// Adding a new scoped signer
const restrictionsSigner5: SignerWithRestrictionsInput = {
signer: signer5Wallet.address,
isAdmin: false,
restrictions: { approvedCallTargets: [adminWallet.address] },
};
signersWithRestrictions.push(restrictionsSigner5);
// Removing an existing scoped signer
await account.grantAccess(signer6Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
const restrictionsSigner6: SignerWithRestrictionsInput = {
signer: signer6Wallet.address,
isAdmin: false,
restrictions: { approvedCallTargets: [] },
};
signersWithRestrictions.push(restrictionsSigner6);
// Updating the restrictions of an existing scoped signer
await account.grantAccess(signer7Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
const restrictionsSigner7: SignerWithRestrictionsInput = {
signer: signer7Wallet.address,
isAdmin: false,
restrictions: { approvedCallTargets: [signer1Wallet.address] },
};
signersWithRestrictions.push(restrictionsSigner7);
// Existing admin -> new scoped signer (demote)
await account.grantAdminAccess(signer8Wallet.address);
const restrictionsSigner8: SignerWithRestrictionsInput = {
signer: signer8Wallet.address,
isAdmin: false,
restrictions: { approvedCallTargets: [adminWallet.address] },
};
signersWithRestrictions.push(restrictionsSigner8);
// Existing scoped signer -> new admin (promote)
await account.grantAccess(signer9Wallet.address, {
approvedCallTargets: [adminWallet.address],
});
const restrictionsSigner9: SignerWithRestrictionsInput = {
signer: signer9Wallet.address,
isAdmin: true,
restrictions: { approvedCallTargets: [] },
};
signersWithRestrictions.push(restrictionsSigner9);
// Set access in batch
await account.setAccess(signersWithRestrictions);
// Now checking if permissions are set correctly
const signersWithRestrictionsAfter =
await account.getSignersWithRestrictions();
for (const restrictions of signersWithRestrictionsAfter) {
switch (restrictions.signer) {
// `adminWallet` should be an admin
case adminWallet.address:
assert.isTrue(
restrictions.isAdmin,
"Admin account should be an admin.",
);
break;
// `signer3Wallet` should be an admin
case signer3Wallet.address:
assert.isTrue(
restrictions.isAdmin,
"Signer3 account should be an admin.",
);
break;
// `signer4Wallet` should not be admin or scoped signer
case signer4Wallet.address:
assert.isFalse(
restrictions.isAdmin,
"Signer4 account should not be an admin.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
0,
"Signer4 account should not have any approved call targets.",
);
break;
// `signer5Wallet` should be a scoped signer
case signer5Wallet.address:
assert.isFalse(
restrictions.isAdmin,
"Signer5 account should not be an admin.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
1,
"Signer5 account should have one approved call target.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets[0],
adminWallet.address,
"Signer5 account should have the expected approved call targets.",
);
break;
// `signer6Wallet` should not be admin or scoped signer
case signer6Wallet.address:
assert.isFalse(
restrictions.isAdmin,
"Signer6 account should not be an admin.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
0,
"Signer6 account should not have any approved call targets.",
);
break;
// `signer7Wallet` should be a scoped signer with its updated restrictions
case signer7Wallet.address:
assert.isFalse(
restrictions.isAdmin,
"Signer7 account should not be an admin.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
1,
"Signer7 account should have one approved call target.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets[0],
signer1Wallet.address,
"Signer7 account should have the expected approved call targets.",
);
break;
// `signer8Wallet` should be a scoped signer
case signer8Wallet.address:
assert.isFalse(
restrictions.isAdmin,
"Signer8 account should not be an admin.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets.length,
1,
"Signer8 account should have one approved call target.",
);
assert.strictEqual(
restrictions.restrictions.approvedCallTargets[0],
adminWallet.address,
"Signer8 account should have the expected approved call targets.",
);
break;
// `signer9Wallet` should be an admin
case signer9Wallet.address:
assert.isTrue(
restrictions.isAdmin,
"Signer9 account should be an admin.",
);
break;
default:
assert.fail("Unexpected signer.");
}
}
});
});
});
+3
View File
@@ -82,6 +82,7 @@ let signers: SignerWithAddress[];
let storage: ThirdwebStorage;
let implementations: { [key in ContractType]?: string };
let mock_weth_address: string;
let thirdwebFactory: TWFactory;
const fastForwardTime = async (timeInSeconds: number): Promise<void> => {
const now = Math.floor(Date.now() / 1000);
@@ -133,6 +134,7 @@ export const mochaHooks = {
.connect(signer)
.deploy(trustedForwarderAddress, registry.address)) as TWFactory;
thirdwebFactory = await thirdwebFactoryDeployer.deployed();
const deployTxFactory = thirdwebFactoryDeployer.deployTransaction;
await deployTxFactory.wait();
const thirdwebRegistryAddress = await thirdwebFactoryDeployer.registry();
@@ -391,4 +393,5 @@ export {
storage,
implementations,
hardhatEthers,
thirdwebFactory,
};
+2 -2
View File
@@ -1,12 +1,12 @@
import { ThirdwebSDK } from "../../src/evm";
import { expectError, sdk } from "./before-setup";
import { expect } from "chai";
import { ethers } from "ethers";
import { Wallet } from "ethers";
describe("Error Handling", async () => {
it("should throw proper error on account with no balance", async () => {
const newSdk = ThirdwebSDK.fromSigner(
ethers.Wallet.createRandom().connect(sdk.getProvider()),
Wallet.createRandom().connect(sdk.getProvider()),
);
try {
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,85 @@
// from SplitWallet published at: ipfs://Qmc5cQotuEnCo71ZztJk69wc8kuKbGmuCARL2FyAojoi9r/
const abi = [
{
inputs: [],
stateMutability: "nonpayable",
type: "constructor",
},
{
inputs: [],
name: "Unauthorized",
type: "error",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "split",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "ReceiveETH",
type: "event",
},
{
inputs: [
{
internalType: "contract ERC20",
name: "token",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "sendERC20ToMain",
outputs: [],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
],
name: "sendETHToMain",
outputs: [],
stateMutability: "payable",
type: "function",
},
{
inputs: [],
name: "splitMain",
outputs: [
{
internalType: "contract ISplitMain",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
];
const bytecode =
"0x60a060405234801561001057600080fd5b5033606081901b60805261035461004c60003960008181604b0152818160bc015281816101080152818161013c015261018601526103546000f3fe6080604052600436106100345760003560e01c80630e769b2b146100395780637c1f3ffe14610089578063ab0ebff41461009e575b600080fd5b34801561004557600080fd5b5061006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b61009c6100973660046102d0565b6100b1565b005b61009c6100ac366004610306565b610131565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146100f9576040516282b42960e81b815260040160405180910390fd5b61012d6001600160a01b0383167f0000000000000000000000000000000000000000000000000000000000000000836101af565b5050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610179576040516282b42960e81b815260040160405180910390fd5b6101ac6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001682610233565b50565b600060405163a9059cbb60e01b81526001600160a01b03841660048201528260248201526000806044836000895af19150506101ea81610289565b61022d5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064015b60405180910390fd5b50505050565b600080600080600085875af19050806102845760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b6044820152606401610224565b505050565b60003d8261029b57806000803e806000fd5b80602081146102b35780156102c457600092506102c9565b816000803e600051151592506102c9565b600192505b5050919050565b600080604083850312156102e2578182fd5b82356001600160a01b03811681146102f8578283fd5b946020939093013593505050565b600060208284031215610317578081fd5b503591905056fea26469706673582212208e095a368bcb2efb2a8afd9b560ad94441e926086a4bc92edf16c33900df798e64736f6c63430008040033";
const deployedBytecode =
"0x6080604052600436106100345760003560e01c80630e769b2b146100395780637c1f3ffe14610089578063ab0ebff41461009e575b600080fd5b34801561004557600080fd5b5061006d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b61009c6100973660046102d0565b6100b1565b005b61009c6100ac366004610306565b610131565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146100f9576040516282b42960e81b815260040160405180910390fd5b61012d6001600160a01b0383167f0000000000000000000000000000000000000000000000000000000000000000836101af565b5050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610179576040516282b42960e81b815260040160405180910390fd5b6101ac6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001682610233565b50565b600060405163a9059cbb60e01b81526001600160a01b03841660048201528260248201526000806044836000895af19150506101ea81610289565b61022d5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b60448201526064015b60405180910390fd5b50505050565b600080600080600085875af19050806102845760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b6044820152606401610224565b505050565b60003d8261029b57806000803e806000fd5b80602081146102b35780156102c457600092506102c9565b816000803e600051151592506102c9565b600192505b5050919050565b600080604083850312156102e2578182fd5b82356001600160a01b03811681146102f8578283fd5b946020939093013593505050565b600060208284031215610317578081fd5b503591905056fea26469706673582212208e095a368bcb2efb2a8afd9b560ad94441e926086a4bc92edf16c33900df798e64736f6c63430008040033";
export { abi, bytecode, deployedBytecode };
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+9 -1
View File
@@ -20,5 +20,13 @@ export async function mockUploadWithCID(
file: FileOrBufferOrString,
options?: IpfsUploadBatchOptions | undefined,
) {
storage[cid] = file;
const [cidMain, id] = cid.split("/");
if (id) {
storage[cidMain] = {
[id]: file,
};
} else {
storage[cid] = file;
}
}
@@ -0,0 +1,360 @@
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
import { jsonProvider, sdk, signers, thirdwebFactory } from "./before-setup";
import { expect, assert } from "chai";
import {
Forwarder__factory,
DropERC721__factory,
ERC721Base__factory,
} from "@thirdweb-dev/contracts-js";
import {
bytecode as SplitMainBytecode,
deployedBytecode as SplitMainDeployedBytecode,
abi as SplitMainAbi,
} from "./metadata/SplitMain";
import {
bytecode as SplitWalletBytecode,
deployedBytecode as SplitWalletDeployedBytecode,
abi as SplitWalletAbi,
} from "./metadata/SplitWallet";
import {
bytecode as TWCloneFactoryBytecode,
abi as TWCloneFactoryAbi,
} from "./metadata/TWCloneFactory";
import { mockUploadMetadataWithBytecode } from "./utils";
const extendedMetadataMock = {
name: "",
metadataUri: "",
bytecodeUri: "",
analytics: {},
version: "1.0.0",
displayName: "",
description: "",
changelog: "",
isDeployableViaFactory: false,
isDeployableViaProxy: false,
factoryDeploymentData: {
implementationAddresses: {},
implementationInitializerFunction: "initialize",
factoryAddresses: {},
},
constructorParams: {},
publisher: "",
customFactoryInput: {
factoryFunction: "",
customFactoryAddresses: [],
},
};
describe("New Publish Flow", async () => {
let adminWallet: SignerWithAddress,
samWallet: SignerWithAddress,
bobWallet: SignerWithAddress,
w4: SignerWithAddress;
beforeEach(async () => {
await jsonProvider.send("hardhat_reset", []);
[adminWallet, samWallet, bobWallet, , , , , w4] = signers;
});
before("Mock upload infra contracts", async () => {
// mock upload Forwarder
await mockUploadMetadataWithBytecode(
"Forwarder",
Forwarder__factory.abi,
Forwarder__factory.bytecode,
"",
{
...extendedMetadataMock,
deployType: "standard",
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
},
"ipfs://Qmcu8FaqerUvQYb4qPg7PwkXa6dRtEe45LedLJPN42Jwqe/0",
// ^ we use actual publish uri as mock uri here, because this contract's uri is fetched from publisher by contractName
);
// mock upload TWCloneFactory
await mockUploadMetadataWithBytecode(
"Forwarder",
TWCloneFactoryAbi,
TWCloneFactoryBytecode,
"",
{
...extendedMetadataMock,
deployType: "standard",
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
},
"ipfs://QmYfw13Zykqf9jAmJobNgYrEpatEF9waWcQPUHvJ7sctRb/0",
// ^ we use actual publish uri as mock uri here, because this contract's uri is fetched from publisher by contractName
);
});
describe("Deploy newly published contract", async () => {
it("should deploy regular contract", async () => {
// mock publish as a standard-deploy contract
const mockPublishUri = await mockUploadMetadataWithBytecode(
"ERC721Base",
ERC721Base__factory.abi,
ERC721Base__factory.bytecode,
"",
{
...extendedMetadataMock,
deployType: "standard",
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
publisher: await adminWallet.getAddress(),
},
);
// deploy with mock uri
const admin = await adminWallet.getAddress();
const contractAddress = await sdk.deployer.deployContractFromUri(
mockPublishUri,
["Direct Deploy ERC721Base", "DDE", admin, 5],
);
const contract = await sdk.getContract(contractAddress);
const name = await contract.call("name");
const owner = await contract.call("owner");
expect(name).to.equal("Direct Deploy ERC721Base");
expect(owner).to.equal(admin);
});
it("should deploy with custom factory", async () => {
// mock publish SplitMain as a standard-deploy contract
const splitMainMockUri = await mockUploadMetadataWithBytecode(
"SplitMain",
SplitMainAbi,
SplitMainBytecode,
SplitMainDeployedBytecode,
{
...extendedMetadataMock,
deployType: "standard",
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
publisher: await adminWallet.getAddress(),
},
);
// deploy custom-factory `SplitMain`, with mock uri
const splitMainAddress = await sdk.deployer.deployContractFromUri(
splitMainMockUri,
[],
);
// mock publish SplitWallet as a custom-factory-deploy contract
const splitWalletMockUri = await mockUploadMetadataWithBytecode(
"SplitWallet",
SplitWalletAbi,
SplitWalletBytecode,
SplitWalletDeployedBytecode,
{
...extendedMetadataMock,
deployType: "customFactory",
factoryDeploymentData: {
...extendedMetadataMock.factoryDeploymentData,
customFactoryInput: {
factoryFunction: "createSplit",
params: [],
customFactoryAddresses: {
"31337": splitMainAddress,
},
},
},
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
publisher: await adminWallet.getAddress(),
},
);
// deploy contract via custom-factory, with mock uri
const splitWalletAddress = await sdk.deployer.deployContractFromUri(
splitWalletMockUri,
[
[await bobWallet.getAddress(), await adminWallet.getAddress()],
[500000, 500000],
0,
await adminWallet.getAddress(),
],
);
const splitContract = await sdk.getContract(splitWalletAddress);
const deployingAddress = await splitContract.call("splitMain");
expect(deployingAddress).to.equal(splitMainAddress);
});
});
describe("Deploy re-published contract", async () => {
it("should deploy with auto factory", async () => {
const mockPublisher = process.env.contractPublisherAddress;
process.env.contractPublisherAddress =
"0x664244560eBa21Bf82d7150C791bE1AbcD5B4cd7";
const adminAddress = await adminWallet.getAddress();
// mock publish as a autoFactory-deploy contract
const mockPublishUri = await mockUploadMetadataWithBytecode(
"DropERC721",
DropERC721__factory.abi,
DropERC721__factory.bytecode,
"",
{
...extendedMetadataMock,
deployType: "autoFactory",
networksForDeployment: {
allNetworks: true,
networksEnabled: [],
},
publisher: adminAddress,
},
);
// deploy via autoFactory, with mock uri
const contractAddress = await sdk.deployer.deployContractFromUri(
mockPublishUri,
[
adminAddress,
"DropERC721 Minimal Proxy",
"DME",
"",
[],
adminAddress,
adminAddress,
5,
5,
adminAddress,
],
);
const contract = await sdk.getContract(contractAddress);
const name = await contract.call("name");
const owner = await contract.call("owner");
const code = await sdk.getProvider().getCode(contractAddress);
expect(name).to.equal("DropERC721 Minimal Proxy");
expect(owner).to.equal(adminAddress);
assert(code.startsWith("0x363d3d373d3d3d363d73"));
process.env.contractPublisherAddress = mockPublisher;
});
});
describe("Deploy old contract without re-publishing", async () => {
it("should deploy regular contract", async () => {
// mock publish as old style -- omit deployType, networksForDeployment etc
const mockPublishUri = await mockUploadMetadataWithBytecode(
"ERC721Base",
ERC721Base__factory.abi,
ERC721Base__factory.bytecode,
"",
extendedMetadataMock,
);
// deploy with mock uri
const admin = await adminWallet.getAddress();
const contractAddress = await sdk.deployer.deployContractFromUri(
mockPublishUri,
["Direct Deploy ERC721Base", "DDE", admin, 5],
);
const contract = await sdk.getContract(contractAddress);
const name = await contract.call("name");
const owner = await contract.call("owner");
expect(name).to.equal("Direct Deploy ERC721Base");
expect(owner).to.equal(admin);
});
it("should deploy via old factory", async () => {
const mockPublisher = process.env.contractPublisherAddress;
process.env.contractPublisherAddress =
"0x664244560eBa21Bf82d7150C791bE1AbcD5B4cd7";
const adminAddress = await adminWallet.getAddress();
// mock publish as old-style factory deployable -- omit deployType, networksForDeployment etc
const mockPublishUri = await mockUploadMetadataWithBytecode(
"DropERC721",
DropERC721__factory.abi,
DropERC721__factory.bytecode,
"",
{
...extendedMetadataMock,
isDeployableViaFactory: true,
publisher: adminAddress,
},
);
// deploy implementation
const implementationAddress = await sdk.deployer.deployContractFromUri(
mockPublishUri,
[],
{
forceDirectDeploy: true,
},
);
await (
await thirdwebFactory.addImplementation(implementationAddress)
).wait();
// re-upload, with implementation address
const mockPublishUriUpdated = await mockUploadMetadataWithBytecode(
"DropERC721",
DropERC721__factory.abi,
DropERC721__factory.bytecode,
"",
{
...extendedMetadataMock,
isDeployableViaFactory: true,
factoryDeploymentData: {
...extendedMetadataMock?.factoryDeploymentData,
implementationAddresses: {
"31337": implementationAddress,
},
factoryAddresses: {
"31337": process.env.factoryAddress,
},
},
publisher: adminAddress,
},
);
// Deploy proxy DropERC721
const contractAddress = await sdk.deployer.deployContractFromUri(
mockPublishUriUpdated,
[
adminAddress,
"Drop Proxy Old",
"DPA",
"",
[],
adminAddress,
adminAddress,
5,
5,
adminAddress,
],
);
const drop = await sdk.getContract(contractAddress);
const bytecode = await sdk.getProvider().getCode(contractAddress);
const name = await drop.call("name");
expect(name).to.equal("Drop Proxy Old");
assert(bytecode.startsWith("0x363d3d373d3d3d363d73"));
});
});
// TODO: test any-evm vs standard chain deploys with same publish-uri
});
+66 -3
View File
@@ -1,4 +1,8 @@
import { resolveContractUriFromAddress } from "../../src/evm";
import {
FullPublishMetadata,
extractIPFSHashFromBytecode,
resolveContractUriFromAddress,
} from "../../src/evm";
import { defaultProvider } from "./before-setup";
import { mockUploadWithCID } from "./mock/MockStorage";
import { ContractInterface, ethers } from "ethers";
@@ -18,10 +22,12 @@ export const deployContractAndUploadMetadata = async (
return deployed.address;
};
const mockUploadContractMetadata = async (
export const mockUploadContractMetadata = async (
contractName: string,
address: string,
abi: any,
extendedMetadata?: FullPublishMetadata | any,
publishUri?: string,
) => {
const ipfsHash = (await resolveContractUriFromAddress(
address,
@@ -57,11 +63,68 @@ const mockUploadContractMetadata = async (
// mock raw-metadata
const rawMeta = {
...extendedMetadata,
name: contractName,
metadataUri: ipfsHash,
bytecodeUri: bytecodeUri,
};
const rawMetaUri = ipfsHash.concat("rawMeta");
const rawMetaUri = publishUri || ipfsHash.concat("rawMeta");
const rawMetaCID = rawMetaUri.replace("ipfs://", "");
await mockUploadWithCID(rawMetaCID, JSON.stringify(rawMeta));
return rawMetaUri;
};
export const mockUploadMetadataWithBytecode = async (
contractName: string,
abi: any,
bytecode: string,
deployedBytecode?: string,
extendedMetadata?: FullPublishMetadata | any,
publishUri?: string,
): Promise<string> => {
const ipfsHash =
deployedBytecode && deployedBytecode.length > 0
? (extractIPFSHashFromBytecode(deployedBytecode) as string)
: (extractIPFSHashFromBytecode(bytecode) as string);
const metadata = {
compiler: {},
output: {
abi,
devdoc: {},
userdoc: {},
},
settings: {
compilationTarget: {
contract: contractName,
},
evmVersion: {},
metadata: {},
optimizer: {},
remappings: [],
},
sources: {},
version: 1,
};
const metadataCID = ipfsHash.replace("ipfs://", "");
await mockUploadWithCID(metadataCID, JSON.stringify(metadata));
// mock bytecode
const bytecodeUri = ipfsHash.concat("bytecode");
const bytecodeCID = bytecodeUri.replace("ipfs://", "");
await mockUploadWithCID(bytecodeCID, bytecode);
// mock raw-metadata
const rawMeta = {
...extendedMetadata,
name: contractName,
metadataUri: ipfsHash,
bytecodeUri: bytecodeUri,
};
const rawMetaUri = publishUri || ipfsHash.concat("rawMeta");
const rawMetaCID = rawMetaUri.replace("ipfs://", "");
await mockUploadWithCID(rawMetaCID, JSON.stringify(rawMeta));
return rawMetaUri;
};

Some files were not shown because too many files have changed in this diff Show More