Compare commits

..
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
4df0c2ee10 Version Packages (#1542)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-28 17:36:13 -07:00
Mariano FuentesandGitHub 48a8f9385e [CLI] - Update cors restrictions on login (#1552) 2023-08-29 00:20:07 +00:00
Joaquim VergesandGitHub a91776e121 [all] fix changesets (#1553) 2023-08-28 17:12:24 -07:00
Joaquim VergesandGitHub f5aed34d3c update chains + fix opbnb-testnet (#1551) 2023-08-29 00:02:20 +00:00
0f02706906 Use auth token for CLI (#1422)
Co-authored-by: Adam Majmudar <[email protected]>
2023-08-28 16:42:07 +00:00
iketwandGitHub eb463735e2 [React/Wallets] Adds a new RainbowWallet that supports Rainbow browser extension (#1544) 2023-08-28 13:00:31 +00:00
Joaquim VergesandGitHub adec589ead [SDK] Expose joinAbis() utility function (#1545) 2023-08-28 03:30:51 +00:00
saminaandGitHub e5246f2667 Update README.md (#1531) 2023-08-26 00:53:32 +00:00
b29ec675f9 [react] export ConnectModalInline component for ConnectWallet playground (#1532)
Co-authored-by: Joaquim Verges <[email protected]>
2023-08-26 00:27:56 +00:00
Joaquim VergesandGitHub b30566c684 [Chains] updated chains (#1540) 2023-08-26 00:15:47 +00:00
96 changed files with 1753 additions and 525 deletions
+7
View File
@@ -1,5 +1,12 @@
# @thirdweb-dev/auth
## 3.2.38
### Patch Changes
- Updated dependencies [[`eb463735`](https://github.com/thirdweb-dev/js/commit/eb463735e2f784cd1d212a982835af95cf60766b), [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3)]:
- @thirdweb-dev/wallets@1.1.21
## 3.2.37
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "3.2.37",
"version": "3.2.38",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
+11
View File
@@ -1,5 +1,16 @@
# @thirdweb-dev/chains
## 0.1.49
### Patch Changes
- [#1540](https://github.com/thirdweb-dev/js/pull/1540) [`b30566c6`](https://github.com/thirdweb-dev/js/commit/b30566c68436ad94ddc938a380eccc13a8a7147d) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - updated chains
- [#1422](https://github.com/thirdweb-dev/js/pull/1422) [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3) Thanks [@Marfuen](https://github.com/Marfuen)! - CLI will now use an OAuth like login flow to authenticate the device with the wallet on the dashboard.
Any actions performed by the CLI will be linked to your account / wallet on the dashboard, including but not limited to tracking your usage, and billing as needed.
- [#1551](https://github.com/thirdweb-dev/js/pull/1551) [`f5aed34d`](https://github.com/thirdweb-dev/js/commit/f5aed34d3c71065c3f45df2c1eb84ba9c36162d5) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Update chains
## 0.1.48
### Patch Changes
+3 -1
View File
@@ -15,7 +15,9 @@ export default {
"https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com"
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
"wss://mainnet.gateway.tenderly.co"
],
"features": [
{
+3 -1
View File
@@ -7,7 +7,9 @@ export default {
"https://optimism-mainnet.infura.io/v3/${INFURA_API_KEY}",
"https://opt-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://mainnet.optimism.io",
"https://optimism.publicnode.com"
"https://optimism.publicnode.com",
"https://optimism.gateway.tenderly.co",
"wss://optimism.gateway.tenderly.co"
],
"faucets": [],
"nativeCurrency": {
+3 -1
View File
@@ -10,7 +10,9 @@ export default {
"https://rpc.sepolia.org",
"https://rpc2.sepolia.org",
"https://rpc-sepolia.rockx.com",
"https://rpc.sepolia.ethpandaops.io"
"https://rpc.sepolia.ethpandaops.io",
"https://sepolia.gateway.tenderly.co",
"wss://sepolia.gateway.tenderly.co"
],
"faucets": [
"http://fauceth.komputing.org?chain=11155111&address=${ADDRESS}"
-25
View File
@@ -1,25 +0,0 @@
import type { Chain } from "../src/types";
export default {
"name": "DeBank Testnet",
"chain": "DeBank",
"rpc": [],
"faucets": [],
"icon": {
"url": "ipfs://QmW9pBps8WHRRWmyXhjLZrjZJUe8F48hUu7z98bu2RVsjN",
"width": 400,
"height": 400,
"format": "png"
},
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"infoURL": "https://debank.com",
"shortName": "debank-testnet",
"chainId": 115,
"networkId": 115,
"explorers": [],
"testnet": true,
"slug": "debank-testnet"
} as const satisfies Chain;
-25
View File
@@ -1,25 +0,0 @@
import type { Chain } from "../src/types";
export default {
"name": "DeBank Mainnet",
"chain": "DeBank",
"rpc": [],
"faucets": [],
"icon": {
"url": "ipfs://QmW9pBps8WHRRWmyXhjLZrjZJUe8F48hUu7z98bu2RVsjN",
"width": 400,
"height": 400,
"format": "png"
},
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"infoURL": "https://debank.com",
"shortName": "debank-mainnet",
"chainId": 116,
"networkId": 116,
"explorers": [],
"testnet": false,
"slug": "debank"
} as const satisfies Chain;
+8 -2
View File
@@ -17,8 +17,14 @@ export default {
"symbol": "MC",
"decimals": 18
},
"infoURL": "https://gaming.meritcircle.io",
"shortName": "BEAM",
"infoURL": "https://www.onbeam.com",
"shortName": "beam-testnet",
"icon": {
"url": "ipfs://QmQJ21NWyGGDraicVEzS1Uqq1yXahM9NCuNZgnfYvtspdt",
"width": 512,
"height": 512,
"format": "png"
},
"chainId": 13337,
"networkId": 13337,
"explorers": [
+3 -1
View File
@@ -18,7 +18,9 @@ export default {
"https://rpc-mainnet.maticvigil.com",
"https://rpc-mainnet.matic.quiknode.pro",
"https://matic-mainnet-full-rpc.bwarelabs.com",
"https://polygon-bor.publicnode.com"
"https://polygon-bor.publicnode.com",
"https://polygon.gateway.tenderly.co",
"wss://polygon.gateway.tenderly.co"
],
"faucets": [],
"nativeCurrency": {
+19
View File
@@ -0,0 +1,19 @@
import type { Chain } from "../src/types";
export default {
"name": "Redbelly Network Mainnet",
"shortName": "rbn",
"chain": "RBN",
"chainId": 151,
"networkId": 151,
"rpc": [],
"faucets": [],
"infoURL": "https://redbelly.network",
"nativeCurrency": {
"name": "Redbelly Network Coin",
"symbol": "RBNT",
"decimals": 18
},
"status": "incubating",
"testnet": false,
"slug": "redbelly-network"
} as const satisfies Chain;
+19
View File
@@ -0,0 +1,19 @@
import type { Chain } from "../src/types";
export default {
"name": "Redbelly Network Devnet",
"shortName": "rbn-devnet",
"chain": "RBN",
"chainId": 152,
"networkId": 152,
"rpc": [],
"faucets": [],
"infoURL": "https://redbelly.network",
"nativeCurrency": {
"name": "Redbelly Network Coin",
"symbol": "RBNT",
"decimals": 18
},
"status": "incubating",
"testnet": false,
"slug": "redbelly-network-devnet"
} as const satisfies Chain;
+19
View File
@@ -0,0 +1,19 @@
import type { Chain } from "../src/types";
export default {
"name": "Redbelly Network Testnet",
"shortName": "rbn-testnet",
"chain": "RBN",
"chainId": 153,
"networkId": 153,
"rpc": [],
"faucets": [],
"infoURL": "https://redbelly.network",
"nativeCurrency": {
"name": "Redbelly Network Coin",
"symbol": "RBNT",
"decimals": 18
},
"status": "incubating",
"testnet": true,
"slug": "redbelly-network-testnet"
} as const satisfies Chain;
+18
View File
@@ -0,0 +1,18 @@
import type { Chain } from "../src/types";
export default {
"name": "Redbelly Network TGE",
"shortName": "rbn-tge",
"chain": "RBN",
"chainId": 154,
"networkId": 154,
"rpc": [],
"faucets": [],
"infoURL": "https://redbelly.network",
"nativeCurrency": {
"name": "Redbelly Network Coin",
"symbol": "RBNT",
"decimals": 18
},
"testnet": false,
"slug": "redbelly-network-tge"
} as const satisfies Chain;
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"name": "Bitchain Mainnet",
"chain": "Bit",
"rpc": [
"https://bitchain.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.bitchain.biz/"
],
"faucets": [],
"nativeCurrency": {
"name": "Bitcoin",
"symbol": "BTC",
"decimals": 18
},
"infoURL": "https://www.bitchain.biz/",
"shortName": "bit",
"chainId": 198,
"networkId": 198,
"explorers": [
{
"name": "Bitchain Scan",
"url": "https://explorer.bitchain.biz",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "bitchain"
} as const satisfies Chain;
+2 -2
View File
@@ -6,8 +6,8 @@ export default {
"https://boba-network.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://mainnet.boba.network",
"https://replica.boba.network",
"http://boba-ethereum.gateway.tenderly.co",
"http://gateway.tenderly.co/public/boba-ethereum",
"https://boba-ethereum.gateway.tenderly.co",
"https://gateway.tenderly.co/public/boba-ethereum",
"wss://boba-ethereum.gateway.tenderly.co/",
"wss://gateway.tenderly.co/public/boba-ethereum"
],
+21
View File
@@ -0,0 +1,21 @@
import type { Chain } from "../src/types";
export default {
"name": "Dubxcoin network",
"chain": "DUBX",
"rpc": [
"https://dubxcoin-network.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpcmain.arabianchain.org"
],
"faucets": [],
"nativeCurrency": {
"name": "Dubxcoin mainnet",
"symbol": "DUBX",
"decimals": 18
},
"infoURL": "https://arabianchain.org",
"shortName": "dubx",
"chainId": 3269,
"networkId": 3269,
"testnet": false,
"slug": "dubxcoin-network"
} as const satisfies Chain;
+23
View File
@@ -0,0 +1,23 @@
import type { Chain } from "../src/types";
export default {
"name": "Dubxcoin testnet",
"chain": "TESTDUBX",
"rpc": [
"https://dubxcoin-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpctestnet.arabianchain.org"
],
"faucets": [
"https://faucet.arabianchain.org/"
],
"nativeCurrency": {
"name": "Dubxcoin testnet",
"symbol": "TDUBX",
"decimals": 18
},
"infoURL": "https://arabianchain.org",
"shortName": "testdubx",
"chainId": 3270,
"networkId": 3270,
"testnet": true,
"slug": "dubxcoin-testnet"
} as const satisfies Chain;
+7 -9
View File
@@ -1,14 +1,12 @@
import type { Chain } from "../src/types";
export default {
"name": "Unicorn Ultra Testnet",
"name": "U2U Solaris Mainnet",
"chain": "u2u",
"rpc": [
"https://unicorn-ultra-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-testnet.uniultra.xyz"
],
"faucets": [
"https://faucet.uniultra.xyz"
"https://u2u-solaris.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-mainnet.uniultra.xyz"
],
"faucets": [],
"nativeCurrency": {
"name": "Unicorn Ultra",
"symbol": "U2U",
@@ -33,10 +31,10 @@ export default {
"format": "png"
},
"name": "U2U Explorer",
"url": "https://testnet.uniultra.xyz",
"url": "https://u2uscan.xyz",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "unicorn-ultra-testnet"
"testnet": false,
"slug": "u2u-solaris"
} as const satisfies Chain;
+3 -1
View File
@@ -7,7 +7,9 @@ export default {
"https://optimism-goerli.infura.io/v3/${INFURA_API_KEY}",
"https://opt-goerli.g.alchemy.com/v2/${ALCHEMY_API_KEY}",
"https://goerli.optimism.io",
"https://optimism-goerli.publicnode.com"
"https://optimism-goerli.publicnode.com",
"https://optimism-goerli.gateway.tenderly.co",
"wss://optimism-goerli.gateway.tenderly.co"
],
"faucets": [
"https://coinbase.com/faucets/optimism-goerli-faucet"
+3 -1
View File
@@ -9,7 +9,9 @@ export default {
"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"
"https://ethereum-goerli.publicnode.com",
"https://goerli.gateway.tenderly.co",
"wss://goerli.gateway.tenderly.co"
],
"faucets": [
"https://faucet.paradigm.xyz/",
+3 -3
View File
@@ -1,6 +1,6 @@
import type { Chain } from "../src/types";
export default {
"name": "opBNB",
"name": "Opbnb Testnet",
"chain": "opBNB",
"shortName": "opBNB",
"chainId": 5611,
@@ -11,7 +11,7 @@ export default {
"decimals": 18
},
"rpc": [
"https://opbnb.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://opbnb-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://opbnb-testnet-rpc.bnbchain.org"
],
"explorers": [
@@ -31,5 +31,5 @@ export default {
"width": 512,
"format": "svg"
},
"slug": "opbnb"
"slug": "opbnb-testnet"
} as const satisfies Chain;
+2 -2
View File
@@ -5,8 +5,8 @@ export default {
"rpc": [
"https://boba-bnb.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://bnb.boba.network",
"http://boba-bnb.gateway.tenderly.co/",
"http://gateway.tenderly.co/public/boba-bnb",
"https://boba-bnb.gateway.tenderly.co/",
"https://gateway.tenderly.co/public/boba-bnb",
"https://replica.bnb.boba.network",
"wss://boba-bnb.gateway.tenderly.co/",
"wss://gateway.tenderly.co/public/boba-bnb"
+33
View File
@@ -0,0 +1,33 @@
import type { Chain } from "../src/types";
export default {
"name": "Amplify Subnet",
"chain": "AMPLIFY",
"rpc": [
"https://amplify-subnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/amplify/testnet/rpc"
],
"features": [
{
"name": "EIP1559"
}
],
"faucets": [],
"nativeCurrency": {
"name": "AMP",
"symbol": "AMP",
"decimals": 18
},
"infoURL": "https://www.avax.network",
"shortName": "amplify",
"chainId": 78430,
"networkId": 78430,
"explorers": [
{
"name": "AMPLIFY Explorer",
"url": "https://subnets-test.avax.network/amplify",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "amplify-subnet"
} as const satisfies Chain;
+33
View File
@@ -0,0 +1,33 @@
import type { Chain } from "../src/types";
export default {
"name": "Bulletin Subnet",
"chain": "BULLETIN",
"rpc": [
"https://bulletin-subnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/bulletin/testnet/rpc"
],
"features": [
{
"name": "EIP1559"
}
],
"faucets": [],
"nativeCurrency": {
"name": "BLT",
"symbol": "BLT",
"decimals": 18
},
"infoURL": "https://www.avax.network",
"shortName": "bulletin",
"chainId": 78431,
"networkId": 78431,
"explorers": [
{
"name": "BULLETIN Explorer",
"url": "https://subnets-test.avax.network/bulletin",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "bulletin-subnet"
} as const satisfies Chain;
+33
View File
@@ -0,0 +1,33 @@
import type { Chain } from "../src/types";
export default {
"name": "Conduit Subnet",
"chain": "CONDUIT",
"rpc": [
"https://conduit-subnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/conduit/testnet/rpc"
],
"features": [
{
"name": "EIP1559"
}
],
"faucets": [],
"nativeCurrency": {
"name": "CON",
"symbol": "CON",
"decimals": 18
},
"infoURL": "https://www.avax.network",
"shortName": "conduit",
"chainId": 78432,
"networkId": 78432,
"explorers": [
{
"name": "CONDUIT Explorer",
"url": "https://subnets-test.avax.network/conduit",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "conduit-subnet"
} as const satisfies Chain;
+3 -1
View File
@@ -16,7 +16,9 @@ export default {
"https://matic-mumbai.chainstacklabs.com",
"https://rpc-mumbai.maticvigil.com",
"https://matic-testnet-archive-rpc.bwarelabs.com",
"https://polygon-mumbai-bor.publicnode.com"
"https://polygon-mumbai-bor.publicnode.com",
"https://polygon-mumbai.gateway.tenderly.co",
"wss://polygon-mumbai.gateway.tenderly.co"
],
"faucets": [
"https://faucet.polygon.technology/"
+4 -1
View File
@@ -5,7 +5,10 @@ export default {
"rpc": [
"https://base.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://mainnet.base.org/",
"https://developer-access-mainnet.base.org/"
"https://developer-access-mainnet.base.org/",
"https://base.gateway.tenderly.co",
"wss://base.gateway.tenderly.co",
"https://base.publicnode.com"
],
"faucets": [],
"nativeCurrency": {
+3
View File
@@ -4,6 +4,9 @@ export default {
"chain": "ETH",
"rpc": [
"https://base-goerli.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://base-goerli.publicnode.com",
"wss://base-goerli.gateway.tenderly.co",
"https://base-goerli.gateway.tenderly.co",
"https://goerli.base.org"
],
"faucets": [
+3 -1
View File
@@ -7,7 +7,9 @@ export default {
"https://testnet.bnb.boba.network",
"wss://wss.testnet.bnb.boba.network",
"https://replica.testnet.bnb.boba.network",
"wss://replica-wss.testnet.bnb.boba.network"
"wss://replica-wss.testnet.bnb.boba.network",
"https://boba-bnb-testnet.gateway.tenderly.co",
"wss://boba-bnb-testnet.gateway.tenderly.co"
],
"faucets": [],
"nativeCurrency": {
+8
View File
@@ -0,0 +1,8 @@
// @ts-check
/** @typedef { import("../../src/types").Chain } Chain */
/** @type {Partial<Chain>} */
export default {
name: "Opbnb Testnet",
slug: "opbnb-testnet",
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/chains",
"version": "0.1.48",
"version": "0.1.49",
"main": "dist/thirdweb-dev-chains.cjs.js",
"module": "dist/thirdweb-dev-chains.esm.js",
"browser": {
File diff suppressed because one or more lines are too long
-1
View File
@@ -75,7 +75,6 @@ export function getChainRPCs(
*
* @param chain - The chain config to assemble RPC URLs from
* @param clientId - The client id to use for the RPC URL
* @param secretKey - The secret key to use for the RPC URL
* @returns The first valid RPC URL for the chain
*/
export function getValidChainRPCs(
+19 -19
View File
@@ -18,6 +18,7 @@ const CHAIN_RPC_TEST_CASES: [ChainRPCOptions, string[]][] = [
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
[
@@ -27,6 +28,7 @@ const CHAIN_RPC_TEST_CASES: [ChainRPCOptions, string[]][] = [
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
[
@@ -36,12 +38,16 @@ const CHAIN_RPC_TEST_CASES: [ChainRPCOptions, string[]][] = [
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
// infura is supported for both http and ws
[
{ mode: "ws", infuraApiKey: "SAMPLE_KEY" },
["wss://mainnet.infura.io/ws/v3/SAMPLE_KEY"],
[
"wss://mainnet.infura.io/ws/v3/SAMPLE_KEY",
"wss://mainnet.gateway.tenderly.co",
],
],
];
@@ -53,12 +59,6 @@ describe("chains/utils", () => {
},
);
test("Should throw error if no processedRPCs (getChainRPCs(%p))", () => {
expect(() => {
getChainRPCs(Ethereum, { mode: "ws" });
}).toThrowError('No RPC available for chainId "1" with mode ws');
});
test.each([
[
undefined,
@@ -68,6 +68,7 @@ describe("chains/utils", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
[
@@ -78,6 +79,7 @@ describe("chains/utils", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
[
@@ -88,6 +90,7 @@ describe("chains/utils", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
[
@@ -98,24 +101,13 @@ describe("chains/utils", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
])("getValidChainRPCs(Ethereum, %s, %s) = %p", (clientId, mode, expected) => {
expect(getValidChainRPCs(Ethereum, clientId, mode)).toEqual(expected);
});
test.each([
[undefined, "ws"],
["SAMPLE_CLIENT_KEY", "ws"],
])(
"Should throw error if no processedRPCs (getValidChainRPCs(Ethereum, %s, %s))",
(clientId, mode) => {
expect(() => {
getValidChainRPCs(Ethereum, clientId, mode);
}).toThrowError('No RPC available for chainId "1" with mode ws');
},
);
test.each(CHAIN_RPC_TEST_CASES)(
"getChainRPC(%p) = %p",
(options, expected) => {
@@ -174,6 +166,7 @@ describe("chains/utils", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
},
],
@@ -187,6 +180,7 @@ describe("chains/utils", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
},
],
@@ -210,6 +204,7 @@ describe("chains/utils with APP_BUNDLE_ID", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
[
@@ -220,6 +215,7 @@ describe("chains/utils with APP_BUNDLE_ID", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
[
@@ -230,6 +226,7 @@ describe("chains/utils with APP_BUNDLE_ID", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
[
@@ -240,6 +237,7 @@ describe("chains/utils with APP_BUNDLE_ID", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
],
])("getValidChainRPCs(Ethereum, %s, %s) = %p", (clientId, mode, expected) => {
@@ -257,6 +255,7 @@ describe("chains/utils with APP_BUNDLE_ID", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
},
],
@@ -270,6 +269,7 @@ describe("chains/utils with APP_BUNDLE_ID", () => {
"https://api.mycryptoapi.com/eth",
"https://cloudflare-eth.com",
"https://ethereum.publicnode.com",
"https://mainnet.gateway.tenderly.co",
],
},
],
+16
View File
@@ -1,5 +1,21 @@
# thirdweb
## 0.12.0
### Minor Changes
- [#1422](https://github.com/thirdweb-dev/js/pull/1422) [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3) Thanks [@Marfuen](https://github.com/Marfuen)! - CLI will now use an OAuth like login flow to authenticate the device with the wallet on the dashboard.
Any actions performed by the CLI will be linked to your account / wallet on the dashboard, including but not limited to tracking your usage, and billing as needed.
### Patch Changes
- Updated dependencies [[`eb463735`](https://github.com/thirdweb-dev/js/commit/eb463735e2f784cd1d212a982835af95cf60766b), [`adec589e`](https://github.com/thirdweb-dev/js/commit/adec589ead8ceff1b57169e05a3e6733b4652cc7), [`b30566c6`](https://github.com/thirdweb-dev/js/commit/b30566c68436ad94ddc938a380eccc13a8a7147d), [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3), [`f5aed34d`](https://github.com/thirdweb-dev/js/commit/f5aed34d3c71065c3f45df2c1eb84ba9c36162d5)]:
- @thirdweb-dev/wallets@1.1.21
- @thirdweb-dev/sdk@3.10.57
- @thirdweb-dev/chains@0.1.49
- @thirdweb-dev/storage@1.2.9
- @thirdweb-dev/auth@3.2.38
## 0.11.20
### Patch Changes
+4 -2
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.11.20",
"version": "0.12.0",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -37,9 +37,11 @@
},
"dependencies": {
"@iarna/toml": "^2.2.5",
"@thirdweb-dev/auth": "workspace:*",
"@thirdweb-dev/chains": "workspace:*",
"@thirdweb-dev/sdk": "workspace:*",
"@thirdweb-dev/storage": "workspace:*",
"@thirdweb-dev/wallets": "workspace:*",
"async-retry": "^1.3.3",
"commander": "^9.1.0",
"detect-package-manager": "^2.0.1",
@@ -53,10 +55,10 @@
"prompts": "^2.4.2",
"rimraf": "^3.0.2",
"solc": "0.8.17",
"sync-disk-cache": "^2.1.0",
"tar": "^6.1.15",
"update-notifier": "^6.0.2",
"validate-npm-package-name": "^4.0.0",
"xdg-app-paths": "^8.3.0",
"yaml": "^2.3.1"
},
"bin": {
+254 -65
View File
@@ -1,105 +1,294 @@
import { LocalWallet } from "@thirdweb-dev/wallets";
import assert from "assert";
import chalk from "chalk";
import prompts from "prompts";
import Cache, { CacheEntry } from "sync-disk-cache";
import { ApiResponse } from "../lib/types";
import os from "os";
import fs from "fs";
import http from "http";
import open from "open";
import ora from "ora";
import url from "url";
import { logger, spinner } from "../core/helpers/logger";
import { ThirdwebAuth } from "@thirdweb-dev/auth";
import { ICredsConfig } from "../lib/types";
import { generateStateParameter } from "../lib/utils";
import crypto from "node:crypto";
type LoginProps = {
browser: boolean;
configPaths: ConfigPaths;
};
const defaultLoginProps = {
browser: true,
configPaths: {
credsConfigPath: "",
cliWalletPath: "",
tokenPath: "",
},
};
type ConfigPaths = {
credsConfigPath: string;
cliWalletPath: string;
tokenPath: string;
}
export async function loginUser(
cache: Cache,
configPaths: ConfigPaths,
options?: { new: boolean },
showLogs?: boolean,
) {
const keyFound = getSession(cache);
if (keyFound && !options?.new) {
const { credsConfigPath, tokenPath } = configPaths;
const authToken = await getSession(tokenPath, credsConfigPath);
if (authToken && !options?.new) {
if (showLogs) {
console.log(chalk.green("You are already logged in"));
}
return keyFound;
globalThis["TW_AUTH_TOKEN"] = authToken;
return authToken;
} else {
const apiKey = await createSession(cache);
return apiKey;
const token = await authenticateUser({ browser: true, configPaths });
if (!token) {
throw new Error("Failed to login");
}
globalThis["TW_AUTH_TOKEN"] = token;
return token;
}
}
export async function logoutUser(cache: Cache) {
export async function logoutUser(credsConfigPath: string, tokenPath: string, cliWalletPath: string) {
try {
cache.remove("api-secret-key");
console.log(chalk.green("You have been logged out"));
ora("Logging out...").start();
const dirExists = fs.existsSync(credsConfigPath) && fs.existsSync(tokenPath);
if (!dirExists) {
ora().warn(chalk.yellow("You are already logged out, did you mean to login?"));
return;
}
fs.unlinkSync(credsConfigPath);
fs.unlinkSync(tokenPath);
// TODO: We can consider not removing this on logout later, once we want to implement teams. For now this wallet will be ephemeral.
fs.unlinkSync(cliWalletPath);
ora().succeed(chalk.green("You have been logged out"));
} catch (error) {
console.log(chalk.red("Something went wrong", error));
}
}
export function getSession(cache: Cache) {
export async function getSession(tokenPath: string, configCredsPath: string) {
if (!fs.existsSync(tokenPath) || !fs.existsSync(configCredsPath)) {
return null;
}
try {
const apiKey: CacheEntry = cache.get("api-secret-key");
return apiKey.value;
return fs.readFileSync(tokenPath, "utf8");
} catch (error) {
console.log(error);
console.error(error);
}
}
export async function createSession(cache: Cache) {
try {
const isWindows = os.type() === "Windows_NT";
if (isWindows) {
console.log(chalk.yellow("Windows detected: if you are using powershell, there are some known issues with it that we are actively working on, please use git bash or the command prompt. Thank you for your understanding."));
}
const response = await prompts({
type: "invisible",
name: "apiSecretKey",
message: `Please enter your API secret key, you can find or create it on ${chalk.blue(
"https://thirdweb.com/create-api-key",
)}`,
export const authenticateUser = async (
props: LoginProps = defaultLoginProps,
) => {
const { credsConfigPath, cliWalletPath, tokenPath } = props.configPaths;
const waitForDashboard = spinner("Waiting for a response from the dashboard").clear();
// Get or generate a localwallet.
const wallet = await getOrGenerateLocalWallet(credsConfigPath, cliWalletPath);
const walletAddress = await wallet.getAddress();
const domain = "thirdweb.com";
const auth = new ThirdwebAuth(wallet, domain);
// Generate the login payload to pass to the dashboard.
const loggedIn = await auth.login({
domain: domain,
address: walletAddress,
});
// In this case the state is the loggedIn object.
const ourState = generateStateParameter(32);
const payload = encodeURIComponent(JSON.stringify(loggedIn));
const urlToOpen = `https://thirdweb.com/cli/login?payload=${payload}&#${ourState}`;
let server: http.Server;
let loginTimeoutHandle: NodeJS.Timeout;
const timerPromise = new Promise<void>((resolve, reject) => {
loginTimeoutHandle = setTimeout(() => {
logger.error("Login session timed out, server didn't receive a response in 5 minutes. Please try again.");
server.close();
clearTimeout(loginTimeoutHandle);
reject(new Error("Login session timed out, server didn't receive a response in 5 minutes. Please try again."));
}, 300000);
});
const loginPromise = new Promise<string>((resolve, reject) => {
server = http.createServer(async (req, res) => {
function finish(error?: Error) {
clearTimeout(loginTimeoutHandle);
server.close((closeErr?: Error) => {
if (error || closeErr) {
reject(error || closeErr);
}
});
}
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Origin', "https://thirdweb.com");
res.setHeader('Access-Control-Allow-Methods', 'POST');
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Headers', 'content-type, baggage, sentry-trace');
res.writeHead(200);
res.end();
return;
}
assert(req.url, "This request doesn't have a URL");
const { pathname, query } = url.parse(req.url, true);
switch (pathname) {
case "/auth/callback": {
if (query.failed) {
res.writeHead(500, "Unable to authenticate with the dashboard!");
res.end("Unable to authenticate with the dashboard!", () => {
finish(new Error("Unable to authenticate with the dashboard!"));
})
reject(chalk.red("Something went wrong! Unable to authenticate with the dashboard."));
waitForDashboard.stop();
}
if (query.token) {
const token = Array.isArray(query.token) ? query.token[0] : query.token;
const theirState = Array.isArray(query.state)
? query.state[0]
: query.state;
// Check if the state sent back is the same as the one we sent.
if (theirState !== ourState) {
res.writeHead(400, { "Content-Type": "text/plain" }); // send a 400 response
res.end("Unauthorized request, state mismatch", () => {
finish(new Error("Unauthorized request, state mismatch"));
});
reject(
new Error(chalk.red("\nUnauthorized request, state mismatch")),
);
waitForDashboard.stop();
} else {
// Save the token to the config file.
// eslint-disable-next-line no-unused-expressions
fs.writeFileSync(tokenPath, token), {
encoding: "utf8",
mode: 0o600,
};
res.end(() => {
waitForDashboard.clear();
console.log(chalk.green(`Successfully linked your account to this device`));
finish();
});
logger.info(chalk.green(`\nSuccessfully logged in.`));
resolve(token); // resolve promise with secretKey
}
} else {
res.writeHead(400, { "Content-Type": "text/plain" });
res.end("No authToken received", () => {
finish(new Error("No authToken received"));
});
reject(new Error(chalk.red("\nNo authToken received")));
waitForDashboard.stop();
}
}
}
});
const keyPassed = response.apiSecretKey;
if (!keyPassed) {
console.log(chalk.red("You need to pass an API secret key"));
process.exit(1);
}
server.listen(8976);
});
if (props?.browser) {
console.log(chalk.yellow(`Automatically opening a link to authenticate with our dashboard...\n`))
waitForDashboard.start();
// Adding this timeout since it feels weird for the browser to open before the spinner.
setTimeout(async () => {
await open(urlToOpen);
}, 2000);
} else {
logger.info(`Visit this link to authenticate: ${urlToOpen}`);
}
if (keyPassed.length === 32) {
console.log(chalk.red(`This is not a valid secret key. To get your secret key
1. Create an API key at https://thirdweb.com/create-api-key
2. Store and copy your Secret Key. This will be shown only once.
3. Paste it in the CLI when prompted`));
process.exit(1);
}
return Promise.race([timerPromise, loginPromise]);
};
async function getOrCreatePassword(configCredsPath: string): Promise<string> {
const passwordFileExists = fs.existsSync(configCredsPath);
if (!passwordFileExists) {
// Generate a random password for the CLI to be able to import / export the wallet.
return crypto.randomUUID();
} else {
const file = fs.readFileSync(configCredsPath, "utf8");
try {
await validateKey(response.apiSecretKey);
} catch (error) {
console.log(error);
process.exit(1);
JSON.parse(file);
} catch (e) {
// If it fails to parse the password we should just delete the file and generate a new password, since this password is not shown to the user.
fs.unlinkSync(configCredsPath);
return getOrCreatePassword(configCredsPath);
}
cache.set("api-secret-key", response.apiSecretKey);
return response.apiSecretKey;
} catch (error) {
console.log(error);
const { password } = JSON.parse(file) as ICredsConfig;
return password;
}
}
export async function validateKey(apiSecretKey: string) {
const fetch = (await import("node-fetch")).default;
// TODO: CHANGE THIS TO PROD BEFORE MERGING!!!
const response = await fetch(
`https://api.thirdweb.com/v1/keys/use?scope=storage`,
{
async function getOrGenerateLocalWallet(configCredsPath: string, cliWalletPath: string) {
// Get or prompt for password.
const password = await getOrCreatePassword(configCredsPath);
const wallet = new LocalWallet();
const foundWallet = fs.existsSync(cliWalletPath);
if (foundWallet) {
const walletJson = fs.readFileSync(cliWalletPath, "utf8");
try {
// See if file is valid before proceeding.
JSON.parse(walletJson);
await wallet.import({
encryptedJson: walletJson,
password,
});
return wallet;
} catch (e) {
// Wallet file is not valid json, create a new one.
}
}
// Otherwise, generate a new wallet.
wallet.generate();
const walletExported = await wallet.export({
strategy: "encryptedJson",
password,
});
// write wallet
fs.writeFileSync(cliWalletPath, walletExported, {
encoding: "utf8",
mode: 0o600,
});
// write password
fs.writeFileSync(configCredsPath, JSON.stringify({
password: password,
}), "utf8");
return wallet;
}
export const validateKey = async (apiSecretKey: string) => {
const apiUrl = "https://api.thirdweb.com/v1/keys/use";
try {
const response = await fetch(`${apiUrl}?scope=storage`, {
method: "GET",
headers: {
"Content-Type": "application/json",
"x-secret-key": apiSecretKey,
},
},
);
}
})
const apiResponse = (await response.json()) as ApiResponse;
if (response.status !== 200) {
throw new Error("Unauthorized key");
}
if (!response.ok) {
const { error } = apiResponse;
throw new Error(chalk.red(error.message));
} else {
return apiSecretKey;
} catch (error) {
throw new Error(chalk.red("Unauthorized key"));
}
}
};
+104 -39
View File
@@ -3,11 +3,11 @@ import { ThirdwebStorage } from "@thirdweb-dev/storage";
import chalk from "chalk";
import { exec, spawn } from "child_process";
import { Command } from "commander";
import fs from "fs";
import open from "open";
import os from "os";
import path from "path";
import prompts from "prompts";
import Cache, { CacheEntry } from "sync-disk-cache";
import xdgAppPaths from "xdg-app-paths";
import { loginUser, logoutUser, validateKey } from "../auth";
import { detectExtensions } from "../common/feature-detector";
import { processProject } from "../common/processor";
@@ -24,11 +24,17 @@ import { upload } from "../storage/command";
const main = async () => {
// eslint-disable-next-line turbo/no-undeclared-env-vars
const skipIntro = process.env.THIRDWEB_CLI_SKIP_INTRO === "true";
const configDir = xdgAppPaths(".thirdweb").config();
const cacheDir = xdgAppPaths(".thirdweb").cache();
const lastVersionCheckCachePath = path.join(
cacheDir,
"last-version-check.txt",
);
const credsConfigPath = path.join(configDir, "creds.json");
const cliWalletPath = path.join(configDir, "wallet.json");
const tokenPath = path.join(configDir, "auth-token.txt");
const program = new Command();
const cache = new Cache("thirdweb-cli", {
location: path.join(os.homedir(), ".thirdweb", "creds"),
});
// yes this has to look like this, eliminates whitespace
if (!skipIntro) {
@@ -50,16 +56,32 @@ const main = async () => {
.version(cliVersion, "-v, --version")
.option("--skip-update-check", "Skip check for auto updates")
.hook("preAction", async () => {
const regenerateConfig = !fs.existsSync(configDir);
const regenerateCache = !fs.existsSync(cacheDir);
// Create config directory if it doesn't exist
if (regenerateConfig) {
fs.mkdirSync(configDir, { recursive: true });
}
// Create cache directory if it doesn't exist
if (regenerateCache) {
fs.mkdirSync(cacheDir, { recursive: true });
}
if (skipIntro || program.opts().skipUpdateCheck) {
return;
}
let shouldCheckVersion = true;
try {
const lastCheckCache: CacheEntry = cache.get("last-version-check");
const lastVersionCheckCache = fs.readFileSync(
lastVersionCheckCachePath,
"utf-8",
);
if (lastCheckCache.isCached) {
const lastVersionCheck = new Date(lastCheckCache.value);
if (lastVersionCheckCache) {
const lastVersionCheck = new Date(lastVersionCheckCache);
// Don't check for updates if already checked within past 24 hours
if (Date.now() - lastVersionCheck.getTime() < 1000 * 60 * 60 * 24) {
shouldCheckVersion = false;
@@ -88,7 +110,14 @@ const main = async () => {
try {
// Set cache to prevent checking for updates again for 24 hours
cache.set("last-version-check", new Date().toISOString());
fs.writeFileSync(
lastVersionCheckCachePath,
new Date().toISOString(),
{
encoding: "utf-8",
mode: 0o600,
},
);
} catch {
// no-op
}
@@ -300,17 +329,22 @@ const main = async () => {
"Deploy a dynamic smart contract made up of extensions to blockchains",
)
.option("--zksync", "Deploy on ZKSync")
.option("-k, --key <key>", "API key to authorize usage")
.option("-k, --key <key>", "API secret key to authorize usage")
.action(async (options) => {
let apiSecretKey = "";
let secretKey = "";
// If no key is passed in, prompt the user to login. If it is passed in, use it.
if (!options.key) {
apiSecretKey = await loginUser(cache);
await loginUser({
credsConfigPath,
cliWalletPath,
tokenPath,
});
} else {
// Don't need to validate anymore? Also, should probably rely on the underlying service to throw that error.
await validateKey(options.key);
apiSecretKey = options.key;
secretKey = options.key;
}
const url = await deploy(options, apiSecretKey);
const url = await deploy(options, secretKey);
if (url && !options.ci) {
await open(url);
}
@@ -332,20 +366,25 @@ const main = async () => {
.option("--dry-run", "dry run (skip actually publishing)")
.option("-d, --debug", "show debug logs")
.option("--ci", "Continuous Integration mode")
.option("-k, --key <key>", "API key to authorize usage")
.option("-k, --key <key>", "API secret key to authorize usage")
.action(async (options) => {
let apiSecretKey = "";
let secretKey = "";
// If no key is passed in, prompt the user to login. If it is passed in, use it.
if (!options.key) {
apiSecretKey = await loginUser(cache);
await loginUser({
credsConfigPath,
cliWalletPath,
tokenPath,
});
} else {
// Don't need to validate anymore? Also, should probably rely on the underlying service to throw that error.
await validateKey(options.key);
apiSecretKey = options.key;
secretKey = options.key;
}
logger.warn(
"'release' is deprecated and will be removed in a future update. Please use 'publish' instead.",
);
const url = await processProject(options, "publish", apiSecretKey);
const url = await processProject(options, "publish", secretKey);
info(
`Open this link to publish your contracts: ${chalk.blueBright(
url.toString(),
@@ -374,17 +413,22 @@ const main = async () => {
.option("--dry-run", "dry run (skip actually publishing)")
.option("-d, --debug", "show debug logs")
.option("--ci", "Continuous Integration mode")
.option("-k, --key <key>", "API key to authorize usage")
.option("-k, --key <key>", "API secret key to authorize usage")
.action(async (options) => {
let apiSecretKey = "";
let secretKey = "";
// If no key is passed in, prompt the user to login. If it is passed in, use it.
if (!options.key) {
apiSecretKey = await loginUser(cache);
await loginUser({
credsConfigPath,
cliWalletPath,
tokenPath,
});
} else {
// Don't need to validate anymore? Also, should probably rely on the underlying service to throw that error.
await validateKey(options.key);
apiSecretKey = options.key;
secretKey = options.key;
}
const url = await processProject(options, "publish", apiSecretKey);
const url = await processProject(options, "publish", secretKey);
info(
`Open this link to publish your contracts: ${chalk.blueBright(
url.toString(),
@@ -399,19 +443,31 @@ const main = async () => {
.command("upload")
.description("Upload any file or directory to decentralized storage (IPFS)")
.argument("[upload]", "path to file or directory to upload")
.option("-k, --key <key>", "API key to authorize usage")
.option("-k, --key <key>", "API secret key to authorize usage")
.action(async (_path, options) => {
let apiSecretKey = "";
let secretKey = "";
let storage: ThirdwebStorage;
// If no key is passed in, prompt the user to login. If it is passed in, use it.
if (!options.key) {
apiSecretKey = await loginUser(cache);
await loginUser({
credsConfigPath,
cliWalletPath,
tokenPath,
});
} else {
// Don't need to validate anymore? Also, should probably rely on the underlying service to throw that error.
await validateKey(options.key);
apiSecretKey = options.key;
secretKey = options.key;
}
const storage = new ThirdwebStorage({
secretKey: apiSecretKey,
});
if (secretKey) {
storage = new ThirdwebStorage({
secretKey,
});
} else {
storage = new ThirdwebStorage();
}
try {
const uri = await upload(storage, _path);
info(
@@ -438,7 +494,7 @@ const main = async () => {
),
);
} else {
logger.error(chalk.redBright("Failed to upload files"), err);
logger.error(chalk.redBright(err.message ? err.message : err));
}
}
});
@@ -460,17 +516,22 @@ const main = async () => {
"Preload ABIs and generate types for your smart contract to strongly type the thirdweb SDK",
)
.option("-p, --path <project-path>", "path to project", ".")
.option("-k, --key <key>", "API key to authorize usage")
.option("-k, --key <key>", "API secret key to authorize usage")
.action(async (options) => {
let apiSecretKey = "";
let secretKey = "";
// If no key is passed in, prompt the user to login. If it is passed in, use it.
if (!options.key) {
apiSecretKey = await loginUser(cache);
await loginUser({
credsConfigPath,
cliWalletPath,
tokenPath,
});
} else {
// Don't need to validate anymore? Also, should probably rely on the underlying service to throw that error.
await validateKey(options.key);
apiSecretKey = options.key;
secretKey = options.key;
}
await generate(options, apiSecretKey);
await generate(options, secretKey);
});
program
@@ -480,7 +541,11 @@ const main = async () => {
)
.option("-n, --new", "Login with a new API secret key", false)
.action(async (options) => {
await loginUser(cache, options, true);
await loginUser({
credsConfigPath,
cliWalletPath,
tokenPath,
}, options, true);
});
program
@@ -489,7 +554,7 @@ const main = async () => {
"Logout of the thirdweb CLI, effectively removing your API secret key from your machine",
)
.action(async () => {
await logoutUser(cache);
await logoutUser(credsConfigPath, tokenPath, cliWalletPath);
});
await program.parseAsync();
+25 -13
View File
@@ -32,12 +32,18 @@ import {
export async function processProject(
options: any,
command: "deploy" | "publish",
apiSecretKey: string,
secretKey: string,
) {
// TODO: allow overriding the default storage
const storage = new ThirdwebStorage({
secretKey: apiSecretKey,
});
let storage: ThirdwebStorage;
if (secretKey) {
storage = new ThirdwebStorage({
secretKey,
});
} else {
// Since the auth key is being set in the global context, we don't need to pass anything here.
storage = new ThirdwebStorage();
}
logger.setSettings({
minLevel: options.debug ? "debug" : "info",
@@ -240,7 +246,7 @@ export async function processProject(
} else {
const deployArgs: RouterParams = await formatToExtensions(
selectedContracts,
apiSecretKey,
secretKey,
);
const outputDeployArgs = JSON.stringify(deployArgs, undefined, 2);
@@ -345,9 +351,9 @@ export async function processProject(
loader.succeed("Upload successful");
return getUrl(combinedURIs, command);
} catch (e) {
} catch (err: any) {
loader.fail("Error uploading metadata");
throw e;
throw new Error(err.message ? err.message : err);
}
}
@@ -356,8 +362,8 @@ export function getUrl(hashes: string[], command: string) {
if (hashes.length === 1) {
url = new URL(
THIRDWEB_URL +
`/contracts/${command}/` +
encodeURIComponent(hashes[0].replace("ipfs://", "")),
`/contracts/${command}/` +
encodeURIComponent(hashes[0].replace("ipfs://", "")),
);
} else {
url = new URL(THIRDWEB_URL + "/contracts/" + command);
@@ -370,14 +376,20 @@ export function getUrl(hashes: string[], command: string) {
async function formatToExtensions(
contracts: ContractPayload[],
apiSecretKey: string,
secretKey: string,
): Promise<{
extensions: Extension[];
extensionDeployArgs: ExtensionDeployArgs[];
}> {
const storage = new ThirdwebStorage({
secretKey: apiSecretKey,
});
let storage: ThirdwebStorage;
if (secretKey) {
storage = new ThirdwebStorage({
secretKey,
});
} else {
// Since the auth key is being set in the global context, we don't need to pass anything here.
storage = new ThirdwebStorage();
}
const extensions: Extension[] = [];
const extensionDeployArgs: ExtensionDeployArgs[] = [];
+12 -6
View File
@@ -8,11 +8,17 @@ import { upload } from "../storage/command";
export async function deployApp(
distPath = "dist",
projectPath = ".",
apiSecretKey: string,
secretKey: string,
) {
const storage = new ThirdwebStorage({
secretKey: apiSecretKey,
});
let storage: ThirdwebStorage;
if (secretKey) {
storage = new ThirdwebStorage({
secretKey,
});
} else {
// Since the auth key is being set in the global context, we don't need to pass anything here.
storage = new ThirdwebStorage();
}
const detectedPackageManager = await detectPackageManager(projectPath, {});
const detectedFramework = await detectFramework(
projectPath,
@@ -74,8 +80,8 @@ export async function deployApp(
"ipfs://",
"https://cf-ipfs.com/ipfs/",
)}`;
} catch (err) {
console.error("Can't upload project", err);
} catch (err: any) {
console.error(err.message ? err.message : err);
return Promise.reject("Can't upload project");
}
}
+3 -3
View File
@@ -12,7 +12,7 @@ type DeployOptions = {
distPath?: string;
};
export async function deploy(options: DeployOptions, apiSecretKey: string) {
export async function deploy(options: DeployOptions, secretKey: string) {
if (options.name) {
const url = generateDashboardUrl(options.name, options.contractVersion);
@@ -33,7 +33,7 @@ export async function deploy(options: DeployOptions, apiSecretKey: string) {
if (options.app) {
try {
const url = await deployApp(options.distPath, options.path, apiSecretKey);
const url = await deployApp(options.distPath, options.path, secretKey);
info(`Here is the link to your app: ${chalk.blueBright(url.toString())}`);
return url.toString();
} catch (err) {
@@ -44,7 +44,7 @@ export async function deploy(options: DeployOptions, apiSecretKey: string) {
}
}
const url = await processProject(options, "deploy", apiSecretKey);
const url = await processProject(options, "deploy", secretKey);
info(
`Open this link to deploy your contracts: ${chalk.blueBright(
url.toString(),
+13 -12
View File
@@ -14,7 +14,7 @@ import { info } from "../core/helpers/logger";
import { GenerateOptions, ThirdwebConfig } from "./types";
import { CHAIN_OPTIONS, getContractsForAddresses } from "./utils";
export async function generate(options: GenerateOptions, apiSecretKey: string) {
export async function generate(options: GenerateOptions, secretKey: string) {
const projectPath: string = options.path?.replace(/\/$/, "") || ".";
let contracts: DeployedContract[] = [];
@@ -55,8 +55,7 @@ export async function generate(options: GenerateOptions, apiSecretKey: string) {
const numberOfNewContracts =
contracts.length - thirdwebConfig.contracts.length;
info(
`Updated thirdweb.json with ${numberOfNewContracts} new contract${
numberOfNewContracts === 1 ? "" : "s"
`Updated thirdweb.json with ${numberOfNewContracts} new contract${numberOfNewContracts === 1 ? "" : "s"
}`,
);
}
@@ -86,19 +85,22 @@ export async function generate(options: GenerateOptions, apiSecretKey: string) {
);
info(
`Created a thirdweb.json file with configuration for ${
contracts.length
} contract${
contracts.length === 1 ? "" : "s"
`Created a thirdweb.json file with configuration for ${contracts.length
} contract${contracts.length === 1 ? "" : "s"
} detected in your project.\n\n - You can also update this configuration manually by editing the file.\n`,
);
}
// Attempt to download the ABI for each contract
ora(`Downloading ABIs for contracts configured in 'thirdweb.json'`).info();
const storage = new ThirdwebStorage({
secretKey: apiSecretKey,
});
let storage: ThirdwebStorage;
if (secretKey) {
storage = new ThirdwebStorage({
secretKey,
});
} else {
storage = new ThirdwebStorage();
}
const metadata: {
address: string;
metadata: Awaited<ReturnType<typeof fetchContractMetadataFromAddress>>;
@@ -187,8 +189,7 @@ export async function generate(options: GenerateOptions, apiSecretKey: string) {
}
ora(
`Downloaded and cached ABIs for ${metadata.length} smart contract${
metadata.length === 1 ? "" : "s"
`Downloaded and cached ABIs for ${metadata.length} smart contract${metadata.length === 1 ? "" : "s"
}`,
).succeed();
+5
View File
@@ -1,5 +1,10 @@
import { DeployedContract } from "@thirdweb-dev/sdk";
export type AuthConfig = {
secretKey?: string;
authToken?: string;
}
export type ThirdwebConfig = {
chainIds: number[];
contracts: DeployedContract[];
+4
View File
@@ -0,0 +1,4 @@
declare module globalThis {
// eslint-disable-next-line no-var
var TW_AUTH_TOKEN: string;
}
+6 -1
View File
@@ -4,6 +4,11 @@ import {
PackageManagerType,
} from "../core/types/ProjectType";
export type ICredsConfig = {
password: string;
expiration: number;
}
export type IProcessContractAppTypeArgs = {
detectedPackageManager: PackageManagerType;
thirdwebDepsToUpdate: Set<string>;
@@ -43,4 +48,4 @@ export type ApiResponse = {
statusCode: number;
message: string;
};
};
};
+5
View File
@@ -4,6 +4,11 @@ import path from "path";
import { PackageManagerType } from "../core/types/ProjectType";
import { runCommand } from "../create/helpers/run-command";
import { IProcessAppTypeArgs, IProcessContractAppTypeArgs } from "./types";
import crypto from "crypto";
export function generateStateParameter(length: number) {
return crypto.randomBytes(length).toString("hex");
}
export function convertDependenciesToStringFormat(dependenciesObject: object) {
return Object.entries(dependenciesObject).map(([pkg, versionDetails]) => {
+2 -2
View File
@@ -69,9 +69,9 @@ export async function upload(
);
try {
uri = await storage.upload(file, { uploadWithoutDirectory: true });
} catch (err) {
} catch (err: any) {
spin.fail("Failed to upload file to IPFS.");
return Promise.reject(err);
return Promise.reject(err.message ? err.message : err);
}
spin.succeed("Successfully uploaded file to IPFS.");
} else {
+11
View File
@@ -1,5 +1,16 @@
# @thirdweb-dev/react-core
## 3.14.38
### Patch Changes
- Updated dependencies [[`eb463735`](https://github.com/thirdweb-dev/js/commit/eb463735e2f784cd1d212a982835af95cf60766b), [`adec589e`](https://github.com/thirdweb-dev/js/commit/adec589ead8ceff1b57169e05a3e6733b4652cc7), [`b30566c6`](https://github.com/thirdweb-dev/js/commit/b30566c68436ad94ddc938a380eccc13a8a7147d), [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3), [`f5aed34d`](https://github.com/thirdweb-dev/js/commit/f5aed34d3c71065c3f45df2c1eb84ba9c36162d5)]:
- @thirdweb-dev/wallets@1.1.21
- @thirdweb-dev/sdk@3.10.57
- @thirdweb-dev/chains@0.1.49
- @thirdweb-dev/storage@1.2.9
- @thirdweb-dev/auth@3.2.38
## 3.14.37
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "3.14.37",
"version": "3.14.38",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -1,5 +1,7 @@
# @thirdweb-dev/react-native-compat
## 0.2.57
## 0.2.56
## 0.2.55
+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.56",
"version": "0.2.57",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+11
View File
@@ -1,5 +1,16 @@
# @thirdweb-dev/react-native
## 0.2.57
### Patch Changes
- Updated dependencies [[`eb463735`](https://github.com/thirdweb-dev/js/commit/eb463735e2f784cd1d212a982835af95cf60766b), [`adec589e`](https://github.com/thirdweb-dev/js/commit/adec589ead8ceff1b57169e05a3e6733b4652cc7), [`b30566c6`](https://github.com/thirdweb-dev/js/commit/b30566c68436ad94ddc938a380eccc13a8a7147d), [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3), [`f5aed34d`](https://github.com/thirdweb-dev/js/commit/f5aed34d3c71065c3f45df2c1eb84ba9c36162d5)]:
- @thirdweb-dev/wallets@1.1.21
- @thirdweb-dev/sdk@3.10.57
- @thirdweb-dev/chains@0.1.49
- @thirdweb-dev/storage@1.2.9
- @thirdweb-dev/react-core@3.14.38
## 0.2.56
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.2.56",
"version": "0.2.57",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/react
## 3.14.38
### Patch Changes
- [#1532](https://github.com/thirdweb-dev/js/pull/1532) [`b29ec675`](https://github.com/thirdweb-dev/js/commit/b29ec675f9c41c49c015af2427d5402367c06c73) Thanks [@MananTank](https://github.com/MananTank)! - Export ConnectModalInline component
- [#1544](https://github.com/thirdweb-dev/js/pull/1544) [`eb463735`](https://github.com/thirdweb-dev/js/commit/eb463735e2f784cd1d212a982835af95cf60766b) Thanks [@iketw](https://github.com/iketw)! - Adds support for Rainbow's new browser extension
- Updated dependencies [[`eb463735`](https://github.com/thirdweb-dev/js/commit/eb463735e2f784cd1d212a982835af95cf60766b), [`adec589e`](https://github.com/thirdweb-dev/js/commit/adec589ead8ceff1b57169e05a3e6733b4652cc7), [`b30566c6`](https://github.com/thirdweb-dev/js/commit/b30566c68436ad94ddc938a380eccc13a8a7147d), [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3), [`f5aed34d`](https://github.com/thirdweb-dev/js/commit/f5aed34d3c71065c3f45df2c1eb84ba9c36162d5)]:
- @thirdweb-dev/wallets@1.1.21
- @thirdweb-dev/sdk@3.10.57
- @thirdweb-dev/chains@0.1.49
- @thirdweb-dev/react-core@3.14.38
## 3.14.37
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.14.37",
"version": "3.14.38",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+1 -1
View File
@@ -152,7 +152,7 @@ const DialogContent = styled.div<{ theme?: Theme }>`
}
`;
const DialogTitle = /* @__PURE__ */ styled(/* @__PURE__ */ Dialog.Title)<{
const DialogTitle = /* @__PURE__ */ styled.h2<{
theme?: Theme;
}>`
margin: 0;
@@ -2,10 +2,9 @@ import { type Theme, fontSize, media } from "../design-system";
import { iconSize } from "../design-system";
import { IconButton } from "./buttons";
import styled from "@emotion/styled";
import { Title } from "@radix-ui/react-dialog";
import { ChevronLeftIcon } from "@radix-ui/react-icons";
export const ModalTitle = /* @__PURE__ */ styled(Title)<{
export const ModalTitle = /* @__PURE__ */ styled.h2<{
theme?: Theme;
centerOnMobile?: boolean;
}>`
+2
View File
@@ -7,6 +7,8 @@ export { useSafe } from "./connectors/gnosis";
export { useMagic } from "./connectors/magic";
export { ConnectWallet } from "../wallet/ConnectWallet/ConnectWallet";
export { ConnectModalInline } from "../wallet/ConnectWallet/ConnectModal";
export { NetworkSelector } from "../wallet/ConnectWallet/NetworkSelector";
export type { NetworkSelectorProps } from "../wallet/ConnectWallet/NetworkSelector";
@@ -10,25 +10,41 @@ import {
import {
ModalConfigCtx,
SetModalConfigCtx,
WalletUIStatesProvider,
useIsWalletModalOpen,
useSetIsWalletModalOpen,
} from "../../evm/providers/wallet-ui-states-provider";
import { ThemeProvider } from "@emotion/react";
import { darkTheme, lightTheme } from "../../design-system";
import {
Theme,
darkTheme,
lightTheme,
radius,
spacing,
} from "../../design-system";
import { useState, useCallback, useEffect, useRef, useContext } from "react";
import { GetStartedWithWallets } from "./screens/GetStartedWithWallets";
import { reservedScreens } from "./constants";
import { HeadlessConnectUI } from "../wallets/headlessConnectUI";
import styled from "@emotion/styled";
import { scrollbar } from "../../design-system/styles";
export const ConnectModal = () => {
const { theme, title } = useContext(ModalConfigCtx);
export const ConnectModalContent = (props: {
screen: string | WalletConfig;
setScreen: (screen: string | WalletConfig) => void;
theme?: "light" | "dark";
title?: string;
}) => {
const { screen, setScreen } = props;
const modalConfig = useContext(ModalConfigCtx);
const title = props.title || modalConfig.title;
const theme = props.theme || modalConfig.theme;
const walletConfigs = useWallets();
const initialScreen =
walletConfigs.length === 1 && !walletConfigs[0].selectUI
? walletConfigs[0]
: reservedScreens.main;
const [screen, setScreen] = useState<string | WalletConfig>(initialScreen);
const isWalletModalOpen = useIsWalletModalOpen();
const setIsWalletModalOpen = useSetIsWalletModalOpen();
const connectionStatus = useConnectionStatus();
@@ -47,7 +63,13 @@ export const ConnectModal = () => {
}
setIsWalletModalOpen(false);
},
[setIsWalletModalOpen, initialScreen, connectionStatus, disconnect],
[
connectionStatus,
setIsWalletModalOpen,
setScreen,
initialScreen,
disconnect,
],
);
const handleBack = useCallback(() => {
@@ -95,6 +117,55 @@ export const ConnectModal = () => {
: darkTheme
}
>
{screen === reservedScreens.main && (
<WalletSelector
title={title}
walletConfigs={walletConfigs}
onGetStarted={() => {
setScreen(reservedScreens.getStarted);
}}
selectWallet={setScreen}
/>
)}
{screen === reservedScreens.getStarted && (
<GetStartedWithWallets onBack={handleBack} />
)}
{WalletConnectUI && (
<WalletConnectUI
supportedWallets={walletConfigs}
theme={theme}
goBack={handleBack}
close={handleClose}
isOpen={isWalletModalOpen}
open={() => {
setIsWalletModalOpen(true);
}}
walletConfig={screen}
selectionData={walletModalConfig.data}
setSelectionData={(data) => {
setWalletModalConfig((config) => ({
...config,
data,
}));
}}
/>
)}
</ThemeProvider>
);
};
export const ConnectModal = () => {
const { theme } = useContext(ModalConfigCtx);
const { screen, setScreen, initialScreen } = useScreen();
const isWalletModalOpen = useIsWalletModalOpen();
const setIsWalletModalOpen = useSetIsWalletModalOpen();
const connectionStatus = useConnectionStatus();
const disconnect = useDisconnect();
return (
<ThemeProvider theme={theme === "light" ? lightTheme : darkTheme}>
<Modal
style={{
maxWidth: "450px",
@@ -111,42 +182,62 @@ export const ConnectModal = () => {
}
}}
>
{screen === reservedScreens.main && (
<WalletSelector
title={title}
walletConfigs={walletConfigs}
onGetStarted={() => {
setScreen(reservedScreens.getStarted);
}}
selectWallet={setScreen}
/>
)}
{screen === reservedScreens.getStarted && (
<GetStartedWithWallets onBack={handleBack} />
)}
{WalletConnectUI && (
<WalletConnectUI
supportedWallets={walletConfigs}
theme={theme}
goBack={handleBack}
close={handleClose}
isOpen={isWalletModalOpen}
open={() => {
setIsWalletModalOpen(true);
}}
walletConfig={screen}
selectionData={walletModalConfig.data}
setSelectionData={(data) => {
setWalletModalConfig((config) => ({
...config,
data,
}));
}}
/>
)}
<ConnectModalContent screen={screen} setScreen={setScreen} />
</Modal>
</ThemeProvider>
);
};
export const ConnectModalInline = (props: {
theme?: "light" | "dark";
title?: string;
className?: string;
}) => {
const { screen, setScreen } = useScreen();
return (
<WalletUIStatesProvider theme={props.theme}>
<ThemeProvider theme={props.theme === "light" ? lightTheme : darkTheme}>
<ConnectModalInlineContainer className={props.className}>
<ConnectModalContent
screen={screen}
setScreen={setScreen}
theme={props.theme}
title={props.title}
/>
</ConnectModalInlineContainer>
</ThemeProvider>
</WalletUIStatesProvider>
);
};
function useScreen() {
const walletConfigs = useWallets();
const initialScreen =
walletConfigs.length === 1 && !walletConfigs[0].selectUI
? walletConfigs[0]
: reservedScreens.main;
const [screen, setScreen] = useState<string | WalletConfig>(initialScreen);
return {
screen,
setScreen,
initialScreen,
};
}
const ConnectModalInlineContainer = styled.div<{ theme?: Theme }>`
background: ${(p) => p.theme.bg.base};
padding: ${spacing.lg};
border-radius: ${radius.xl};
max-width: 450px;
width: 100%;
box-sizing: border-box;
max-height: 70vh;
overflow-y: auto;
${(p) =>
scrollbar({
track: "transparent",
thumb: p.theme.bg.elevated,
hover: p.theme.bg.highlighted,
})}
`;
@@ -1,15 +1,14 @@
import { ConnectUIProps, useConnect } from "@thirdweb-dev/react-core";
import { WalletConnect } from "@thirdweb-dev/wallets";
import { useEffect, useRef, useState } from "react";
import { isMobile } from "../../../evm/utils/isMobile";
import { ConnectingScreen } from "../../ConnectWallet/screens/ConnectingScreen";
import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen";
import { isMobile } from "../../../evm/utils/isMobile";
import { useEffect, useRef, useState } from "react";
import { RainbowScan } from "./RainbowScan";
import { WCOpenURI } from "../../ConnectWallet/screens/WCOpenUri";
import { GetStartedScreen } from "../../ConnectWallet/screens/GetStartedScreen";
import { RainbowWallet } from "@thirdweb-dev/wallets";
export const RainbowConnectUI = (props: ConnectUIProps<WalletConnect>) => {
export const RainbowConnectUI = (props: ConnectUIProps<RainbowWallet>) => {
const [screen, setScreen] = useState<
"connecting" | "scanning" | "get-started" | "open-wc-uri"
"connecting" | "scanning" | "get-started"
>("connecting");
const { walletConfig, close } = props;
const connect = useConnect();
@@ -24,14 +23,34 @@ export const RainbowConnectUI = (props: ConnectUIProps<WalletConnect>) => {
return;
}
const isInstalled = walletConfig.isInstalled
? walletConfig.isInstalled()
: false;
// if loading
(async () => {
// on mobile, open rainbow app link
if (isMobile()) {
setScreen("open-wc-uri");
} else {
// on desktop, show the rainbow scan qr code
setScreen("scanning");
if (isInstalled) {
try {
connectPrompted.current = true;
setScreen("connecting");
await connect(walletConfig);
close();
} catch (e) {
goBack();
}
}
// if rainbow is not injected
else {
// on mobile, open rainbow app link
if (isMobile()) {
window.open(
`https://rainbow.app.link/dapp/${window.location.toString()}`,
);
} else {
// on desktop, show the rainbow scan qr code
setScreen("scanning");
}
}
})();
}, [walletConfig, close, connect, goBack]);
@@ -43,24 +62,7 @@ export const RainbowConnectUI = (props: ConnectUIProps<WalletConnect>) => {
onBack={props.goBack}
walletName={walletConfig.meta.name}
walletIconURL={walletConfig.meta.iconURL}
supportLink="https://rainbow.me/"
/>
);
}
if (screen === "open-wc-uri") {
return (
<WCOpenURI
hideBackButton={hideBackButton}
onBack={props.goBack}
onConnected={close}
walletConfig={walletConfig}
supportLink="https://rainbow.me/"
appUriPrefix={{
ios: "rainbow://",
android: "https://rnbwapp.com/",
other: "https://rnbwapp.com/",
}}
supportLink="https://support.rainbow.io/hc/en-us/articles/4406430256539-User-Guide-Troubleshooting"
/>
);
}
@@ -83,7 +85,7 @@ export const RainbowConnectUI = (props: ConnectUIProps<WalletConnect>) => {
if (screen === "scanning") {
return (
<RainbowScan
onBack={props.goBack}
onBack={goBack}
onConnected={close}
onGetStarted={() => {
setScreen("get-started");
@@ -4,14 +4,14 @@ import {
useWalletContext,
} from "@thirdweb-dev/react-core";
import { useEffect, useRef, useState } from "react";
import type { WalletConnect } from "@thirdweb-dev/wallets";
import type { RainbowWallet } from "@thirdweb-dev/wallets";
import type { WalletConfig } from "@thirdweb-dev/react-core";
export const RainbowScan: React.FC<{
onBack: () => void;
onGetStarted: () => void;
onConnected: () => void;
walletConfig: WalletConfig<WalletConnect>;
walletConfig: WalletConfig<RainbowWallet>;
hideBackButton: boolean;
}> = ({ onBack, onConnected, onGetStarted, walletConfig, hideBackButton }) => {
const createInstance = useCreateWalletInstance();
@@ -1,8 +1,9 @@
import type { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { WalletConnect } from "@thirdweb-dev/wallets";
import {
RainbowWallet,
getInjectedRainbowProvider,
} from "@thirdweb-dev/wallets";
import { RainbowConnectUI } from "./RainbowConnectUI";
import { rainbowWalletUris } from "./rainbowWalletUris";
import { handelWCSessionRequest } from "../handleWCSessionRequest";
type RainbowWalletOptions = {
/**
@@ -16,33 +17,22 @@ type RainbowWalletOptions = {
export const rainbowWallet = (
options?: RainbowWalletOptions,
): WalletConfig<WalletConnect> => {
): WalletConfig<RainbowWallet> => {
return {
id: "rainbow",
meta: {
name: "Rainbow Wallet",
iconURL:
"ipfs://QmSZn47p4DVVBfzvg9BAX2EqwnPxkT1YAE7rUnrtd9CybQ/rainbow-logo.png",
urls: {
android: "https://rnbwapp.com/e/Va41HWS6Oxb",
ios: "https://rnbwapp.com/e/OeMdmkJ6Oxb",
},
},
id: RainbowWallet.id,
meta: RainbowWallet.meta,
create: (walletOptions: WalletOptions) => {
const wallet = new WalletConnect({
const wallet = new RainbowWallet({
...walletOptions,
walletId: "rainbow",
projectId: options?.projectId,
qrcode: false,
});
handelWCSessionRequest(wallet, rainbowWalletUris);
return wallet;
},
connectUI: RainbowConnectUI,
isInstalled() {
return false;
return !!getInjectedRainbowProvider();
},
};
};
+12
View File
@@ -1,5 +1,17 @@
# @thirdweb-dev/sdk
## 3.10.57
### Patch Changes
- [#1545](https://github.com/thirdweb-dev/js/pull/1545) [`adec589e`](https://github.com/thirdweb-dev/js/commit/adec589ead8ceff1b57169e05a3e6733b4652cc7) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose joinAbis() utility function
- [#1422](https://github.com/thirdweb-dev/js/pull/1422) [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3) Thanks [@Marfuen](https://github.com/Marfuen)! - CLI will now use an OAuth like login flow to authenticate the device with the wallet on the dashboard.
Any actions performed by the CLI will be linked to your account / wallet on the dashboard, including but not limited to tracking your usage, and billing as needed.
- Updated dependencies [[`b30566c6`](https://github.com/thirdweb-dev/js/commit/b30566c68436ad94ddc938a380eccc13a8a7147d), [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3), [`f5aed34d`](https://github.com/thirdweb-dev/js/commit/f5aed34d3c71065c3f45df2c1eb84ba9c36162d5)]:
- @thirdweb-dev/chains@0.1.49
- @thirdweb-dev/storage@1.2.9
## 3.10.56
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/sdk",
"version": "3.10.56",
"version": "3.10.57",
"description": "The main thirdweb SDK.",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
"license": "Apache-2.0",
+1
View File
@@ -28,6 +28,7 @@ export * from "./feature-detection/isFeatureEnabled";
export * from "./feature-detection/assertEnabled";
export * from "./feature-detection/detectContractFeature";
export * from "./feature-detection/hasFunction";
export * from "./plugin/joinABIs";
export * from "./version-checker";
export * from "./fetchSourceFilesFromMetadata";
+13 -13
View File
@@ -64,9 +64,8 @@ export function getChainProvider(
// if we still don't have an url fall back to just using the chainId or slug in the rpc and try that
if (!rpcUrl) {
rpcUrl = `https://${chainId || network}.rpc.thirdweb.com/${
options.clientId || ""
}`;
rpcUrl = `https://${chainId || network}.rpc.thirdweb.com/${options.clientId || ""
}`;
}
if (!rpcUrl) {
@@ -241,6 +240,7 @@ export function getProviderFromRpcUrl(
headers["authorization"] = `Bearer ${
(globalThis as any).TW_AUTH_TOKEN as string
}`;
headers["x-authorize-wallet"] = "true";
authStrategy = "twAuthToken";
}
@@ -278,18 +278,18 @@ export function getProviderFromRpcUrl(
// Otherwise, create a new provider on the specific network
const newProvider = chainId
? // If we know the chainId we should use the StaticJsonRpcBatchProvider
new StaticJsonRpcBatchProvider(
{
url: rpcUrl,
headers,
},
chainId,
)
: // Otherwise fall back to the built in json rpc batch provider
new providers.JsonRpcBatchProvider({
new StaticJsonRpcBatchProvider(
{
url: rpcUrl,
headers,
});
},
chainId,
)
: // Otherwise fall back to the built in json rpc batch provider
new providers.JsonRpcBatchProvider({
url: rpcUrl,
headers,
});
// Save the provider in our cache
RPC_PROVIDER_MAP.set(seralizedOpts, newProvider);
+4
View File
@@ -0,0 +1,4 @@
declare module globalThis {
// eslint-disable-next-line no-var
var TW_AUTH_TOKEN: string;
}
+7
View File
@@ -1,5 +1,12 @@
# @thirdweb-dev/service-utils
## 0.4.3
### Patch Changes
- [#1422](https://github.com/thirdweb-dev/js/pull/1422) [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3) Thanks [@Marfuen](https://github.com/Marfuen)! - CLI will now use an OAuth like login flow to authenticate the device with the wallet on the dashboard.
Any actions performed by the CLI will be linked to your account / wallet on the dashboard, including but not limited to tracking your usage, and billing as needed.
## 0.4.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/service-utils",
"version": "0.4.2",
"version": "0.4.3",
"main": "dist/thirdweb-dev-service-utils.cjs.js",
"module": "dist/thirdweb-dev-service-utils.esm.js",
"exports": {
+3 -1
View File
@@ -6,6 +6,7 @@ export type CoreServiceConfig = {
serviceScope: ServiceName;
serviceApiKey: string;
serviceAction?: string;
useWalletAuth?: boolean;
};
export type ApiKeyMetadata = {
@@ -73,9 +74,10 @@ export async function fetchKeyMetadataFromApi(
export async function fetchAccountFromApi(
jwt: string,
config: CoreServiceConfig,
useWalletAuth: boolean,
): Promise<ApiAccountResponse> {
const { apiUrl, serviceApiKey } = config;
const url = `${apiUrl}/v1/account/me`;
const url = useWalletAuth ? `${apiUrl}/v1/wallet/me` : `${apiUrl}/v1/account/me`
const response = await fetch(url, {
method: "GET",
headers: {
@@ -18,6 +18,8 @@ export type AuthorizationInput = {
jwt: string | null;
hashedJWT: string | null;
targetAddress?: string | string[];
// IMPORTANT: this is a stringified boolean! Only pass in true or false here. IK it's not ideal, but it's required to pass it in the headers.
useWalletAuth?: string | null;
};
type CacheOptions = {
@@ -31,16 +33,16 @@ type CacheOptions = {
type ApiKeyCacheWithPossibleTTL =
| {
apiKeyMeta: ApiKeyMetadata;
updatedAt: number;
}
apiKeyMeta: ApiKeyMetadata;
updatedAt: number;
}
| ApiKeyMetadata;
type AccountCacheWithPossibleTTL =
| {
apiKeyMeta: AccountMetadata;
updatedAt: number;
}
apiKeyMeta: AccountMetadata;
updatedAt: number;
}
| AccountMetadata;
export async function authorize(
@@ -48,7 +50,7 @@ export async function authorize(
serviceConfig: CoreServiceConfig,
cacheOptions?: CacheOptions,
): Promise<AuthorizationResult> {
const { clientId, targetAddress, secretKeyHash, jwt, hashedJWT } = authData;
const { clientId, targetAddress, secretKeyHash, jwt, hashedJWT, useWalletAuth } = authData;
const { enforceAuth } = serviceConfig;
// BACKWARDS COMPAT: if auth not enforced and we don't have auth credentials bypass
@@ -89,7 +91,7 @@ export async function authorize(
}
if (!accountMeta) {
try {
const { data, error } = await fetchAccountFromApi(jwt, serviceConfig);
const { data, error } = await fetchAccountFromApi(jwt, serviceConfig, useWalletAuth?.toLowerCase() === "true");
if (error) {
return {
authorized: false,
+8 -1
View File
@@ -20,7 +20,7 @@ export async function authorizeNode(
authInput: AuthInput,
serviceConfig: NodeServiceConfig,
): Promise<AuthorizationResult> {
let authData;
let authData: AuthorizationInput;
try {
authData = extractAuthorizationData(authInput);
} catch (e) {
@@ -121,12 +121,18 @@ export function extractAuthorizationData(
}
let jwt: null | string = null;
let useWalletAuth: null | string = null;
// check for authorization header on the request
const authorizationHeader = getHeader(headers, "authorization");
if (authorizationHeader) {
const [type, token] = authorizationHeader.split(" ");
if (type.toLowerCase() === "bearer" && !!token) {
jwt = token;
const walletAuthHeader = getHeader(headers, "x-authorize-wallet");
// IK a stringified boolean is not ideal, but it's required to pass it in the headers.
if (walletAuthHeader?.toLowerCase() === "true") {
useWalletAuth = walletAuthHeader;
}
}
}
@@ -139,6 +145,7 @@ export function extractAuthorizationData(
origin,
bundleId,
targetAddress: authInput.targetAddress,
useWalletAuth,
};
}
+7
View File
@@ -1,5 +1,12 @@
# @thirdweb-dev/storage
## 1.2.9
### Patch Changes
- [#1422](https://github.com/thirdweb-dev/js/pull/1422) [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3) Thanks [@Marfuen](https://github.com/Marfuen)! - CLI will now use an OAuth like login flow to authenticate the device with the wallet on the dashboard.
Any actions performed by the CLI will be linked to your account / wallet on the dashboard, including but not limited to tracking your usage, and billing as needed.
## 1.2.8
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/storage",
"version": "1.2.8",
"version": "1.2.9",
"main": "dist/thirdweb-dev-storage.cjs.js",
"module": "dist/thirdweb-dev-storage.esm.js",
"browser": {
@@ -104,9 +104,8 @@ export class StorageDownloader implements IStorageDownloader {
) {
headers = {
...headers,
authorization: `Bearer ${
(globalThis as any).TW_AUTH_TOKEN as string
}`,
authorization: `Bearer ${(globalThis as any).TW_AUTH_TOKEN as string}`,
"x-authorize-wallet": 'true',
};
}
@@ -96,9 +96,8 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
extensions = file.name.substring(extensionStartIndex);
}
}
fileName = `${
i + options.rewriteFileNames.fileStartNumber
}${extensions}`;
fileName = `${i + options.rewriteFileNames.fileStartNumber
}${extensions}`;
} else {
fileName = `${file.name}`;
}
@@ -300,6 +299,10 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
"authorization",
`Bearer ${(globalThis as any).TW_AUTH_TOKEN as string}`,
);
xhr.setRequestHeader(
"x-authorize-wallet",
`true`,
);
}
xhr.send(form as any);
@@ -316,6 +319,7 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
}
const headers: HeadersInit = {};
if (this.secretKey) {
headers["x-secret-key"] = this.secretKey;
} else if (this.clientId) {
@@ -333,9 +337,9 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
"TW_AUTH_TOKEN" in globalThis &&
typeof (globalThis as any).TW_AUTH_TOKEN === "string"
) {
headers["authorization"] = `Bearer ${
(globalThis as any).TW_AUTH_TOKEN as string
}`;
headers["authorization"] = `Bearer ${(globalThis as any).TW_AUTH_TOKEN as string
}`;
headers["x-authorize-wallet"] = "true";
}
const res = await fetch(`${this.uploadServerUrl}/ipfs/upload`, {
@@ -346,8 +350,13 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
},
body: form.getBuffer(),
});
if (!res.ok) {
console.warn(await res.text());
if (res.status === 401) {
throw new Error(
"Unauthorized - You don't have permission to use this service.",
);
}
throw new Error("Failed to upload files to IPFS");
}
+4
View File
@@ -0,0 +1,4 @@
declare module globalThis {
// eslint-disable-next-line no-var
var TW_AUTH_TOKEN: string;
}
+1 -1
View File
@@ -426,7 +426,7 @@ describe("IPFS", async () => {
getGatewayUrlForCid(
authorizedUrls["ipfs://"][0],
// CID changes based on file contents (prod gateway vs staging gateway since they get written)
`bafybeie4otkctycxgas34kjsvapcbqspqfj2xzjvyaet26i3nvlxv56oxy/0`,
`bafybeicn4fmtzb7kcg5idlzoo7ahomt5khqyz7lrpv2r5zz5n7sb4s2uvm/0`,
),
);
});
+11
View File
@@ -1,5 +1,16 @@
# @thirdweb-dev/unity-js-bridge
## 0.2.75
### Patch Changes
- Updated dependencies [[`eb463735`](https://github.com/thirdweb-dev/js/commit/eb463735e2f784cd1d212a982835af95cf60766b), [`adec589e`](https://github.com/thirdweb-dev/js/commit/adec589ead8ceff1b57169e05a3e6733b4652cc7), [`b30566c6`](https://github.com/thirdweb-dev/js/commit/b30566c68436ad94ddc938a380eccc13a8a7147d), [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3), [`f5aed34d`](https://github.com/thirdweb-dev/js/commit/f5aed34d3c71065c3f45df2c1eb84ba9c36162d5)]:
- @thirdweb-dev/wallets@1.1.21
- @thirdweb-dev/sdk@3.10.57
- @thirdweb-dev/chains@0.1.49
- @thirdweb-dev/storage@1.2.9
- @thirdweb-dev/auth@3.2.38
## 0.2.74
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/unity-js-bridge",
"version": "0.2.74",
"version": "0.2.75",
"main": "dist/thirdweb-unity-bridge.js",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
"license": "Apache-2.0",
+12
View File
@@ -1,5 +1,17 @@
# @thirdweb-dev/wallets
## 1.1.21
### Patch Changes
- [#1544](https://github.com/thirdweb-dev/js/pull/1544) [`eb463735`](https://github.com/thirdweb-dev/js/commit/eb463735e2f784cd1d212a982835af95cf60766b) Thanks [@iketw](https://github.com/iketw)! - Adds support for Rainbow's new browser extension
- [#1422](https://github.com/thirdweb-dev/js/pull/1422) [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3) Thanks [@Marfuen](https://github.com/Marfuen)! - CLI will now use an OAuth like login flow to authenticate the device with the wallet on the dashboard.
Any actions performed by the CLI will be linked to your account / wallet on the dashboard, including but not limited to tracking your usage, and billing as needed.
- Updated dependencies [[`adec589e`](https://github.com/thirdweb-dev/js/commit/adec589ead8ceff1b57169e05a3e6733b4652cc7), [`b30566c6`](https://github.com/thirdweb-dev/js/commit/b30566c68436ad94ddc938a380eccc13a8a7147d), [`0f027069`](https://github.com/thirdweb-dev/js/commit/0f027069064bebe647f9235fa86ef7f165ffc7b3), [`f5aed34d`](https://github.com/thirdweb-dev/js/commit/f5aed34d3c71065c3f45df2c1eb84ba9c36162d5)]:
- @thirdweb-dev/sdk@3.10.57
- @thirdweb-dev/chains@0.1.49
## 1.1.20
### Patch Changes
+1 -1
View File
@@ -37,7 +37,7 @@ await wallet.connect();
- Private key
- AWS KMs
- AWS Sercret Manager
- AWS Secret Manager
#### Documentation
@@ -0,0 +1,7 @@
{
"main": "dist/thirdweb-dev-wallets-evm-connectors-rainbow.cjs.js",
"module": "dist/thirdweb-dev-wallets-evm-connectors-rainbow.esm.js",
"browser": {
"./dist/thirdweb-dev-wallets-evm-connectors-rainbow.esm.js": "./dist/thirdweb-dev-wallets-evm-connectors-rainbow.browser.esm.js"
}
}
@@ -0,0 +1,7 @@
{
"main": "dist/thirdweb-dev-wallets-evm-wallets-rainbow-wallet.cjs.js",
"module": "dist/thirdweb-dev-wallets-evm-wallets-rainbow-wallet.esm.js",
"browser": {
"./dist/thirdweb-dev-wallets-evm-wallets-rainbow-wallet.esm.js": "./dist/thirdweb-dev-wallets-evm-wallets-rainbow-wallet.browser.esm.js"
}
}
+15 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/wallets",
"version": "1.1.20",
"version": "1.1.21",
"main": "dist/thirdweb-dev-wallets.cjs.js",
"module": "dist/thirdweb-dev-wallets.esm.js",
"types": "dist/thirdweb-dev-wallets.cjs.d.ts",
@@ -197,6 +197,13 @@
},
"default": "./evm/connectors/trust/dist/thirdweb-dev-wallets-evm-connectors-trust.cjs.js"
},
"./evm/wallets/rainbow-wallet": {
"module": {
"browser": "./evm/wallets/rainbow-wallet/dist/thirdweb-dev-wallets-evm-wallets-rainbow-wallet.browser.esm.js",
"default": "./evm/wallets/rainbow-wallet/dist/thirdweb-dev-wallets-evm-wallets-rainbow-wallet.esm.js"
},
"default": "./evm/wallets/rainbow-wallet/dist/thirdweb-dev-wallets-evm-wallets-rainbow-wallet.cjs.js"
},
"./evm/wallets/wallet-connect": {
"module": {
"browser": "./evm/wallets/wallet-connect/dist/thirdweb-dev-wallets-evm-wallets-wallet-connect.browser.esm.js",
@@ -232,6 +239,13 @@
},
"default": "./evm/wallets/coinbase-wallet/dist/thirdweb-dev-wallets-evm-wallets-coinbase-wallet.cjs.js"
},
"./evm/connectors/rainbow": {
"module": {
"browser": "./evm/connectors/rainbow/dist/thirdweb-dev-wallets-evm-connectors-rainbow.browser.esm.js",
"default": "./evm/connectors/rainbow/dist/thirdweb-dev-wallets-evm-connectors-rainbow.esm.js"
},
"default": "./evm/connectors/rainbow/dist/thirdweb-dev-wallets-evm-connectors-rainbow.cjs.js"
},
"./evm/connectors/injected": {
"module": {
"browser": "./evm/connectors/injected/dist/thirdweb-dev-wallets-evm-connectors-injected.browser.esm.js",
@@ -0,0 +1,67 @@
import { Ethereum } from "../injected/types";
import { assertWindowEthereum } from "../../utils/assertWindowEthereum";
export function getInjectedRainbowProvider(): Ethereum | undefined {
if (typeof window === "undefined") {
return;
}
function getReady(ethereum?: Ethereum): Ethereum | undefined {
const isRainbow = !!ethereum?.isRainbow;
if (!isRainbow) {
return;
}
// Brave tries to make itself look like MetaMask
// Could also try RPC `web3_clientVersion` if following is unreliable
if (ethereum.isBraveWallet && !ethereum._events && !ethereum._state) {
return;
}
if (ethereum.isPhantom) {
return;
}
if (ethereum.isAvalanche) {
return;
}
if (ethereum.isBitKeep) {
return;
}
if (ethereum.isMathWallet) {
return;
}
if (ethereum.isZerion) {
return;
}
if (ethereum.isKuCoinWallet) {
return;
}
if (ethereum.isPortal) {
return;
}
if (ethereum.isTokenPocket) {
return;
}
if (ethereum.isTokenary) {
return;
}
return ethereum;
}
if (assertWindowEthereum(globalThis.window)) {
if (globalThis.window.ethereum?.providers) {
return globalThis.window.ethereum.providers.find(getReady);
}
return getReady(globalThis.window.ethereum);
}
}
@@ -0,0 +1,156 @@
import { AsyncStorage } from "../../../core/AsyncStorage";
import {
ConnectorNotFoundError,
ResourceUnavailableError,
RpcError,
UserRejectedRequestError,
} from "../../../lib/wagmi-core/errors";
import { walletIds } from "../../constants/walletIds";
import { InjectedConnector, InjectedConnectorOptions } from "../injected";
import type { Chain } from "@thirdweb-dev/chains";
import { utils } from "ethers";
import { getInjectedRainbowProvider } from "./getInjectedRainbowProvider";
export type RainbowConnectorOptions = Pick<
InjectedConnectorOptions,
"shimDisconnect"
> & {
/**
* While "disconnected" with `shimDisconnect`, allows user to select a different Rainbow account (than the currently connected account) when trying to connect.
*/
UNSTABLE_shimOnConnectSelectAccount?: boolean;
};
type RainbowConnectorConstructorArg = {
chains?: Chain[];
connectorStorage: AsyncStorage;
options?: RainbowConnectorOptions;
};
export class RainbowConnector extends InjectedConnector {
readonly id = walletIds.rainbow;
#UNSTABLE_shimOnConnectSelectAccount: RainbowConnectorOptions["UNSTABLE_shimOnConnectSelectAccount"];
constructor(arg: RainbowConnectorConstructorArg) {
const defaultOptions = {
name: "Rainbow Wallet",
shimDisconnect: true,
shimChainChangedDisconnect: true,
getProvider: getInjectedRainbowProvider,
};
const options = {
...defaultOptions,
...arg.options,
};
super({
chains: arg.chains,
options,
connectorStorage: arg.connectorStorage,
});
this.#UNSTABLE_shimOnConnectSelectAccount =
options.UNSTABLE_shimOnConnectSelectAccount;
}
/**
* Connect to injected Rainbow provider
*/
async connect(options: { chainId?: number } = {}) {
try {
const provider = await this.getProvider();
if (!provider) {
throw new ConnectorNotFoundError();
}
this.setupListeners();
// emit "connecting" event
this.emit("message", { type: "connecting" });
// Attempt to show wallet select prompt with `wallet_requestPermissions` when
// `shimDisconnect` is active and account is in disconnected state (flag in storage)
let account: string | null = null;
if (
this.#UNSTABLE_shimOnConnectSelectAccount &&
this.options?.shimDisconnect &&
!Boolean(this.connectorStorage.getItem(this.shimDisconnectKey))
) {
account = await this.getAccount().catch(() => null);
const isConnected = !!account;
if (isConnected) {
// Attempt to show another prompt for selecting wallet if already connected
try {
await provider.request({
method: "wallet_requestPermissions",
params: [{ eth_accounts: {} }],
});
} catch (error) {
// Not all Rainbow injected providers support `wallet_requestPermissions` (e.g. Rainbow iOS).
// Only bubble up error if user rejects request
if (this.isUserRejectedRequestError(error)) {
throw new UserRejectedRequestError(error);
}
}
}
}
// if account is not already set, request accounts and use the first account
if (!account) {
const accounts = await provider.request({
method: "eth_requestAccounts",
});
account = utils.getAddress(accounts[0] as string);
}
// get currently connected chainId
let connectedChainId = await this.getChainId();
// check if connected chain is unsupported
let isUnsupported = this.isChainUnsupported(connectedChainId);
// if chainId is given, but does not match the currently connected chainId, switch to the given chainId
if (options.chainId && connectedChainId !== options.chainId) {
try {
await this.switchChain(options.chainId);
// recalculate the chainId and isUnsupported
connectedChainId = options.chainId;
isUnsupported = this.isChainUnsupported(options.chainId);
} catch (e) {
console.error(`Could not switch to chain id : ${options.chainId}`, e);
}
}
// if shimDisconnect is enabled
if (this.options?.shimDisconnect) {
// add shimDisconnectKey in storage - this signals that connector is "connected"
await this.connectorStorage.setItem(this.shimDisconnectKey, "true");
}
const connectionInfo = {
chain: { id: connectedChainId, unsupported: isUnsupported },
provider: provider,
account,
};
this.emit("connect", connectionInfo);
return connectionInfo;
} catch (error) {
if (this.isUserRejectedRequestError(error)) {
throw new UserRejectedRequestError(error);
}
if ((error as RpcError).code === -32002) {
throw new ResourceUnavailableError(error);
}
throw error;
}
}
async switchAccount() {
const provider = await this.getProvider();
await provider.request({
method: "wallet_requestPermissions",
params: [{ eth_accounts: {} }],
});
}
}
@@ -0,0 +1,4 @@
declare module globalThis {
// eslint-disable-next-line no-var
var TW_AUTH_TOKEN: string;
}
@@ -54,6 +54,7 @@ export class HttpRpcClient {
headers["authorization"] = `Bearer ${
(globalThis as any).TW_AUTH_TOKEN as string
}`;
headers["x-authorize-wallet"] = "true";
}
headers["x-sdk-version"] = pkg.version;
@@ -34,6 +34,12 @@ class VerifyingPaymasterAPI extends PaymasterAPI {
};
if (isTwUrl(this.paymasterUrl)) {
if (this.secretKey && this.clientId) {
throw new Error(
"Cannot use both secret key and client ID. Please use secretKey for server-side applications and clientId for client-side applications.",
);
}
if (this.secretKey) {
headers["x-secret-key"] = this.secretKey;
} else if (this.clientId) {
@@ -51,9 +57,8 @@ class VerifyingPaymasterAPI extends PaymasterAPI {
"TW_AUTH_TOKEN" in globalThis &&
typeof (globalThis as any).TW_AUTH_TOKEN === "string"
) {
headers["authorization"] = `Bearer ${
(globalThis as any).TW_AUTH_TOKEN as string
}`;
headers["authorization"] = `Bearer ${(globalThis as any).TW_AUTH_TOKEN as string}`;
headers["x-authorize-wallet"] = "true";
}
}
@@ -1,14 +1,16 @@
export const walletIds = {
blocto: "blocto",
coinbase: "coinbase",
frame: "frame",
metamask: "metamask",
localWallet: "localWallet",
magicLink: "magicLink",
metamask: "metamask",
paper: "paper",
rainbow: "rainbowWallet",
smartWallet: "smartWallet",
safe: "safe",
trust: "trust",
walletConnectV1: "walletConnectV1",
walletConnect: "walletConnect",
magicLink: "magicLink",
paper: "paper",
blocto: "blocto",
// add new ids sorted alphabetically
};
+2
View File
@@ -30,6 +30,7 @@ export * from "./wallets/local-wallet";
export * from "./wallets/frame";
export * from "./wallets/injected";
export * from "./wallets/metamask";
export * from "./wallets/rainbow-wallet";
export * from "./wallets/wallet-connect";
export * from "./wallets/wallet-connect-v1";
export * from "./wallets/safe";
@@ -46,3 +47,4 @@ export type { Chain } from "@thirdweb-dev/chains";
// export the window ethereum util
export { assertWindowEthereum } from "./utils/assertWindowEthereum";
export { getInjectedMetamaskProvider } from "./connectors/metamask/getInjectedMetamaskProvider";
export { getInjectedRainbowProvider } from "./connectors/rainbow/getInjectedRainbowProvider";
@@ -380,85 +380,85 @@ export class LocalWallet extends AbstractClientWallet<
type DecryptOptions =
| {
decrypt?: (message: string, password: string) => Promise<string>;
password: string;
}
decrypt?: (message: string, password: string) => Promise<string>;
password: string;
}
| false;
type EncryptOptions =
| {
encrypt?: (message: string, password: string) => Promise<string>;
password: string;
}
encrypt?: (message: string, password: string) => Promise<string>;
password: string;
}
| false;
type ImportOptions =
| {
privateKey: string;
encryption: DecryptOptions;
}
privateKey: string;
encryption: DecryptOptions;
}
| {
mnemonic: string;
encryption: DecryptOptions;
}
mnemonic: string;
encryption: DecryptOptions;
}
| {
encryptedJson: string;
password: string;
};
encryptedJson: string;
password: string;
};
type LoadOptions =
| {
strategy: "encryptedJson";
password: string;
storage?: AsyncStorage;
}
strategy: "encryptedJson";
password: string;
storage?: AsyncStorage;
}
| {
strategy: "privateKey";
storage?: AsyncStorage;
encryption: DecryptOptions;
}
strategy: "privateKey";
storage?: AsyncStorage;
encryption: DecryptOptions;
}
| {
strategy: "mnemonic";
storage?: AsyncStorage;
encryption: DecryptOptions;
};
strategy: "mnemonic";
storage?: AsyncStorage;
encryption: DecryptOptions;
};
// omit the mnemonic strategy option from LoadOptions
type LoadOrCreateOptions =
| {
strategy: "encryptedJson";
password: string;
storage?: AsyncStorage;
}
strategy: "encryptedJson";
password: string;
storage?: AsyncStorage;
}
| {
strategy: "privateKey";
storage?: AsyncStorage;
encryption: DecryptOptions;
};
strategy: "privateKey";
storage?: AsyncStorage;
encryption: DecryptOptions;
};
type SaveOptions =
| { strategy: "encryptedJson"; password: string; storage?: AsyncStorage }
| {
strategy: "privateKey";
encryption: EncryptOptions;
storage?: AsyncStorage;
}
strategy: "privateKey";
encryption: EncryptOptions;
storage?: AsyncStorage;
}
| {
strategy: "mnemonic";
encryption: EncryptOptions;
storage?: AsyncStorage;
};
strategy: "mnemonic";
encryption: EncryptOptions;
storage?: AsyncStorage;
};
type ExportOptions =
| { strategy: "encryptedJson"; password: string }
| {
strategy: "privateKey";
encryption: EncryptOptions;
}
strategy: "privateKey";
encryption: EncryptOptions;
}
| {
strategy: "mnemonic";
encryption: EncryptOptions;
};
strategy: "mnemonic";
encryption: EncryptOptions;
};
async function defaultEncrypt(message: string, password: string) {
const cryptoJS = (await import("crypto-js")).default;
@@ -481,7 +481,7 @@ function getDecryptor(encryption: DecryptOptions | undefined) {
const noop = async (msg: string) => msg;
return encryption
? (msg: string) =>
(encryption.decrypt || defaultDecrypt)(msg, encryption.password)
(encryption.decrypt || defaultDecrypt)(msg, encryption.password)
: noop;
}
@@ -496,7 +496,7 @@ function getEncryptor(encryption: EncryptOptions | undefined) {
const noop = async (msg: string) => msg;
return encryption
? (msg: string) =>
(encryption.encrypt || defaultEncrypt)(msg, encryption.password)
(encryption.encrypt || defaultEncrypt)(msg, encryption.password)
: noop;
}
@@ -0,0 +1,161 @@
import type { WalletConnectConnector as WalletConnectConnectorType } from "../connectors/wallet-connect";
import type { QRModalOptions } from "../connectors/wallet-connect/qrModalOptions";
import { Connector, WagmiAdapter } from "../interfaces/connector";
import { AbstractClientWallet, WalletOptions } from "./base";
import type { RainbowConnector as RainbowConnectorType } from "../connectors/rainbow";
import { walletIds } from "../constants/walletIds";
import { TW_WC_PROJECT_ID } from "../constants/wc";
import { getInjectedRainbowProvider } from "../connectors/rainbow/getInjectedRainbowProvider";
type RainbowAdditionalOptions = {
/**
* Whether to open the default Wallet Connect QR code Modal for connecting to Zerion Wallet on mobile if Zerion is not injected when calling connect().
*/
qrcode?: boolean;
/**
* When connecting Rainbow using the QR Code - Wallet Connect connector is used which requires a project id.
* This project id is Your project’s unique identifier for wallet connect that can be obtained at cloud.walletconnect.com.
*
* https://docs.walletconnect.com/2.0/web3modal/options#projectid-required
*/
projectId?: string;
/**
* options to customize the Wallet Connect QR Code Modal ( only relevant when qrcode is true )
*
* https://docs.walletconnect.com/2.0/web3modal/options
*/
qrModalOptions?: QRModalOptions;
};
export type RainbowWalletOptions = WalletOptions<RainbowAdditionalOptions>;
type ConnectWithQrCodeArgs = {
chainId?: number;
onQrCodeUri: (uri: string) => void;
onConnected: (accountAddress: string) => void;
};
export class RainbowWallet extends AbstractClientWallet<RainbowAdditionalOptions> {
connector?: Connector;
walletConnectConnector?: WalletConnectConnectorType;
rainbowConnector?: RainbowConnectorType;
isInjected: boolean;
static meta = {
name: "Rainbow Wallet",
iconURL:
"ipfs://QmSZn47p4DVVBfzvg9BAX2EqwnPxkT1YAE7rUnrtd9CybQ/rainbow-logo.png",
urls: {
chrome:
"https://chrome.google.com/webstore/detail/rainbow/opfgelmcmbiajamepnmloijbpoleiama",
android: "https://rnbwapp.com/e/Va41HWS6Oxb",
ios: "https://rnbwapp.com/e/OeMdmkJ6Oxb",
},
};
static id = walletIds.rainbow;
public get walletName() {
return "Rainbow Wallet" as const;
}
constructor(options: RainbowWalletOptions) {
super(RainbowWallet.id, options);
this.isInjected = !!getInjectedRainbowProvider();
}
protected async getConnector(): Promise<Connector> {
if (!this.connector) {
// if rainbow is injected, use the injected connector
// otherwise, use the wallet connect connector for using the rainbow app on mobile via QR code scan
if (this.isInjected) {
// import the connector dynamically
const { RainbowConnector } = await import("../connectors/rainbow");
const rainbowConnector = new RainbowConnector({
chains: this.chains,
connectorStorage: this.walletStorage,
options: {
shimDisconnect: true,
},
});
this.rainbowConnector = rainbowConnector;
this.connector = new WagmiAdapter(rainbowConnector);
} else {
const { WalletConnectConnector } = await import(
"../connectors/wallet-connect"
);
const walletConnectConnector = new WalletConnectConnector({
chains: this.chains,
options: {
projectId: this.options?.projectId || TW_WC_PROJECT_ID, // TODO,
storage: this.walletStorage,
qrcode: this.options?.qrcode,
dappMetadata: this.dappMetadata,
qrModalOptions: this.options?.qrModalOptions,
},
});
walletConnectConnector.getProvider().then((provider) => {
provider.signer.client.on("session_request_sent", () => {
this.emit("wc_session_request_sent");
});
});
// need to save this for getting the QR code URI
this.walletConnectConnector = walletConnectConnector;
this.connector = new WagmiAdapter(walletConnectConnector);
}
}
return this.connector;
}
/**
* connect to wallet with QR code
*
* @example
* ```typescript
* rainbow.connectWithQrCode({
* chainId: 1,
* onQrCodeUri(qrCodeUri) {
* // render the QR code with `qrCodeUri`
* },
* onConnected(accountAddress) {
* // update UI to show connected state
* },
* })
* ```
*/
async connectWithQrCode(options: ConnectWithQrCodeArgs) {
await this.getConnector();
const wcConnector = this.walletConnectConnector;
if (!wcConnector) {
throw new Error("WalletConnect connector not found");
}
const wcProvider = await wcConnector.getProvider();
// set a listener for display_uri event
wcProvider.on("display_uri", (uri) => {
options.onQrCodeUri(uri);
});
// trigger connect flow
this.connect({ chainId: options.chainId }).then(options.onConnected);
}
async switchAccount() {
if (!this.rainbowConnector) {
throw new Error("Can not switch Account");
}
await this.rainbowConnector.switchAccount();
}
}
+62 -72
View File
@@ -239,6 +239,9 @@ importers:
'@iarna/toml':
specifier: ^2.2.5
version: 2.2.5
'@thirdweb-dev/auth':
specifier: workspace:*
version: link:../auth
'@thirdweb-dev/chains':
specifier: workspace:*
version: link:../chains
@@ -248,6 +251,9 @@ importers:
'@thirdweb-dev/storage':
specifier: workspace:*
version: link:../storage
'@thirdweb-dev/wallets':
specifier: workspace:*
version: link:../wallets
async-retry:
specifier: ^1.3.3
version: 1.3.3
@@ -287,9 +293,6 @@ importers:
solc:
specifier: 0.8.17
version: 0.8.17
sync-disk-cache:
specifier: ^2.1.0
version: 2.1.0
tar:
specifier: ^6.1.15
version: 6.1.15
@@ -299,6 +302,9 @@ importers:
validate-npm-package-name:
specifier: ^4.0.0
version: 4.0.0
xdg-app-paths:
specifier: ^8.3.0
version: 8.3.0
yaml:
specifier: ^2.3.1
version: 2.3.1
@@ -1585,7 +1591,7 @@ packages:
'@account-abstraction/contracts': 0.5.0
'@ethersproject/abi': 5.7.0
'@ethersproject/providers': 5.7.2
'@openzeppelin/contracts': 4.8.3
'@openzeppelin/contracts': 4.9.3
debug: 4.3.4([email protected])
ethers: 5.7.2
transitivePeerDependencies:
@@ -2308,13 +2314,6 @@ packages:
dependencies:
'@babel/types': 7.22.5
/@babel/[email protected]:
resolution: {integrity: sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/types': 7.22.5
dev: false
/@babel/[email protected]:
resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
engines: {node: '>=6.9.0'}
@@ -3599,7 +3598,7 @@ packages:
'@babel/helper-environment-visitor': 7.22.5
'@babel/helper-function-name': 7.22.5
'@babel/helper-hoist-variables': 7.22.5
'@babel/helper-split-export-declaration': 7.22.5
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.22.7
'@babel/types': 7.22.5
debug: 4.3.4([email protected])
@@ -3786,7 +3785,7 @@ packages:
outdent: 0.5.0
prettier: 2.8.8
resolve-from: 5.0.0
semver: 7.5.3
semver: 7.5.4
dev: false
/@changesets/[email protected]:
@@ -3797,7 +3796,7 @@ packages:
'@changesets/get-dependents-graph': 1.3.6
'@changesets/types': 5.2.1
'@manypkg/get-packages': 1.1.3
semver: 7.5.3
semver: 7.5.4
dev: false
/@changesets/[email protected]:
@@ -6125,7 +6124,7 @@ packages:
'@magic-sdk/provider': 17.4.0([email protected])
'@magic-sdk/types': 15.6.0
'@react-native-async-storage/async-storage': 1.18.2([email protected])
'@types/lodash': 4.14.194
'@types/lodash': 4.14.195
buffer: 5.6.1
localforage: 1.10.0
localforage-driver-memory: 1.0.5([email protected])
@@ -7180,10 +7179,6 @@ packages:
resolution: {integrity: sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA==}
dev: false
/@openzeppelin/[email protected]:
resolution: {integrity: sha512-bQHV8R9Me8IaJoJ2vPG4rXcL7seB7YVuskr4f+f5RyOStSZetwzkWtoqDMl5erkBJy0lDRUnIR2WIkPiC0GJlg==}
dev: false
/@openzeppelin/[email protected]:
resolution: {integrity: sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg==}
dev: false
@@ -10837,8 +10832,8 @@ packages:
'@types/node': 18.17.1
dev: true
/@types/[email protected]4:
resolution: {integrity: sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==}
/@types/[email protected]5:
resolution: {integrity: sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg==}
dev: false
/@types/[email protected]:
@@ -15500,9 +15495,14 @@ packages:
engines: {node: '>=10.0.0'}
dev: true
/engine.io@6.4.1:
resolution: {integrity: sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==}
/engine.io[email protected].1:
resolution: {integrity: sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==}
engines: {node: '>=10.0.0'}
dev: true
/[email protected]:
resolution: {integrity: sha512-IXsMcGpw/xRfjra46sVZVHiSWo/nJ/3g1337q9KNXtS6YRzbW5yIzTCb9DjhrBe7r3GZQR0I4+nq+4ODk5g/cA==}
engines: {node: '>=10.2.0'}
dependencies:
'@types/cookie': 0.4.1
'@types/cors': 2.8.13
@@ -15512,7 +15512,7 @@ packages:
cookie: 0.4.2
cors: 2.8.5
debug: 4.3.4([email protected])
engine.io-parser: 5.0.6
engine.io-parser: 5.2.1
ws: 8.11.0
transitivePeerDependencies:
- bufferutil
@@ -16223,7 +16223,7 @@ packages:
minimatch: 3.1.2
object.values: 1.1.6
resolve: 1.22.1
semver: 7.5.3
semver: 7.5.4
tsconfig-paths: 3.14.2
transitivePeerDependencies:
- eslint-import-resolver-typescript
@@ -18251,12 +18251,6 @@ packages:
tslib: 2.5.0
dev: true
/[email protected]:
resolution: {integrity: sha512-o9bd30+5vLBvBtzCPwwGqpry2+n0Hi6H1+qwt6y+0kwRHGGF8TFIhJPmnuM0xO97zaKrDZMwO/V56fAnn8m/tA==}
dependencies:
rsvp: 3.2.1
dev: false
/[email protected]:
resolution: {integrity: sha512-W6JDAOLZ5pMPMjEiQGLCXSSV7pIBEgRR5zGkxgmzGSXHOxqV5dC/M1Zevqpbm9TZDE5tu358qZf8Vkzmsc+u7Q==}
@@ -20458,7 +20452,7 @@ packages:
parse5: 6.0.1
saxes: 5.0.1
symbol-tree: 3.2.4
tough-cookie: 4.1.2
tough-cookie: 4.1.3
w3c-hr-time: 1.0.2
w3c-xmlserializer: 2.0.0
webidl-conversions: 6.1.0
@@ -22685,6 +22679,13 @@ packages:
resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==}
engines: {node: '>=0.10.0'}
/[email protected]:
resolution: {integrity: sha512-Ux1J4NUqC6tZayBqLN1kUlDAEvLiQlli/53sSddU4IN+h+3xxnv2HmRSMpVSvr1hvJzotfMs3ERvETGK+f4OwA==}
engines: {node: '>= 4.0'}
optionalDependencies:
fsevents: 2.3.2
dev: false
/[email protected]:
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
engines: {node: '>=0.10.0'}
@@ -22816,7 +22817,7 @@ packages:
got: 11.8.5
registry-auth-token: 4.2.2
registry-url: 5.1.0
semver: 7.5.3
semver: 7.5.4
dev: false
/[email protected]:
@@ -24772,7 +24773,7 @@ packages:
resolve: 1.22.1
resolve-url-loader: 4.0.0
sass-loader: 12.6.0([email protected])
semver: 7.3.8
semver: 7.5.4
source-map-loader: 3.0.2([email protected])
style-loader: 3.3.2([email protected])
tailwindcss: 3.2.7([email protected])
@@ -25337,10 +25338,6 @@ packages:
bufferutil: 4.0.7
utf-8-validate: 5.0.10
/[email protected]:
resolution: {integrity: sha512-Rf4YVNYpKjZ6ASAmibcwTNciQ5Co5Ztq6iZPEykHpkoflnD/K5ryE/rHehFsTm4NJj8nKDhbi3eKBWGogmNnkg==}
dev: false
/[email protected]:
resolution: {integrity: sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==}
engines: {node: '>=6.0.0', npm: '>=3.10.0'}
@@ -25549,7 +25546,7 @@ packages:
resolution: {integrity: sha512-Ij1vCAdFgWABd7zTg50Xw1/p0JgESNxuLlneEAsmBrKishA06ulTTL/SHGmNy2Zud7+rKrHTKNI6moJsn1ppAQ==}
dependencies:
'@types/semver': 6.2.3
semver: 7.5.3
semver: 7.5.4
dev: false
/[email protected]:
@@ -25559,14 +25556,6 @@ packages:
semver: 7.5.4
dev: false
/[email protected]:
resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
engines: {node: '>=10'}
hasBin: true
dependencies:
lru-cache: 6.0.0
dev: false
/[email protected]:
resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==}
engines: {node: '>=10'}
@@ -25870,15 +25859,15 @@ packages:
'@socket.io/component-emitter': 3.1.0
debug: 4.3.4([email protected])
engine.io-client: 6.4.0
socket.io-parser: 4.2.2
socket.io-parser: 4.2.4
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
dev: true
/[email protected].2:
resolution: {integrity: sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==}
/[email protected].4:
resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
engines: {node: '>=10.0.0'}
dependencies:
'@socket.io/component-emitter': 3.1.0
@@ -25894,9 +25883,9 @@ packages:
accepts: 1.3.8
base64id: 2.0.0
debug: 4.3.4([email protected])
engine.io: 6.4.1
engine.io: 6.5.2
socket.io-adapter: 2.5.2
socket.io-parser: 4.2.2
socket.io-parser: 4.2.4
transitivePeerDependencies:
- bufferutil
- supports-color
@@ -26578,19 +26567,6 @@ packages:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
dev: false
/[email protected]:
resolution: {integrity: sha512-vngT2JmkSapgq0z7uIoYtB9kWOOzMihAAYq/D3Pjm/ODOGMgS4r++B+OZ09U4hWR6EaOdy9eqQ7/8ygbH3wehA==}
engines: {node: 8.* || >= 10.*}
dependencies:
debug: 4.3.4([email protected])
heimdalljs: 0.2.6
mkdirp: 0.5.6
rimraf: 3.0.2
username-sync: 1.0.3
transitivePeerDependencies:
- supports-color
dev: false
/[email protected]:
resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -26912,8 +26888,8 @@ packages:
resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==}
dev: true
/[email protected].2:
resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==}
/[email protected].3:
resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
engines: {node: '>=6'}
dependencies:
psl: 1.9.0
@@ -27557,7 +27533,7 @@ packages:
is-yarn-global: 0.4.1
latest-version: 7.0.0
pupa: 3.1.0
semver: 7.3.8
semver: 7.5.4
semver-diff: 4.0.0
xdg-basedir: 5.1.0
dev: false
@@ -27652,10 +27628,6 @@ packages:
resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==}
engines: {node: '>=0.10.0'}
/[email protected]:
resolution: {integrity: sha512-m/7/FSqjJNAzF2La448c/aEom0gJy7HY7Y509h6l0ePvEkFictAGptwWaj1msWJ38JbfEDOUoE8kqFee9EHKdA==}
dev: false
/[email protected]:
resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==}
engines: {node: '>=6.14.2'}
@@ -28791,11 +28763,29 @@ packages:
simple-plist: 1.3.1
uuid: 7.0.3
/[email protected]:
resolution: {integrity: sha512-mgxlWVZw0TNWHoGmXq+NC3uhCIc55dDpAlDkMQUaIAcQzysb0kxctwv//fvuW61/nAAeUBJMQ8mnZjMmuYwOcQ==}
engines: {node: '>= 4.0'}
dependencies:
xdg-portable: 10.6.0
optionalDependencies:
fsevents: 2.3.2
dev: false
/[email protected]:
resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==}
engines: {node: '>=12'}
dev: false
/[email protected]:
resolution: {integrity: sha512-xrcqhWDvtZ7WLmt8G4f3hHy37iK7D2idtosRgkeiSPZEPmBShp0VfmRBLWAPC6zLF48APJ21yfea+RfQMF4/Aw==}
engines: {node: '>= 4.0'}
dependencies:
os-paths: 7.4.0
optionalDependencies:
fsevents: 2.3.2
dev: false
/[email protected]:
resolution: {integrity: sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==}
dependencies: