Compare commits

..
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
4895217142 Version Packages (#1560)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-09-01 12:45:44 -07:00
Joaquim VergesandGitHub 0b59cb2e59 update readme (#1574) 2023-09-01 18:41:16 +00:00
Joaquim VergesandGitHub 2088de1cac [SDK] Dynamic gas fee estimation (#1572) 2023-09-01 18:28:46 +00:00
Danny FridayandGitHub 066b9cfa09 [Storage] Improve storage SDK IPFS upload error logging (#1564) 2023-08-31 16:39:55 +00:00
Mariano FuentesandGitHub 1e6f9dcc04 CLI - Update globalThis variables (#1569) 2023-08-31 02:25:46 +00:00
Joaquim VergesandGitHub a023cb8cf1 [SDK] Adjust gas fee calculations for OP chains (#1568) 2023-08-30 23:37:08 +00:00
YashandGitHub a00cbaf78c [SDK] Fix param encoding for deterministic deploy (#1567) 2023-08-30 23:08:47 +00:00
Winston YeoandGitHub d378106a85 Winston/documentation for service utils (#1566) 2023-08-30 22:11:13 +00:00
Joaquim VergesandGitHub 3eb9592e10 [Storage] Up default download timeout to 60s, expose option to override it (#1565) 2023-08-30 17:40:06 +00:00
Mariano FuentesandGitHub 04b6ef73e4 [CLI] - Update logout logic (#1562) 2023-08-29 22:25:57 +00:00
Tair AsimandGitHub 13ef6a50cf [service-utils] expose account status in api key meta (#1559) 2023-08-29 17:24:59 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
04c607ccde Version Packages (#1556)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-29 10:23:47 -07:00
Mariano FuentesandGitHub 926dd7b03f [CLI] - Descriptions improvements, deprecate Release (#1558) 2023-08-29 17:01:20 +00:00
Manan TankandGitHub e00dd12357 [sdk] Use default import for EventEmitter (#1557) 2023-08-29 15:49:09 +00:00
iketwandGitHub cdc08c5528 [RN] Use regular import for coinbase connector (#1546) 2023-08-29 13:23:43 +00:00
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
130 changed files with 2733 additions and 818 deletions
+1
View File
@@ -23,6 +23,7 @@
| Package | Description | Latest Version |
| ---------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| [/sdk](./packages/sdk) | Best in class web3 SDK for Browser, Node and Mobile apps | <a href="https://www.npmjs.com/package/@thirdweb-dev/sdk"><img src="https://img.shields.io/npm/v/@thirdweb-dev/sdk?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/wallets](./packages/wallets) | Unified web3 Wallet library to integrate any wallet into your applications. | <a href="https://www.npmjs.com/package/@thirdweb-dev/wallets"><img src="https://img.shields.io/npm/v/@thirdweb-dev/wallets?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/react](./packages/react) | Ultimate collection of React hooks for your web3 apps | <a href="https://www.npmjs.com/package/@thirdweb-dev/react"><img src="https://img.shields.io/npm/v/@thirdweb-dev/react?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/react-native](./packages/react-native) | Ultimate collection of React hooks for your native mobile web3 apps | <a href="https://www.npmjs.com/package/@thirdweb-dev/react-native"><img src="https://img.shields.io/npm/v/@thirdweb-dev/react-native?color=red&label=npm&logo=npm" alt="npm version"/></a> |
| [/auth](./packages/auth) | Best in class wallet authentication for Node backends | <a href="https://www.npmjs.com/package/@thirdweb-dev/auth"><img src="https://img.shields.io/npm/v/@thirdweb-dev/auth?color=red&label=npm&logo=npm" alt="npm version"/></a> |
+21
View File
@@ -1,5 +1,26 @@
# @thirdweb-dev/auth
## 3.2.40
### Patch Changes
- Updated dependencies [[`a023cb8c`](https://github.com/thirdweb-dev/js/commit/a023cb8cf1e4f08be56a2e33c146c8d307c80f40), [`2088de1c`](https://github.com/thirdweb-dev/js/commit/2088de1cacbc903d4f18a84c21a8f27af8d06b29), [`1e6f9dcc`](https://github.com/thirdweb-dev/js/commit/1e6f9dcc04022c6a8a39d490123a3e22e52b5e0b)]:
- @thirdweb-dev/wallets@1.1.23
## 3.2.39
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/wallets@1.1.22
## 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.40",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
+17
View File
@@ -1,5 +1,22 @@
# @thirdweb-dev/chains
## 0.1.50
### Patch Changes
- [#1558](https://github.com/thirdweb-dev/js/pull/1558) [`926dd7b0`](https://github.com/thirdweb-dev/js/commit/926dd7b03f38ed25ca303dc23d3323d5edd28005) Thanks [@Marfuen](https://github.com/Marfuen)! - Updating chains list
## 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": {
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"name": "Worldland Mainnet",
"chain": "Worldland",
"rpc": [
"https://worldland.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://seoul.worldland.foundation"
],
"faucets": [],
"nativeCurrency": {
"name": "Worldland",
"symbol": "WL",
"decimals": 18
},
"infoURL": "https://worldland.foundation",
"shortName": "WLC",
"chainId": 103,
"networkId": 103,
"explorers": [
{
"name": "Worldland Explorer",
"url": "https://scan.worldland.foundation",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "worldland"
} as const satisfies Chain;
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"name": "Worldland Testnet",
"chain": "Worldland",
"rpc": [
"https://worldland-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://gwangju.worldland.foundation"
],
"faucets": [],
"nativeCurrency": {
"name": "Worldland",
"symbol": "WL",
"decimals": 18
},
"infoURL": "https://worldland.foundation",
"shortName": "TWLC",
"chainId": 10395,
"networkId": 10395,
"explorers": [
{
"name": "Worldland Explorer",
"url": "https://testscan.worldland.foundation",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "worldland-testnet"
} as const satisfies Chain;
+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;
+32
View File
@@ -0,0 +1,32 @@
import type { Chain } from "../src/types";
export default {
"name": "Polygon Supernet Arianee",
"chain": "Arianee",
"rpc": [
"https://polygon-supernet-arianee.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.polygonsupernet.public.arianee.net"
],
"faucets": [],
"nativeCurrency": {
"name": "Arianee",
"symbol": "ARIA20",
"decimals": 18
},
"infoURL": "https://arianee.org",
"shortName": "Arianee",
"chainId": 11891,
"networkId": 11891,
"explorers": [
{
"name": "Polygon Supernet Arianee Explorer",
"url": "https://polygonsupernet.explorer.arianee.net",
"standard": "EIP3091"
}
],
"parent": {
"chain": "eip155-1",
"type": "L2"
},
"testnet": false,
"slug": "polygon-supernet-arianee"
} 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;
+33
View File
@@ -0,0 +1,33 @@
import type { Chain } from "../src/types";
export default {
"name": "Shrapnel Testnet",
"chain": "SHRAPNEL",
"rpc": [
"https://shrapnel-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/shrapnel/testnet/rpc"
],
"features": [
{
"name": "EIP1559"
}
],
"faucets": [],
"nativeCurrency": {
"name": "SHRAPG",
"symbol": "SHRAPG",
"decimals": 18
},
"infoURL": "https://www.shrapnel.com/",
"shortName": "shraptest",
"chainId": 2038,
"networkId": 2038,
"explorers": [
{
"name": "SHRAPNEL Explorer",
"url": "https://subnets-test.avax.network/shrapnel",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "shrapnel-testnet"
} as const satisfies Chain;
+23
View File
@@ -0,0 +1,23 @@
import type { Chain } from "../src/types";
export default {
"name": "BigShortBets",
"chain": "BIGSB",
"rpc": [
"https://bigshortbets.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://market.bigsb.io",
"wss://market.bigsb.io"
],
"faucets": [],
"nativeCurrency": {
"name": "USD Coin",
"symbol": "USDC",
"decimals": 18
},
"infoURL": "https://bigshortbets.com/",
"shortName": "bigsb",
"chainId": 2137,
"networkId": 2137,
"explorers": [],
"testnet": false,
"slug": "bigshortbets"
} as const satisfies Chain;
+40
View File
@@ -0,0 +1,40 @@
import type { Chain } from "../src/types";
export default {
"name": "Nautilus Mainnet",
"chain": "ETH",
"icon": {
"url": "ipfs://QmNutSgM7n6aJPPDiofe9Dm1epy1RcYTMvugukLUK2vmPM",
"width": 500,
"height": 500,
"format": "png"
},
"rpc": [
"https://nautilus.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://api.nautilus.nautchain.xyz"
],
"faucets": [],
"nativeCurrency": {
"name": "Zebec",
"symbol": "ZBC",
"decimals": 18
},
"infoURL": "https://docs.nautchain.xyz",
"shortName": "NAUTCHAIN",
"chainId": 22222,
"networkId": 22222,
"explorers": [
{
"name": "Nautscan",
"url": "https://nautscan.com",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmNutSgM7n6aJPPDiofe9Dm1epy1RcYTMvugukLUK2vmPM",
"width": 500,
"height": 500,
"format": "png"
}
}
],
"testnet": false,
"slug": "nautilus"
} as const satisfies Chain;
+37
View File
@@ -0,0 +1,37 @@
import type { Chain } from "../src/types";
export default {
"name": "Morphism Testnet",
"chain": "ETH",
"rpc": [
"https://morphism-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-testnet.morphism.xyz"
],
"faucets": [],
"nativeCurrency": {
"name": "Ether",
"symbol": "ETH",
"decimals": 18
},
"infoURL": "https://morphism.xyz",
"shortName": "tmorph",
"chainId": 2710,
"networkId": 2710,
"explorers": [
{
"name": "Morphism Testnet Explorer",
"url": "https://explorer-testnet.morphism.xyz",
"standard": "EIP3091"
}
],
"parent": {
"type": "L2",
"chain": "eip155-1",
"bridges": [
{
"url": "https://bridge-testnet.morphism.xyz"
}
]
},
"testnet": true,
"slug": "morphism-testnet"
} 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;
+2 -2
View File
@@ -3,7 +3,7 @@ export default {
"name": "Xpla Mainnet",
"chain": "XPLA",
"rpc": [
"https://xpla-xpla.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://xpla.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://dimension-evm-rpc.xpla.dev"
],
"nativeCurrency": {
@@ -31,5 +31,5 @@ export default {
"redFlags": [
"reusedChainId"
],
"slug": "xpla-xpla"
"slug": "xpla"
} as const satisfies Chain;
+11 -8
View File
@@ -1,21 +1,21 @@
import type { Chain } from "../src/types";
export default {
"name": "Xpla Mainnet",
"chain": "XPLA",
"name": "Xpla Testnet",
"chain": "XPLATest",
"rpc": [
"https://xpla.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://xpla-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://dimension-rpc.xpla.dev"
],
"faucets": [
"https://faucet.xpla.io/"
"https://faucet.xpla.io"
],
"nativeCurrency": {
"name": "XPLA",
"symbol": "XPLA",
"decimals": 18
},
"infoURL": "https://xpla.io/",
"shortName": "xpla",
"infoURL": "https://xpla.io",
"shortName": "xplatest",
"chainId": 3701,
"networkId": 3701,
"icon": {
@@ -31,6 +31,9 @@ export default {
"standard": "none"
}
],
"testnet": false,
"slug": "xpla"
"redFlags": [
"reusedChainId"
],
"testnet": true,
"slug": "xpla-testnet"
} as const satisfies Chain;
+36
View File
@@ -0,0 +1,36 @@
import type { Chain } from "../src/types";
export default {
"name": "ConnectorManager Robin",
"chain": "Rangers",
"icon": {
"url": "ipfs://QmXR5e5SDABWfQn6XT9uMsVYAo5Bv7vUv4jVs8DFqatZWG",
"width": 2000,
"height": 2000,
"format": "png"
},
"rpc": [
"https://connectormanager-robin.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://robin-cm.rangersprotocol.com/api/jsonrpc"
],
"faucets": [
"https://robin-faucet.rangersprotocol.com"
],
"nativeCurrency": {
"name": "Rangers Protocol Gas",
"symbol": "ttRPG",
"decimals": 18
},
"infoURL": "https://rangersprotocol.com",
"shortName": "ttrpg",
"chainId": 38401,
"networkId": 38401,
"explorers": [
{
"name": "rangersscan-robin",
"url": "https://robin-rangersscan.rangersprotocol.com",
"standard": "none"
}
],
"testnet": false,
"slug": "connectormanager-robin"
} 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"
+2 -2
View File
@@ -3,7 +3,7 @@ export default {
"name": "Xpla Testnet",
"chain": "XPLA",
"rpc": [
"https://xpla-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://xpla-testnet-xpla-test.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://cube-evm-rpc.xpla.dev"
],
"nativeCurrency": {
@@ -34,5 +34,5 @@ export default {
"redFlags": [
"reusedChainId"
],
"slug": "xpla-testnet"
"slug": "xpla-testnet-xpla-test"
} as const satisfies Chain;
+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/",
+43
View File
@@ -0,0 +1,43 @@
import type { Chain } from "../src/types";
export default {
"name": "Tritanium Testnet",
"chain": "TRITANIUM",
"rpc": [
"https://tritanium-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://nodetestnet-station-one.tritanium.network/",
"https://nodetestnet-station-two.tritanium.network/"
],
"faucets": [
"https://faucet.tritanium.network"
],
"nativeCurrency": {
"name": "Tritanium Native Token",
"symbol": "tTRN",
"decimals": 18
},
"infoURL": "https://tritanium.network",
"shortName": "ttrn",
"chainId": 5353,
"networkId": 5353,
"icon": {
"url": "ipfs://QmRm6gcEPJmU9a86zrmyP7FALTN4Toz9HrnrL2Kwg6FPeh",
"width": 512,
"height": 512,
"format": "png"
},
"explorers": [
{
"name": "TRITANIUM Testnet Explorer",
"icon": {
"url": "ipfs://QmRm6gcEPJmU9a86zrmyP7FALTN4Toz9HrnrL2Kwg6FPeh",
"width": 512,
"height": 512,
"format": "png"
},
"url": "https://testnet.tritanium.network",
"standard": "none"
}
],
"testnet": true,
"slug": "tritanium-testnet"
} as const satisfies Chain;
+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"
+50
View File
@@ -0,0 +1,50 @@
import type { Chain } from "../src/types";
export default {
"name": "Rethereum Mainnet",
"chain": "RTH",
"rpc": [
"https://rethereum.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.rethereum.org",
"https://rethereum.rpc.restratagem.com",
"https://rpc.rthcentral.org"
],
"faucets": [],
"nativeCurrency": {
"name": "Rethereum",
"symbol": "RTH",
"decimals": 18
},
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
],
"infoURL": "https://www.rethereum.org",
"shortName": "rth",
"chainId": 622277,
"networkId": 622277,
"icon": {
"url": "ipfs://bafkreiawlhc2trzyxgnz24vowdymxme2m446uk4vmrplgxsdd74ecpfloq",
"width": 830,
"height": 830,
"format": "png"
},
"explorers": [
{
"name": "rethereum",
"url": "https://explorer.rethereum.org",
"icon": {
"url": "ipfs://QmYtUimyqHkkFxYdbXXRbUqNg2VLPUg6Uu2C2nmFWowiZM",
"width": 551,
"height": 540,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "rethereum"
} as const satisfies Chain;
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"name": "JuncaChain",
"chain": "JuncaChain",
"rpc": [
"https://juncachain.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.juncachain.com"
],
"nativeCurrency": {
"name": "JuncaChain Native Token",
"symbol": "JGC",
"decimals": 18
},
"faucets": [],
"infoURL": "https://junca-cash.world",
"shortName": "junca",
"chainId": 668,
"networkId": 668,
"explorers": [
{
"name": "JuncaScan",
"url": "https://scan.juncachain.com",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "juncachain"
} as const satisfies Chain;
+37
View File
@@ -0,0 +1,37 @@
import type { Chain } from "../src/types";
export default {
"name": "JuncaGlobal Chain Testnet",
"chain": "JuncaGlobal Test Chain",
"rpc": [
"https://juncaglobal-chain-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-testnet.juncachain.com",
"wss://ws-testnet.juncachain.com"
],
"faucets": [
"https://faucet-testnet.juncachain.com"
],
"nativeCurrency": {
"name": "JuncaGlobal Chain Testnet Native Token",
"symbol": "JGCT",
"decimals": 18
},
"infoURL": "https://junca-cash.world",
"shortName": "junca",
"chainId": 669,
"networkId": 669,
"explorers": [
{
"name": "blockscout",
"url": "https://scan-testnet.juncachain.com",
"icon": {
"url": "ipfs://QmYtUimyqHkkFxYdbXXRbUqNg2VLPUg6Uu2C2nmFWowiZM",
"width": 551,
"height": 540,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "juncaglobal-chain-testnet"
} as const satisfies Chain;
+3 -2
View File
@@ -1,6 +1,6 @@
import type { Chain } from "../src/types";
export default {
"name": "Horizen EON",
"name": "Horizen EON Mainnet",
"shortName": "EON",
"chain": "EON",
"icon": {
@@ -11,7 +11,8 @@ export default {
},
"rpc": [
"https://horizen-eon.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://eon-rpc.horizenlabs.io/ethv1"
"https://eon-rpc.horizenlabs.io/ethv1",
"https://rpc.ankr.com/horizen_eon"
],
"features": [
{
+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": [
+42
View File
@@ -0,0 +1,42 @@
import type { Chain } from "../src/types";
export default {
"name": "Nautilus Proteus Testnet",
"chain": "ETH",
"icon": {
"url": "ipfs://QmNutSgM7n6aJPPDiofe9Dm1epy1RcYTMvugukLUK2vmPM",
"width": 500,
"height": 500,
"format": "png"
},
"rpc": [
"https://nautilus-proteus-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://api.proteus.nautchain.xyz/solana"
],
"faucets": [
"https://proteusfaucet.nautchain.xyz"
],
"nativeCurrency": {
"name": "Zebec Test Token",
"symbol": "tZBC",
"decimals": 18
},
"infoURL": "https://docs.nautchain.xyz",
"shortName": "NAUTTest",
"chainId": 88002,
"networkId": 88002,
"explorers": [
{
"name": "Nautscan",
"url": "https://proteus.nautscan.com",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmNutSgM7n6aJPPDiofe9Dm1epy1RcYTMvugukLUK2vmPM",
"width": 500,
"height": 500,
"format": "png"
}
}
],
"testnet": true,
"slug": "nautilus-proteus-testnet"
} as const satisfies Chain;
+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.50",
"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",
],
},
],
+42
View File
@@ -1,5 +1,47 @@
# thirdweb
## 0.12.2
### Patch Changes
- [#1562](https://github.com/thirdweb-dev/js/pull/1562) [`04b6ef73`](https://github.com/thirdweb-dev/js/commit/04b6ef73e4641c5284353bffc43dbc5c597405de) Thanks [@Marfuen](https://github.com/Marfuen)! - Logout underlying wallet on logout
- [#1569](https://github.com/thirdweb-dev/js/pull/1569) [`1e6f9dcc`](https://github.com/thirdweb-dev/js/commit/1e6f9dcc04022c6a8a39d490123a3e22e52b5e0b) Thanks [@Marfuen](https://github.com/Marfuen)! - Use a different globalThis for CLI related flow.
- Updated dependencies [[`3eb9592e`](https://github.com/thirdweb-dev/js/commit/3eb9592e10154e06d2fa5effbc0c1a483f62498f), [`066b9cfa`](https://github.com/thirdweb-dev/js/commit/066b9cfa09df2531c9e5440477a24edc1374e0e0), [`a00cbaf7`](https://github.com/thirdweb-dev/js/commit/a00cbaf78c05ea43d3814ba9f9ec8e667f0ddb25), [`a023cb8c`](https://github.com/thirdweb-dev/js/commit/a023cb8cf1e4f08be56a2e33c146c8d307c80f40), [`2088de1c`](https://github.com/thirdweb-dev/js/commit/2088de1cacbc903d4f18a84c21a8f27af8d06b29), [`1e6f9dcc`](https://github.com/thirdweb-dev/js/commit/1e6f9dcc04022c6a8a39d490123a3e22e52b5e0b)]:
- @thirdweb-dev/storage@1.2.10
- @thirdweb-dev/sdk@3.10.59
- @thirdweb-dev/wallets@1.1.23
- @thirdweb-dev/auth@3.2.40
## 0.12.1
### Patch Changes
- [#1558](https://github.com/thirdweb-dev/js/pull/1558) [`926dd7b0`](https://github.com/thirdweb-dev/js/commit/926dd7b03f38ed25ca303dc23d3323d5edd28005) Thanks [@Marfuen](https://github.com/Marfuen)! - Updated descriptions for publish and deploy. Print out the login url for users without browsers. Remove the Release command (Use "Publish" instead).
- Updated dependencies [[`926dd7b0`](https://github.com/thirdweb-dev/js/commit/926dd7b03f38ed25ca303dc23d3323d5edd28005), [`e00dd123`](https://github.com/thirdweb-dev/js/commit/e00dd123579f75752b6fe4fcf613d2cae5419e19)]:
- @thirdweb-dev/chains@0.1.50
- @thirdweb-dev/sdk@3.10.58
- @thirdweb-dev/wallets@1.1.22
- @thirdweb-dev/auth@3.2.39
## 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.2",
"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": {
+253 -65
View File
@@ -1,105 +1,293 @@
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_CLI_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_CLI_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) && fs.existsSync(cliWalletPath);
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);
});
console.log(`Automatically attempting to open 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);
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);
}
console.log(chalk.yellow(`If the browser doesn't open, please use this link to authenticate:\n`));
console.log(chalk.yellow(urlToOpen + '\n'));
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"));
}
}
};
+100 -81
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) {
@@ -41,25 +47,41 @@ const main = async () => {
$$ |$$\\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ |
\\$$$$ |$$ | $$ |$$ |$$ | \\$$$$$$$ |\\$$$$$\\$$$$ |\\$$$$$$$\\ $$$$$$$ |
\\____/ \\__| \\__|\\__|\\__| \\_______| \\_____\\____/ \\_______|\\_______/ `);
console.info(`\n 💎 thirdweb-cli v${cliVersion} 💎\n`);
console.info(`\n 💎 thirdweb v${cliVersion} 💎\n`);
}
program
.name("thirdweb-cli")
.name("thirdweb")
.description("Official thirdweb command line interface")
.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
}
@@ -267,7 +296,7 @@ const main = async () => {
program
.command("deploy")
.description("Deploy your (or team) contracts securely to blockchains")
.description("Securely deploy your contract to any EVM network without having to deal with scripts, private keys, or ABIs")
.option("-p, --path <project-path>", "path to project", ".")
.option("--clean", "clear the cache before building")
.option("--dry-run", "dry run (skip actually publishing)")
@@ -300,66 +329,31 @@ 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);
}
});
program
.command("release")
.description("[Deprecated] use 'publish' instead.")
.option("-p, --path <project-path>", "path to project", ".")
.option(
"-f, --file [name]",
"Filter for contract files that contain the file name",
)
.option(
"-cn, --contract-name [name]",
"Filter for contracts that contain this contract name",
)
.option("--clean", "clear the cache before building")
.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")
.action(async (options) => {
let apiSecretKey = "";
// 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);
} else {
await validateKey(options.key);
apiSecretKey = 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);
info(
`Open this link to publish your contracts: ${chalk.blueBright(
url.toString(),
)}`,
);
if (url && !options.ci) {
await open(url.toString());
}
});
program
.command("publish")
.description(
"Publish your protocol so other devs can deploy them and unlock SDKs, Dashboards and Analytics",
"Get a shareable page for your contract with a README, changelog, and version control. Enable one-click deploys to any EVM network",
)
.option("-p, --path <project-path>", "path to project", ".")
.option(
@@ -374,17 +368,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 +398,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 +449,7 @@ const main = async () => {
),
);
} else {
logger.error(chalk.redBright("Failed to upload files"), err);
logger.error(chalk.redBright(err.message ? err.message : err));
}
}
});
@@ -460,36 +471,44 @@ 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
.command("login")
.description(
"Authenticate with the thirdweb CLI using your API secret key or replace an existing API secret key",
"Authorize your device to use the thirdweb CLI by authenticating with your wallet from our dashboard",
)
.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
.command("logout")
.description(
"Logout of the thirdweb CLI, effectively removing your API secret key from your machine",
"Logout of the thirdweb CLI",
)
.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[];
+5
View File
@@ -0,0 +1,5 @@
/* eslint-disable no-var */
declare module globalThis {
var TW_AUTH_TOKEN: string;
var TW_CLI_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 {
+31
View File
@@ -1,5 +1,36 @@
# @thirdweb-dev/react-core
## 3.14.40
### Patch Changes
- Updated dependencies [[`3eb9592e`](https://github.com/thirdweb-dev/js/commit/3eb9592e10154e06d2fa5effbc0c1a483f62498f), [`066b9cfa`](https://github.com/thirdweb-dev/js/commit/066b9cfa09df2531c9e5440477a24edc1374e0e0), [`a00cbaf7`](https://github.com/thirdweb-dev/js/commit/a00cbaf78c05ea43d3814ba9f9ec8e667f0ddb25), [`a023cb8c`](https://github.com/thirdweb-dev/js/commit/a023cb8cf1e4f08be56a2e33c146c8d307c80f40), [`2088de1c`](https://github.com/thirdweb-dev/js/commit/2088de1cacbc903d4f18a84c21a8f27af8d06b29), [`1e6f9dcc`](https://github.com/thirdweb-dev/js/commit/1e6f9dcc04022c6a8a39d490123a3e22e52b5e0b)]:
- @thirdweb-dev/storage@1.2.10
- @thirdweb-dev/sdk@3.10.59
- @thirdweb-dev/wallets@1.1.23
- @thirdweb-dev/auth@3.2.40
## 3.14.39
### Patch Changes
- Updated dependencies [[`926dd7b0`](https://github.com/thirdweb-dev/js/commit/926dd7b03f38ed25ca303dc23d3323d5edd28005), [`e00dd123`](https://github.com/thirdweb-dev/js/commit/e00dd123579f75752b6fe4fcf613d2cae5419e19)]:
- @thirdweb-dev/chains@0.1.50
- @thirdweb-dev/sdk@3.10.58
- @thirdweb-dev/wallets@1.1.22
- @thirdweb-dev/auth@3.2.39
## 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.40",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -1,5 +1,11 @@
# @thirdweb-dev/react-native-compat
## 0.2.59
## 0.2.58
## 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.59",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+33
View File
@@ -1,5 +1,38 @@
# @thirdweb-dev/react-native
## 0.2.59
### Patch Changes
- Updated dependencies [[`3eb9592e`](https://github.com/thirdweb-dev/js/commit/3eb9592e10154e06d2fa5effbc0c1a483f62498f), [`066b9cfa`](https://github.com/thirdweb-dev/js/commit/066b9cfa09df2531c9e5440477a24edc1374e0e0), [`a00cbaf7`](https://github.com/thirdweb-dev/js/commit/a00cbaf78c05ea43d3814ba9f9ec8e667f0ddb25), [`a023cb8c`](https://github.com/thirdweb-dev/js/commit/a023cb8cf1e4f08be56a2e33c146c8d307c80f40), [`2088de1c`](https://github.com/thirdweb-dev/js/commit/2088de1cacbc903d4f18a84c21a8f27af8d06b29), [`1e6f9dcc`](https://github.com/thirdweb-dev/js/commit/1e6f9dcc04022c6a8a39d490123a3e22e52b5e0b)]:
- @thirdweb-dev/storage@1.2.10
- @thirdweb-dev/sdk@3.10.59
- @thirdweb-dev/wallets@1.1.23
- @thirdweb-dev/react-core@3.14.40
## 0.2.58
### Patch Changes
- [#1546](https://github.com/thirdweb-dev/js/pull/1546) [`cdc08c55`](https://github.com/thirdweb-dev/js/commit/cdc08c55282d5873972b85217c42ba2f97a1ffed) Thanks [@iketw](https://github.com/iketw)! - Use regular import for coinbase connector in React Native
- Updated dependencies [[`926dd7b0`](https://github.com/thirdweb-dev/js/commit/926dd7b03f38ed25ca303dc23d3323d5edd28005), [`e00dd123`](https://github.com/thirdweb-dev/js/commit/e00dd123579f75752b6fe4fcf613d2cae5419e19)]:
- @thirdweb-dev/chains@0.1.50
- @thirdweb-dev/sdk@3.10.58
- @thirdweb-dev/react-core@3.14.39
- @thirdweb-dev/wallets@1.1.22
## 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.59",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -1,8 +1,9 @@
import { noopStorage } from "../../../core/AsyncStorage";
import type {
CoinbaseWalletConnector,
CoinbaseWalletConnector as CoinbaseWalletConnectorType,
CoinbaseWalletConnectorOptions,
} from "../connectors/coinbase-wallet";
import { CoinbaseWalletConnector } from "../connectors/coinbase-wallet";
import {
AbstractClientWallet,
Connector,
@@ -28,8 +29,8 @@ export class CoinbaseWallet extends AbstractClientWallet<CoinbaseWalletConnector
};
connector?: Connector;
coinbaseConnector?: CoinbaseWalletConnector;
provider?: CoinbaseWalletConnector["provider"];
coinbaseConnector?: CoinbaseWalletConnectorType;
provider?: CoinbaseWalletConnectorType["provider"];
static id = walletIds.coinbase;
public get walletName() {
@@ -50,11 +51,6 @@ export class CoinbaseWallet extends AbstractClientWallet<CoinbaseWalletConnector
protected async getConnector(): Promise<Connector> {
if (!this.connector) {
// import the connector dynamically
const { CoinbaseWalletConnector: CoinbaseWalletConnector } = await import(
"../connectors/coinbase-wallet"
);
const cbConnector = new CoinbaseWalletConnector({
chains: this.chains,
options: {
+33
View File
@@ -1,5 +1,38 @@
# @thirdweb-dev/react
## 3.14.40
### Patch Changes
- Updated dependencies [[`a00cbaf7`](https://github.com/thirdweb-dev/js/commit/a00cbaf78c05ea43d3814ba9f9ec8e667f0ddb25), [`a023cb8c`](https://github.com/thirdweb-dev/js/commit/a023cb8cf1e4f08be56a2e33c146c8d307c80f40), [`2088de1c`](https://github.com/thirdweb-dev/js/commit/2088de1cacbc903d4f18a84c21a8f27af8d06b29), [`1e6f9dcc`](https://github.com/thirdweb-dev/js/commit/1e6f9dcc04022c6a8a39d490123a3e22e52b5e0b)]:
- @thirdweb-dev/sdk@3.10.59
- @thirdweb-dev/wallets@1.1.23
- @thirdweb-dev/react-core@3.14.40
## 3.14.39
### Patch Changes
- Updated dependencies [[`926dd7b0`](https://github.com/thirdweb-dev/js/commit/926dd7b03f38ed25ca303dc23d3323d5edd28005), [`e00dd123`](https://github.com/thirdweb-dev/js/commit/e00dd123579f75752b6fe4fcf613d2cae5419e19)]:
- @thirdweb-dev/chains@0.1.50
- @thirdweb-dev/sdk@3.10.58
- @thirdweb-dev/react-core@3.14.39
- @thirdweb-dev/wallets@1.1.22
## 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.40",
"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();
},
};
};
+6
View File
@@ -14,6 +14,12 @@ module.exports = {
message:
"Do not import entire ethers object, Use named imports instead.",
},
{
name: "eventemitter3",
importNames: ["EventEmitter"],
message:
"Do not use named import for importing EventEmitter, Use default import instead.",
},
],
},
],
+36
View File
@@ -1,5 +1,41 @@
# @thirdweb-dev/sdk
## 3.10.59
### Patch Changes
- [#1567](https://github.com/thirdweb-dev/js/pull/1567) [`a00cbaf7`](https://github.com/thirdweb-dev/js/commit/a00cbaf78c05ea43d3814ba9f9ec8e667f0ddb25) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Fix constructor param encoding for deterministic deployments
- [#1568](https://github.com/thirdweb-dev/js/pull/1568) [`a023cb8c`](https://github.com/thirdweb-dev/js/commit/a023cb8cf1e4f08be56a2e33c146c8d307c80f40) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Better fee calculations for OP stack
- [#1572](https://github.com/thirdweb-dev/js/pull/1572) [`2088de1c`](https://github.com/thirdweb-dev/js/commit/2088de1cacbc903d4f18a84c21a8f27af8d06b29) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Dynamic gas fee estimations
- [#1569](https://github.com/thirdweb-dev/js/pull/1569) [`1e6f9dcc`](https://github.com/thirdweb-dev/js/commit/1e6f9dcc04022c6a8a39d490123a3e22e52b5e0b) Thanks [@Marfuen](https://github.com/Marfuen)! - Use a different globalThis for CLI related flow.
- Updated dependencies [[`3eb9592e`](https://github.com/thirdweb-dev/js/commit/3eb9592e10154e06d2fa5effbc0c1a483f62498f), [`066b9cfa`](https://github.com/thirdweb-dev/js/commit/066b9cfa09df2531c9e5440477a24edc1374e0e0), [`1e6f9dcc`](https://github.com/thirdweb-dev/js/commit/1e6f9dcc04022c6a8a39d490123a3e22e52b5e0b)]:
- @thirdweb-dev/storage@1.2.10
## 3.10.58
### Patch Changes
- [#1557](https://github.com/thirdweb-dev/js/pull/1557) [`e00dd123`](https://github.com/thirdweb-dev/js/commit/e00dd123579f75752b6fe4fcf613d2cae5419e19) Thanks [@MananTank](https://github.com/MananTank)! - Use default export for EventEmitter to fix issue with Nuxt/Vite + Yarn to fix the "Named export 'EventEmitter' not found" error
- Updated dependencies [[`926dd7b0`](https://github.com/thirdweb-dev/js/commit/926dd7b03f38ed25ca303dc23d3323d5edd28005)]:
- @thirdweb-dev/chains@0.1.50
## 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.59",
"description": "The main thirdweb SDK.",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
"license": "Apache-2.0",
@@ -48,17 +48,24 @@ export async function directDeployDeterministic(
const create2Factory = await deployCreate2Factory(signer);
// 2. Encode constructor params
const constructorParamTypes = extractConstructorParamsFromAbi(abi).map(
(p) => {
return p.type;
},
);
const constructorParams = extractConstructorParamsFromAbi(abi);
const constructorParamTypes = constructorParams.map((p) => {
return p.type;
});
const paramValues = convertParamValues(
constructorParamTypes,
constructorArgs,
);
const paramTypesForEncoder = constructorParams.map((p) => {
if (p.type === "tuple[]") {
return utils.ParamType.from(p);
} else {
return p.type;
}
});
const encodedArgs = utils.defaultAbiCoder.encode(
constructorParamTypes,
paramTypesForEncoder,
paramValues,
);
@@ -212,18 +219,24 @@ export async function predictAddressDeterministic(
const create2Factory = await getCreate2FactoryAddress(provider);
// 2. Encode constructor params
const constructorParamTypes = extractConstructorParamsFromAbi(abi).map(
(p) => {
return p.type;
},
);
const constructorParams = extractConstructorParamsFromAbi(abi);
const constructorParamTypes = constructorParams.map((p) => {
return p.type;
});
const paramValues = convertParamValues(
constructorParamTypes,
constructorArgs,
);
const paramTypesForEncoder = constructorParams.map((p) => {
if (p.type === "tuple[]") {
return utils.ParamType.from(p);
} else {
return p.type;
}
});
const encodedArgs = utils.defaultAbiCoder.encode(
constructorParamTypes,
paramTypesForEncoder,
paramValues,
);
+103 -1
View File
@@ -1,6 +1,108 @@
import { ChainId } from "../constants/chains/ChainId";
import fetch from "cross-fetch";
import { BigNumber, utils } from "ethers";
import { BigNumber, utils, providers } from "ethers";
import { Mumbai, Polygon } from "@thirdweb-dev/chains";
import { isBrowser } from "./utils";
type FeeData = {
maxFeePerGas: null | BigNumber;
maxPriorityFeePerGas: null | BigNumber;
};
export async function getDefaultGasOverrides(provider: providers.Provider) {
// If we're running in the browser, let users configure gas price in their wallet UI
if (isBrowser()) {
return {};
}
// handle smart wallet provider
if ((provider as any).originalProvider) {
provider = (provider as any).originalProvider;
}
const feeData = await getDynamicFeeData(
provider as providers.JsonRpcProvider,
);
if (feeData.maxFeePerGas && feeData.maxPriorityFeePerGas) {
return {
maxFeePerGas: feeData.maxPriorityFeePerGas,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
};
} else {
return {
gasPrice: await getGasPrice(provider),
};
}
}
export async function getDynamicFeeData(
provider: providers.JsonRpcProvider,
): Promise<FeeData> {
let maxFeePerGas: null | BigNumber = null;
let maxPriorityFeePerGas: null | BigNumber = null;
const [chainId, block, eth_maxPriorityFeePerGas] = await Promise.all([
(await provider.getNetwork()).chainId,
await provider.getBlock("latest"),
await provider.send("eth_maxPriorityFeePerGas", []).catch(() => null),
]);
const baseBlockFee =
block && block.baseFeePerGas
? block.baseFeePerGas
: utils.parseUnits("100", "wei");
if (chainId === Mumbai.chainId || chainId === Polygon.chainId) {
// for polygon, get fee data from gas station
maxPriorityFeePerGas = await getPolygonGasPriorityFee(chainId);
console.log(
"DYNAMIC: polygon fee",
utils.formatUnits(maxPriorityFeePerGas, "gwei"),
);
} else if (eth_maxPriorityFeePerGas) {
// prioritize fee from eth_maxPriorityFeePerGas
maxPriorityFeePerGas = BigNumber.from(eth_maxPriorityFeePerGas);
} else {
// if eth_maxPriorityFeePerGas is not available, use 1.5 gwei default
const feeData = await provider.getFeeData();
maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
if (!maxPriorityFeePerGas) {
// chain does not support eip-1559, return null for both
return { maxFeePerGas: null, maxPriorityFeePerGas: null };
}
}
// eip-1559 formula, with an extra 10% tip to account for gas volatility
maxFeePerGas = baseBlockFee
.mul(2)
.add(getPreferredPriorityFee(maxPriorityFeePerGas));
return { maxFeePerGas, maxPriorityFeePerGas };
}
function getPreferredPriorityFee(
defaultPriorityFeePerGas: BigNumber,
percentMultiplier: number = 10,
): BigNumber {
const extraTip = defaultPriorityFeePerGas.div(100).mul(percentMultiplier); // + 10%
const txGasPrice = defaultPriorityFeePerGas.add(extraTip);
return txGasPrice;
}
export async function getGasPrice(
provider: providers.Provider,
): Promise<BigNumber> {
const gasPrice = await provider.getGasPrice();
const maxGasPrice = utils.parseUnits("300", "gwei"); // 300 gwei
const extraTip = gasPrice.div(100).mul(10); // + 10%
const txGasPrice = gasPrice.add(extraTip);
if (txGasPrice.gt(maxGasPrice)) {
return maxGasPrice;
}
return txGasPrice;
}
/**
* @internal
+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";
+1 -4
View File
@@ -12,10 +12,7 @@ export * from "./addresses/getDefaultTrustedForwarders";
export * from "./chains/ChainId";
export * from "./chains/SUPPORTED_CHAIN_ID";
export * from "./chains/SUPPORTED_CHAIN_IDS";
export {
setSupportedChains,
getSupportedChains,
} from "./chains/supportedChains";
export * from "./chains/supportedChains";
export * from "./contract";
export * from "./currency";
+20 -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) {
@@ -244,6 +243,14 @@ export function getProviderFromRpcUrl(
authStrategy = "twAuthToken";
}
if (
typeof globalThis !== "undefined" &&
"TW_CLI_AUTH_TOKEN" in globalThis &&
typeof (globalThis as any).TW_CLI_AUTH_TOKEN === "string"
) {
headers["x-authorize-wallet"] = "true";
}
const bundleId =
typeof globalThis !== "undefined" && "APP_BUNDLE_ID" in globalThis
? ((globalThis as any).APP_BUNDLE_ID as string)
@@ -278,18 +285,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);
@@ -2,7 +2,7 @@ import { EventType } from "../../constants/events";
import type { ContractEvent, EventQueryOptions } from "../../types/events";
import { ContractWrapper } from "./contract-wrapper";
import type { BaseContract, Event, providers, utils } from "ethers";
import type { EventEmitter } from "eventemitter3";
import type EventEmitter from "eventemitter3";
/**
* Listen to Contract events in real time
@@ -27,14 +27,12 @@ import {
ForwardRequest,
getAndIncrementNonce,
} from "../../common/forwarder";
import { getPolygonGasPriorityFee } from "../../common/gas-price";
import { getDefaultGasOverrides } from "../../common/gas-price";
import { fetchContractMetadataFromAddress } from "../../common/metadata-resolver";
import { signEIP2612Permit } from "../../common/permit";
import { signTypedDataInternal } from "../../common/sign";
import { isBrowser } from "../../common/utils";
import { CONTRACT_ADDRESSES } from "../../constants/addresses/CONTRACT_ADDRESSES";
import { getContractAddressByChainId } from "../../constants/addresses/getContractAddressByChainId";
import { ChainId } from "../../constants/chains/ChainId";
import { EventType } from "../../constants/events";
import { AbiSchema, ContractSource } from "../../schema/contracts/custom";
import { SDKOptions } from "../../schema/sdk-options";
@@ -133,105 +131,7 @@ export class ContractWrapper<
* @internal
*/
public async getCallOverrides(): Promise<CallOverrides> {
if (isBrowser()) {
// When running in the browser, let the wallet suggest gas estimates
// this means that the gas speed preferences set in the SDK options are ignored in a browser context
// but it also allows users to select their own gas speed prefs per tx from their wallet directly
return {};
}
const feeData = await this.getProvider().getFeeData();
const supports1559 = feeData.maxFeePerGas && feeData.maxPriorityFeePerGas;
if (supports1559) {
const chainId = await this.getChainID();
const block = await this.getProvider().getBlock("latest");
const baseBlockFee =
block && block.baseFeePerGas
? block.baseFeePerGas
: utils.parseUnits("1", "gwei");
let defaultPriorityFee: BigNumber;
if (chainId === ChainId.Mumbai || chainId === ChainId.Polygon) {
// for polygon, get fee data from gas station
defaultPriorityFee = await getPolygonGasPriorityFee(chainId);
} else {
// otherwise get it from ethers
defaultPriorityFee = BigNumber.from(feeData.maxPriorityFeePerGas);
}
// then add additional fee based on user preferences
const maxPriorityFeePerGas =
this.getPreferredPriorityFee(defaultPriorityFee);
// See: https://eips.ethereum.org/EIPS/eip-1559 for formula
const baseMaxFeePerGas = baseBlockFee.mul(2);
const maxFeePerGas = baseMaxFeePerGas.add(maxPriorityFeePerGas);
return {
maxFeePerGas,
maxPriorityFeePerGas,
};
} else {
return {
gasPrice: await this.getPreferredGasPrice(),
};
}
}
/**
* Calculates the priority fee per gas according to user preferences
* @param defaultPriorityFeePerGas - the base priority fee
*/
private getPreferredPriorityFee(
defaultPriorityFeePerGas: BigNumber,
): BigNumber {
const speed = this.options.gasSettings.speed;
const maxGasPrice = this.options.gasSettings.maxPriceInGwei;
let extraTip;
switch (speed) {
case "standard":
extraTip = BigNumber.from(0); // default is 2.5 gwei for ETH, 31 gwei for polygon
break;
case "fast":
extraTip = defaultPriorityFeePerGas.div(100).mul(5); // + 5% - 2.625 gwei / 32.5 gwei
break;
case "fastest":
extraTip = defaultPriorityFeePerGas.div(100).mul(10); // + 10% - 2.75 gwei / 34.1 gwei
break;
}
let txGasPrice = defaultPriorityFeePerGas.add(extraTip);
const max = utils.parseUnits(maxGasPrice.toString(), "gwei"); // no more than max gas setting
const min = utils.parseUnits("2.5", "gwei"); // no less than 2.5 gwei
if (txGasPrice.gt(max)) {
txGasPrice = max;
}
if (txGasPrice.lt(min)) {
txGasPrice = min;
}
return txGasPrice;
}
/**
* Calculates the gas price for transactions according to user preferences
*/
public async getPreferredGasPrice(): Promise<BigNumber> {
const gasPrice = await this.getProvider().getGasPrice();
const speed = this.options.gasSettings.speed;
const maxGasPrice = this.options.gasSettings.maxPriceInGwei;
let txGasPrice = gasPrice;
let extraTip;
switch (speed) {
case "standard":
extraTip = BigNumber.from(1); // min 1 wei
break;
case "fast":
extraTip = gasPrice.div(100).mul(5); // + 5%
break;
case "fastest":
extraTip = gasPrice.div(100).mul(10); // + 10%
break;
}
txGasPrice = txGasPrice.add(extraTip);
const max = utils.parseUnits(maxGasPrice.toString(), "gwei");
if (txGasPrice.gt(max)) {
txGasPrice = max;
}
return txGasPrice;
return getDefaultGasOverrides(this.getProvider());
}
/**
+1 -1
View File
@@ -38,7 +38,7 @@ import {
type ContractInterface,
utils,
} from "ethers";
import { EventEmitter } from "eventemitter3";
import EventEmitter from "eventemitter3";
import invariant from "tiny-invariant";
import { z } from "zod";
import { getApprovedImplementation } from "../../constants/addresses/getApprovedImplementation";
@@ -35,7 +35,7 @@ export class GasCostEstimator<TContract extends BaseContract> {
fn: keyof TContract["functions"] | (string & {}),
args: Parameters<TContract["functions"][typeof fn]> | any[],
): Promise<string> {
const price = await this.contractWrapper.getPreferredGasPrice();
const price = await this.contractWrapper.getProvider().getGasPrice();
const gasUnits = await this.contractWrapper.estimateGas(fn, args);
return utils.formatEther(gasUnits.mul(price));
}
@@ -1,10 +1,8 @@
import { TransactionError, parseRevertReason } from "../../common/error";
import { getPolygonGasPriorityFee } from "../../common/gas-price";
import { getDefaultGasOverrides, getGasPrice } from "../../common/gas-price";
import { fetchContractMetadataFromAddress } from "../../common/metadata-resolver";
import { fetchSourceFilesFromMetadata } from "../../common/fetchSourceFilesFromMetadata";
import { isRouterContract } from "../../common/plugin/isRouterContract";
import { isBrowser } from "../../common/utils";
import { ChainId } from "../../constants/chains/ChainId";
import { ContractSource } from "../../schema/contracts/custom";
import { SDKOptionsOutput } from "../../schema/sdk-options";
import type {
@@ -205,16 +203,7 @@ abstract class TransactionContext {
* Calculates the gas price for transactions (adding a 10% tip buffer)
*/
public async getGasPrice(): Promise<BigNumber> {
const gasPrice = await this.provider.getGasPrice();
const maxGasPrice = utils.parseUnits("300", "gwei"); // 300 gwei
const extraTip = gasPrice.div(100).mul(10); // + 10%
const txGasPrice = gasPrice.add(extraTip);
if (txGasPrice.gt(maxGasPrice)) {
return maxGasPrice;
}
return txGasPrice;
return getGasPrice(this.provider);
}
/**
@@ -228,43 +217,7 @@ abstract class TransactionContext {
* Get gas overrides for the transaction
*/
protected async getGasOverrides() {
// If we're running in the browser, let users configure gas price in their wallet UI
if (isBrowser()) {
return {};
}
const feeData = await this.provider.getFeeData();
const supports1559 = feeData.maxFeePerGas && feeData.maxPriorityFeePerGas;
if (supports1559) {
const chainId = (await this.provider.getNetwork()).chainId;
const block = await this.provider.getBlock("latest");
const baseBlockFee =
block && block.baseFeePerGas
? block.baseFeePerGas
: utils.parseUnits("1", "gwei");
let defaultPriorityFee: BigNumber;
if (chainId === ChainId.Mumbai || chainId === ChainId.Polygon) {
// for polygon, get fee data from gas station
defaultPriorityFee = await getPolygonGasPriorityFee(chainId);
} else {
// otherwise get it from ethers
defaultPriorityFee = BigNumber.from(feeData.maxPriorityFeePerGas);
}
// then add additional fee based on user preferences
const maxPriorityFeePerGas =
this.getPreferredPriorityFee(defaultPriorityFee);
// See: https://eips.ethereum.org/EIPS/eip-1559 for formula
const baseMaxFeePerGas = baseBlockFee.mul(2);
const maxFeePerGas = baseMaxFeePerGas.add(maxPriorityFeePerGas);
return {
maxFeePerGas,
maxPriorityFeePerGas,
};
} else {
return {
gasPrice: await this.getGasPrice(),
};
}
return getDefaultGasOverrides(this.provider);
}
/**
@@ -275,16 +228,6 @@ abstract class TransactionContext {
): BigNumber {
const extraTip = defaultPriorityFeePerGas.div(100).mul(10); // + 10%
const txGasPrice = defaultPriorityFeePerGas.add(extraTip);
const maxGasPrice = utils.parseUnits("300", "gwei"); // no more than 300 gwei
const minGasPrice = utils.parseUnits("2.5", "gwei"); // no less than 2.5 gwei
if (txGasPrice.gt(maxGasPrice)) {
return maxGasPrice;
}
if (txGasPrice.lt(minGasPrice)) {
return minGasPrice;
}
return txGasPrice;
}
}
+1 -1
View File
@@ -103,7 +103,7 @@ import {
utils,
ContractFactory as ethersContractFactory,
} from "ethers";
import { EventEmitter } from "eventemitter3";
import EventEmitter from "eventemitter3";
import invariant from "tiny-invariant";
import { z } from "zod";
import {
+5
View File
@@ -0,0 +1,5 @@
/* eslint-disable no-var */
declare module globalThis {
var TW_AUTH_TOKEN: string;
var TW_CLI_AUTH_TOKEN: string;
}
+17
View File
@@ -1,5 +1,22 @@
# @thirdweb-dev/service-utils
## 0.4.4
### Patch Changes
- [#1566](https://github.com/thirdweb-dev/js/pull/1566) [`d378106a`](https://github.com/thirdweb-dev/js/commit/d378106a852f941779bb72ac1841bddf34fc15a6) Thanks [@ElasticBottle](https://github.com/ElasticBottle)! - docs(service-utils): Add documentation for authorizeNode for node auth function
- [#1569](https://github.com/thirdweb-dev/js/pull/1569) [`1e6f9dcc`](https://github.com/thirdweb-dev/js/commit/1e6f9dcc04022c6a8a39d490123a3e22e52b5e0b) Thanks [@Marfuen](https://github.com/Marfuen)! - Use a different globalThis for CLI related flow.
- [#1559](https://github.com/thirdweb-dev/js/pull/1559) [`13ef6a50`](https://github.com/thirdweb-dev/js/commit/13ef6a50cf10b442b2b54b780e42210cab2db762) Thanks [@assimovt](https://github.com/assimovt)! - expose account status in api key metadata
## 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.4",
"main": "dist/thirdweb-dev-service-utils.cjs.js",
"module": "dist/thirdweb-dev-service-utils.esm.js",
"exports": {
+20 -5
View File
@@ -1,17 +1,21 @@
import type { ServiceName } from "./services";
export type CoreServiceConfig = {
enforceAuth: boolean;
apiUrl: string;
serviceScope: ServiceName;
serviceApiKey: string;
serviceAction?: string;
useWalletAuth?: boolean;
};
export type ApiKeyMetadata = {
id: string;
key: string;
accountId: string;
accountStatus: "noCustomer" | "noPayment" | "validPayment" | "invalidPayment";
accountPlan: "free" | "enterprise";
creatorWalletAddress: string;
secretHash: string;
walletAddresses: string[];
@@ -61,11 +65,15 @@ export async function fetchKeyMetadataFromApi(
"content-type": "application/json",
},
});
let json: ApiResponse
let json: ApiResponse;
try {
json = await response.json();
} catch (e) {
throw new Error(`Error fetching key metadata from API: ${response.status} - ${response.statusText} - ${await response.text()}`);
throw new Error(
`Error fetching key metadata from API: ${response.status} - ${
response.statusText
} - ${await response.text()}`,
);
}
return json;
}
@@ -73,9 +81,12 @@ 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: {
@@ -84,11 +95,15 @@ export async function fetchAccountFromApi(
authorization: `Bearer ${jwt}`,
},
});
let json: ApiAccountResponse
let json: ApiAccountResponse;
try {
json = await response.json();
} catch (e) {
throw new Error(`Error fetching account from API: ${response.status} - ${response.statusText} - ${await response.text()}`);
throw new Error(
`Error fetching account from API: ${response.status} - ${
response.statusText
} - ${await response.text()}`,
);
}
return json;
}

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