Compare commits

..
Author SHA1 Message Date
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
2a7bbf8665 Version Packages (#1507)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-18 18:18:39 -07:00
Jonas DanielsandGitHub f97ddf4c7f [chains] - add xpla chains & update chains (#1506) 2023-08-19 01:01:08 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
66c53360cd Version Packages (#1504)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-18 16:04:05 -07:00
Joaquim VergesandGitHub bd1fcbae32 [SDK] Expose Transaction.populateTransaction() (#1505) 2023-08-18 19:05:05 +00:00
Joaquim VergesandGitHub e196264172 [SDK] Pass custom storage to internal SDK instances (#1503) 2023-08-18 16:21:20 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
c9deda6f4b Version Packages (#1487)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-16 07:44:08 -07:00
b91d3e9901 Small fix for Promise.all() (#1490)
Co-authored-by: Joaquim Verges <[email protected]>
2023-08-15 22:06:07 -07:00
Joaquim VergesandGitHub d248aa2c5a [SDK] Let users pass their own IPFS download timeout and fix gateway fallback logic (#1500) 2023-08-15 19:20:36 -07:00
Mariano FuentesandGitHub 8f3b685ad2 [CLI] Deploy to show public gateway url after upload (#1499) 2023-08-15 22:28:56 +00:00
iketwandGitHub 26349f311d [RN] Improves loading states when disconnecting and loading balances (#1496) 2023-08-14 22:56:26 +00:00
YashandGitHub 2897576513 [SDK] Custom base fee for cronos testnet (#1495) 2023-08-14 22:39:23 +00:00
iketwandGitHub 07fb1b5ffa [RN] Adds support for raw WalletConnect (#1489) 2023-08-13 16:24:11 +00:00
iketwandGitHub 300a3c6f04 [ReactCore] Add better error messages when using hooks outside the <ThirdwebProvider /> (#1486) 2023-08-12 14:21:31 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
26367c04f3 Version Packages (#1483)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-11 16:07:25 -07:00
Joaquim VergesandGitHub 39e2af6f2f [Wallets] correct gas estimation for polygon/mumbai (#1484) 2023-08-11 22:16:29 +00:00
Joaquim VergesandGitHub a5ba9e4fbf [SDK] Separate Account and AccountPermissions extensions (#1480) 2023-08-11 20:31:54 +00:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
c9b0131acd Version Packages (#1478)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-10 15:51:09 -07:00
Joaquim VergesandGitHub 34b315993c [Wallets] use RPCs with no client id when adding to external wallets (#1477) 2023-08-10 22:33:47 +00:00
iketwandGitHub 14d9a71c92 [RN] Deprecates magicWallet in favor of magicLink for consistency (#1479) 2023-08-10 21:22:49 +00:00
YashandGitHub 7f5311222b [SDK] Enable dynamic contract deployment with any evm (#1468) 2023-08-10 19:32:51 +00:00
127 changed files with 3341 additions and 776 deletions
+35
View File
@@ -1,5 +1,40 @@
# @thirdweb-dev/auth
## 3.2.34
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/wallets@1.1.17
## 3.2.33
### Patch Changes
- Updated dependencies []:
- @thirdweb-dev/wallets@1.1.16
## 3.2.32
### Patch Changes
- Updated dependencies [[`07fb1b5f`](https://github.com/thirdweb-dev/js/commit/07fb1b5ffa4c170e252df31070852ddb9a81dec9)]:
- @thirdweb-dev/wallets@1.1.15
## 3.2.31
### Patch Changes
- Updated dependencies [[`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
- @thirdweb-dev/wallets@1.1.14
## 3.2.30
### Patch Changes
- Updated dependencies [[`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2)]:
- @thirdweb-dev/wallets@1.1.13
## 3.2.29
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "3.2.29",
"version": "3.2.34",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
+12
View File
@@ -1,5 +1,17 @@
# @thirdweb-dev/chains
## 0.1.45
### Patch Changes
- [#1506](https://github.com/thirdweb-dev/js/pull/1506) [`f97ddf4c`](https://github.com/thirdweb-dev/js/commit/f97ddf4c7f14854f3b204ad9741b52ddb8dac736) Thanks [@jnsdls](https://github.com/jnsdls)! - add xpla chains & update chains
## 0.1.44
### Patch Changes
- [#1499](https://github.com/thirdweb-dev/js/pull/1499) [`8f3b685a`](https://github.com/thirdweb-dev/js/commit/8f3b685ad2bd73cb4d5d8c8aa25c04ffc10fb7cf) Thanks [@Marfuen](https://github.com/Marfuen)! - Update deploy command from CLI to use show a public gateway url temporarily. Updated chains too.
## 0.1.43
### Patch Changes
+1
View File
@@ -18,6 +18,7 @@ export default {
"https://gnosis.api.onfinality.io/public",
"https://gnosis.blockpi.network/v1/rpc/public",
"https://web3endpoints.com/gnosischain-mainnet",
"https://gnosis.oat.farm",
"wss://rpc.gnosischain.com/wss"
],
"faucets": [
+29
View File
@@ -0,0 +1,29 @@
import type { Chain } from "../src/types";
export default {
"name": "Japan Open Chain Testnet",
"chain": "JOCT",
"rpc": [
"https://japan-open-chain-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-1.testnet.japanopenchain.org:8545",
"https://rpc-2.testnet.japanopenchain.org:8545"
],
"faucets": [],
"nativeCurrency": {
"name": "Japan Open Chain Testnet Token",
"symbol": "JOCT",
"decimals": 18
},
"infoURL": "https://www.japanopenchain.org/",
"shortName": "joct",
"chainId": 10081,
"networkId": 10081,
"explorers": [
{
"name": "Testnet Block Explorer",
"url": "https://explorer.testnet.japanopenchain.org",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "japan-open-chain-testnet"
} as const satisfies Chain;
+38
View File
@@ -0,0 +1,38 @@
import type { Chain } from "../src/types";
export default {
"name": "MaxxChain Mainnet",
"chain": "MaxxChain",
"rpc": [
"https://maxxchain.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.maxxchain.org",
"https://rpc1.maxxchain.org",
"https://rpc2.maxxchain.org"
],
"faucets": [
"https://faucet.maxxchain.org"
],
"nativeCurrency": {
"name": "Power",
"symbol": "PWR",
"decimals": 18
},
"icon": {
"url": "ipfs://QmRhQG9TZrXDdbQeTzBmjg3pqgwaD5TKVZj8k4kaz8AoZx",
"width": 1021,
"height": 1021,
"format": "png"
},
"infoURL": "https://www.maxxchain.org/",
"shortName": "PWR",
"chainId": 10201,
"networkId": 10201,
"explorers": [
{
"name": "MaxxChain Block Explorer",
"url": "https://explorer.maxxchain.org",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "maxxchain"
} as const satisfies Chain;
+42
View File
@@ -0,0 +1,42 @@
import type { Chain } from "../src/types";
export default {
"name": "Arthera Testnet",
"chain": "AA",
"icon": {
"url": "ipfs://QmYQp3e52KjkT4bYdAvB6ACEEpXs2D8DozsDitaADRY2Ak",
"width": 1024,
"height": 998,
"format": "png"
},
"rpc": [
"https://arthera-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-test.arthera.net"
],
"faucets": [
"https://faucet.arthera.net"
],
"nativeCurrency": {
"name": "Arthera",
"symbol": "AA",
"decimals": 18
},
"infoURL": "https://docs.arthera.net",
"shortName": "aa",
"chainId": 10243,
"networkId": 10243,
"explorers": [
{
"name": "blockscout",
"url": "https://explorer-test.arthera.net",
"icon": {
"url": "ipfs://QmYtUimyqHkkFxYdbXXRbUqNg2VLPUg6Uu2C2nmFWowiZM",
"width": 551,
"height": 540,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "arthera-testnet"
} as const satisfies Chain;
+44
View File
@@ -0,0 +1,44 @@
import type { Chain } from "../src/types";
export default {
"name": "Swisstronik Testnet",
"chain": "SWTR",
"rpc": [
"https://swisstronik-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://json-rpc.testnet.swisstronik.com"
],
"faucets": [
"https://faucet.testnet.swisstronik.com"
],
"nativeCurrency": {
"name": "Swisstronik",
"symbol": "SWTR",
"decimals": 18
},
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
],
"infoURL": "https://www.swisstronik.com",
"shortName": "swtr",
"chainId": 1291,
"networkId": 1291,
"icon": {
"url": "ipfs://bafybeihuintkoipxalwans23vhxajbwjnozpy34ww7ia7ds7nay3rpylzi",
"width": 256,
"height": 256,
"format": "svg"
},
"explorers": [
{
"name": "Swisstronik Scout",
"url": "https://explorer-evm.testnet.swisstronik.com",
"standard": "none"
}
],
"testnet": true,
"slug": "swisstronik-testnet"
} as const satisfies Chain;
+2 -2
View File
@@ -10,8 +10,8 @@ export default {
"https://t.me/HumanodeTestnet5FaucetBot"
],
"nativeCurrency": {
"name": "HMND",
"symbol": "HMND",
"name": "eHMND",
"symbol": "eHMND",
"decimals": 18
},
"infoURL": "https://humanode.io",
+44
View File
@@ -0,0 +1,44 @@
import type { Chain } from "../src/types";
export default {
"name": "Holesky",
"chain": "ETH",
"rpc": [
"https://holesky.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.holesky.ethpandaops.io"
],
"faucets": [
"https://faucet.holesky.ethpandaops.io",
"https://holesky-faucet.pk910.de"
],
"nativeCurrency": {
"name": "Testnet ETH",
"symbol": "ETH",
"decimals": 18
},
"infoURL": "https://holesky.ethpandaops.io",
"shortName": "holesky",
"chainId": 17000,
"networkId": 17000,
"icon": {
"url": "ipfs://QmdwQDr6vmBtXmK2TmknkEuZNoaDqTasFdZdu3DRw8b2wt",
"width": 1000,
"height": 1628,
"format": "png"
},
"status": "incubating",
"explorers": [
{
"name": "Holesky Explorer",
"url": "https://holesky.beaconcha.in",
"icon": {
"url": "ipfs://QmdwQDr6vmBtXmK2TmknkEuZNoaDqTasFdZdu3DRw8b2wt",
"width": 1000,
"height": 1628,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "holesky"
} as const satisfies Chain;
+35
View File
@@ -0,0 +1,35 @@
import type { Chain } from "../src/types";
export default {
"name": "Centrifuge",
"chain": "CFG",
"rpc": [
"wss://fullnode.parachain.centrifuge.io"
],
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
],
"faucets": [],
"nativeCurrency": {
"name": "Centrifuge",
"symbol": "CFG",
"decimals": 18
},
"infoURL": "https://centrifuge.io",
"shortName": "cfg",
"chainId": 2031,
"networkId": 2031,
"explorers": [
{
"name": "subscan",
"url": "https://centrifuge.subscan.io",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "centrifuge"
} as const satisfies Chain;
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"name": "Catalyst",
"chain": "CFG",
"rpc": [
"wss://fullnode.catalyst.cntrfg.com"
],
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
],
"faucets": [],
"nativeCurrency": {
"name": "Catalyst CFG",
"symbol": "NCFG",
"decimals": 18
},
"infoURL": "https://centrifuge.io",
"shortName": "ncfg",
"chainId": 2032,
"networkId": 2032,
"testnet": false,
"slug": "catalyst"
} as const satisfies Chain;
+33
View File
@@ -0,0 +1,33 @@
import type { Chain } from "../src/types";
export default {
"name": "Kiwi Subnet",
"chain": "KIWI",
"rpc": [
"https://kiwi-subnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/kiwi/testnet/rpc"
],
"features": [
{
"name": "EIP1559"
}
],
"faucets": [],
"nativeCurrency": {
"name": "Shrapgas",
"symbol": "SHRAP",
"decimals": 18
},
"infoURL": "",
"shortName": "kiwi",
"chainId": 2037,
"networkId": 2037,
"explorers": [
{
"name": "KIWI Explorer",
"url": "https://subnets-test.avax.network/kiwi",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "kiwi-subnet"
} as const satisfies Chain;
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"name": "Altair",
"chain": "AIR",
"rpc": [
"wss://fullnode.altair.centrifuge.io"
],
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
],
"faucets": [],
"nativeCurrency": {
"name": "Altair",
"symbol": "AIR",
"decimals": 18
},
"infoURL": "https://centrifuge.io",
"shortName": "air",
"chainId": 2088,
"networkId": 2088,
"testnet": false,
"slug": "altair"
} as const satisfies Chain;
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"name": "Algol",
"chain": "algol",
"rpc": [
"wss://fullnode.algol.cntrfg.com"
],
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
],
"faucets": [],
"nativeCurrency": {
"name": "Algol",
"symbol": "ALGL",
"decimals": 18
},
"infoURL": "https://centrifuge.io",
"shortName": "algl",
"chainId": 2089,
"networkId": 2089,
"testnet": false,
"slug": "algol"
} as const satisfies Chain;
+5 -4
View File
@@ -1,10 +1,11 @@
import type { Chain } from "../src/types";
export default {
"name": "Kava EVM Testnet",
"name": "Kava Testnet",
"chain": "KAVA",
"rpc": [
"https://kava-evm-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://kava-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://evm.testnet.kava.io",
"https://kava-evm-testnet.rpc.thirdweb.com",
"wss://wevm.testnet.kava.io"
],
"faucets": [
@@ -28,7 +29,7 @@ export default {
"explorers": [
{
"name": "Kava Testnet Explorer",
"url": "https://explorer.testnet.kava.io",
"url": "http://testnet.kavascan.com",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmdpRTk6oL1HRW9xC6cAc4Rnf9gs6zgdAcr4Z3HcLztusm",
@@ -39,5 +40,5 @@ export default {
}
],
"testnet": true,
"slug": "kava-evm-testnet"
"slug": "kava-testnet"
} as const satisfies Chain;
+6 -4
View File
@@ -1,11 +1,13 @@
import type { Chain } from "../src/types";
export default {
"name": "Kava EVM",
"name": "Kava",
"chain": "KAVA",
"rpc": [
"https://kava-evm.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://kava.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://evm.kava.io",
"https://evm2.kava.io",
"https://kava-rpc.gateway.pokt.network",
"https://kava-evm.rpc.thirdweb.com",
"wss://wevm.kava.io",
"wss://wevm2.kava.io",
"https://kava-evm.publicnode.com"
@@ -29,7 +31,7 @@ export default {
"explorers": [
{
"name": "Kava EVM Explorer",
"url": "https://explorer.kava.io",
"url": "https://kavascan.com",
"standard": "EIP3091",
"icon": {
"url": "ipfs://QmdpRTk6oL1HRW9xC6cAc4Rnf9gs6zgdAcr4Z3HcLztusm",
@@ -40,5 +42,5 @@ export default {
}
],
"testnet": false,
"slug": "kava-evm"
"slug": "kava"
} as const satisfies Chain;
+5 -1
View File
@@ -4,7 +4,11 @@ export default {
"chain": "SOMA",
"rpc": [
"https://soma-network-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://data-testnet-v1.somanetwork.io/"
"https://data-testnet-v1.somanetwork.io/",
"https://testnet-au-server-2.somanetwork.io",
"https://testnet-au-server-1.somanetwork.io",
"https://testnet-sg-server-1.somanetwork.io",
"https://testnet-sg-server-2.somanetwork.io"
],
"faucets": [
"https://faucet.somanetwork.io"
+4 -1
View File
@@ -4,7 +4,10 @@ export default {
"chain": "SOMA",
"rpc": [
"https://soma-network.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://data-mainnet-v1.somanetwork.io/"
"https://data-mainnet-v1.somanetwork.io/",
"https://id-mainnet.somanetwork.io",
"https://hk-mainnet.somanetwork.io",
"https://sg-mainnet.somanetwork.io"
],
"faucets": [
"https://airdrop.somanetwork.io"
+42
View File
@@ -0,0 +1,42 @@
import type { Chain } from "../src/types";
export default {
"name": "Unicorn Ultra Nebulas Testnet",
"chain": "u2u",
"rpc": [
"https://unicorn-ultra-nebulas-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc-nebulas-testnet.uniultra.xyz"
],
"faucets": [
"https://faucet.uniultra.xyz"
],
"nativeCurrency": {
"name": "Unicorn Ultra Nebulas Testnet",
"symbol": "U2U",
"decimals": 18
},
"infoURL": "https://uniultra.xyz",
"shortName": "u2u_nebulas",
"chainId": 2484,
"networkId": 2484,
"icon": {
"url": "ipfs://QmcW64RgqQVHnNbVFyfaMNKt7dJvFqEbfEHZmeyeK8dpEa",
"width": 512,
"height": 512,
"format": "png"
},
"explorers": [
{
"icon": {
"url": "ipfs://QmcW64RgqQVHnNbVFyfaMNKt7dJvFqEbfEHZmeyeK8dpEa",
"width": 512,
"height": 512,
"format": "png"
},
"name": "U2U Explorer",
"url": "https://testnet.u2uscan.xyz",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "unicorn-ultra-nebulas-testnet"
} as const satisfies Chain;
+38
View File
@@ -0,0 +1,38 @@
import type { Chain } from "../src/types";
export default {
"name": "Yooldo Verse Mainnet",
"chain": "Yooldo Verse",
"icon": {
"url": "ipfs://QmWvfA2usnYp1ktzLcqFS8eTLgdp4ifrxjiify46KyG9NZ",
"width": 512,
"height": 512,
"format": "png"
},
"rpc": [
"https://yooldo-verse.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://rpc.yooldo-verse.xyz/"
],
"faucets": [],
"nativeCurrency": {
"name": "OAS",
"symbol": "OAS",
"decimals": 18
},
"infoURL": "https://yooldo.gg/",
"shortName": "YVM",
"chainId": 345,
"networkId": 345,
"explorers": [
{
"name": "Yooldo Verse Explorer",
"url": "https://explorer.yooldo-verse.xyz",
"standard": "EIP3091"
}
],
"parent": {
"type": "L2",
"chain": "eip155-248"
},
"testnet": false,
"slug": "yooldo-verse"
} as const satisfies Chain;
+20 -15
View File
@@ -1,27 +1,32 @@
import type { Chain } from "../src/types";
export default {
"name": "SeedCoin-Network",
"chain": "SeedCoin-Network",
"name": "Xpla Mainnet",
"chain": "XPLA",
"rpc": [
"https://seedcoin-network.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://node.seedcoin.network"
"https://xpla.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://dimension-evm-rpc.xpla.dev"
],
"faucets": [],
"nativeCurrency": {
"name": "SeedCoin",
"symbol": "SEED",
"name": "XPLA",
"symbol": "XPLA",
"decimals": 18
},
"infoURL": "https://www.seedcoin.network/",
"shortName": "SEED",
"infoURL": "https://xpla.io/",
"shortName": "xpla",
"chainId": 37,
"icon": {
"url": "ipfs://QmSchLvCCZjBzcv5n22v1oFDAc2yHJ42NERyjZeL9hBgrh",
"width": 64,
"height": 64,
"url": "ipfs://Qmf4GoxfpeA5VGqu7KP5eyv1WKaCpNDbvMxq1MjQBwFWxq",
"width": 512,
"height": 512,
"format": "png"
},
"chainId": 37,
"networkId": 37,
"explorers": [
{
"name": "XPLA Explorer",
"url": "https://explorer.xpla.io",
"standard": "none"
}
],
"testnet": false,
"slug": "seedcoin-network"
"slug": "xpla"
} as const satisfies Chain;
+43
View File
@@ -0,0 +1,43 @@
import type { Chain } from "../src/types";
export default {
"name": "Alterium L2 Testnet",
"chain": "ALT",
"icon": {
"url": "ipfs://bafkreid3v7ow4c4t3ljya6aouiwvqbtssb2lzmkwt2eghryk234g7yynrq",
"width": 756,
"height": 756,
"format": "png"
},
"rpc": [
"https://alterium-l2-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://l2-testnet-rpc.altscan.org"
],
"faucets": [],
"nativeCurrency": {
"name": "Alterium ETH",
"symbol": "AltETH",
"decimals": 18
},
"infoURL": "https://alteriumprotocol.org",
"shortName": "alterium",
"chainId": 420692,
"networkId": 420692,
"parent": {
"type": "L2",
"chain": "eip155-5",
"bridges": [
{
"url": "https://testnet-bridge.alteriumprotocol.org"
}
]
},
"explorers": [
{
"name": "Alterium L2 Testnet Explorer",
"url": "https://l2-testnet.altscan.org",
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "alterium-l2-testnet"
} as const satisfies Chain;
+39
View File
@@ -0,0 +1,39 @@
import type { Chain } from "../src/types";
export default {
"name": "Orderly Sepolia Testnet",
"chain": "ETH",
"rpc": [
"https://orderly-sepolia-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://l2-orderly-l2-4460-sepolia-8tc3sd7dvy.t.conduit.xyz"
],
"faucets": [],
"nativeCurrency": {
"name": "Sepolia Ether",
"symbol": "ETH",
"decimals": 18
},
"shortName": "orderlyl2",
"chainId": 4460,
"networkId": 4460,
"icon": {
"url": "ipfs://QmSpwp3RAVhZsErAQrCQxEmjnGqJQMigarzZbfqr9Ktakb",
"width": 512,
"height": 512,
"format": "png"
},
"explorers": [
{
"name": "basescout",
"url": "https://explorerl2new-orderly-l2-4460-sepolia-8tc3sd7dvy.t.conduit.xyz",
"icon": {
"url": "ipfs://QmYtUimyqHkkFxYdbXXRbUqNg2VLPUg6Uu2C2nmFWowiZM",
"width": 551,
"height": 540,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "orderly-sepolia-testnet"
} as const satisfies Chain;
+21 -22
View File
@@ -1,36 +1,35 @@
import type { Chain } from "../src/types";
export default {
"name": "Acria IntelliChain",
"chain": "AIC",
"name": "Xpla Testnet",
"chain": "XPLA",
"rpc": [
"https://acria-intellichain.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://aic.acria.ai"
"https://xpla-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://cube-evm-rpc.xpla.dev"
],
"faucets": [],
"nativeCurrency": {
"name": "ACRIA",
"symbol": "ACRIA",
"name": "XPLA",
"symbol": "XPLA",
"decimals": 18
},
"features": [
{
"name": "EIP155"
},
{
"name": "EIP1559"
}
"faucets": [
"https://faucet.xpla.io/"
],
"infoURL": "https://acria.ai",
"shortName": "aic",
"infoURL": "https://xpla.io/",
"shortName": "xpla-test",
"chainId": 47,
"networkId": 47,
"icon": {
"url": "ipfs://Qmf4GoxfpeA5VGqu7KP5eyv1WKaCpNDbvMxq1MjQBwFWxq",
"width": 512,
"height": 512,
"format": "png"
},
"explorers": [
{
"name": "Acria IntelliChain-Explorer",
"url": "https://explorer.acria.ai",
"standard": "EIP3091"
"name": "XPLA Explorer",
"url": "https://explorer.xpla.io/testnet",
"standard": "none"
}
],
"testnet": false,
"slug": "acria-intellichain"
"testnet": true,
"slug": "xpla-testnet"
} as const satisfies Chain;
+2 -2
View File
@@ -8,8 +8,8 @@ export default {
],
"faucets": [],
"nativeCurrency": {
"name": "HMND",
"symbol": "HMND",
"name": "eHMND",
"symbol": "eHMND",
"decimals": 18
},
"infoURL": "https://humanode.io",
+28
View File
@@ -0,0 +1,28 @@
import type { Chain } from "../src/types";
export default {
"name": "PAWCHAIN Testnet",
"chain": "PAW",
"rpc": [
"https://pawchain-testnet.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://pawchainx.com/"
],
"faucets": [],
"nativeCurrency": {
"name": "PAW",
"symbol": "PAW",
"decimals": 18
},
"infoURL": "https://pawchainx.com/",
"shortName": "PAW",
"chainId": 542,
"networkId": 542,
"explorers": [
{
"name": "PAWCHAIN Testnet",
"url": "https://pawscan.io",
"standard": "none"
}
],
"testnet": true,
"slug": "pawchain-testnet"
} as const satisfies Chain;
+42
View File
@@ -0,0 +1,42 @@
import type { Chain } from "../src/types";
export default {
"name": "LAOS Arrakis",
"title": "LAOS Testnet Arrakis",
"chain": "LAOS",
"icon": {
"url": "ipfs://QmZ4YYcvhcaeotMPaGXC5Vab7JFaVkka37V8TiTJpT7Mak",
"width": 586,
"height": 558,
"format": "png"
},
"rpc": [
"https://laos-arrakis.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://arrakis.gorengine.com/own",
"wss://arrakis.gorengine.com/own"
],
"faucets": [],
"nativeCurrency": {
"name": "LAOS",
"symbol": "LAOS",
"decimals": 18
},
"infoURL": "https://www.laosfoundation.io/",
"shortName": "laos",
"chainId": 667,
"networkId": 667,
"explorers": [
{
"name": "blockscout",
"url": "https://arrakis.gorengine.com",
"icon": {
"url": "ipfs://QmZ4YYcvhcaeotMPaGXC5Vab7JFaVkka37V8TiTJpT7Mak",
"width": 586,
"height": 558,
"format": "png"
},
"standard": "EIP3091"
}
],
"testnet": true,
"slug": "laos-arrakis"
} as const satisfies Chain;
+5 -1
View File
@@ -4,7 +4,11 @@ export default {
"chain": "DSC",
"rpc": [
"https://decimal-smart-chain.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://node.decimalchain.com/web3/"
"https://node.decimalchain.com/web3/",
"https://node1-mainnet.decimalchain.com/web3/",
"https://node2-mainnet.decimalchain.com/web3/",
"https://node3-mainnet.decimalchain.com/web3/",
"https://node4-mainnet.decimalchain.com/web3/"
],
"faucets": [],
"nativeCurrency": {
+34
View File
@@ -0,0 +1,34 @@
import type { Chain } from "../src/types";
export default {
"name": "Miexs Smartchain",
"chain": "MiexsSmartchain",
"icon": {
"url": "ipfs://bafkreic6tcc6swh5kzljwqnswj6rlemcm7n6ra7xkgttwv5v3fv7ozj5zu",
"width": 1500,
"height": 1500,
"format": "png"
},
"rpc": [
"https://miexs-smartchain.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://mainnet-rpc.miexs.com"
],
"faucets": [],
"nativeCurrency": {
"name": "Miexs Coin",
"symbol": "MIX",
"decimals": 18
},
"infoURL": "https://miexs.com",
"shortName": "Miexs",
"chainId": 761412,
"networkId": 761412,
"explorers": [
{
"name": "Miexs Smartchain Explorer",
"url": "https://miexs.com",
"standard": "EIP3091"
}
],
"testnet": false,
"slug": "miexs-smartchain"
} as const satisfies Chain;
+2 -1
View File
@@ -4,7 +4,8 @@ export default {
"chain": "ETH",
"rpc": [
"https://base.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://mainnet.base.org/"
"https://mainnet.base.org/",
"https://developer-access-mainnet.base.org/"
],
"faucets": [],
"nativeCurrency": {
+31
View File
@@ -0,0 +1,31 @@
// @ts-check
/** @typedef { import("../../src/types").Chain } Chain */
/** @type {Omit<Chain, "slug">} */
export default {
name: "Xpla Mainnet",
chain: "XPLA",
rpc: ["https://dimension-evm-rpc.xpla.dev"],
nativeCurrency: {
name: "XPLA",
symbol: "XPLA",
decimals: 18,
},
infoURL: "https://xpla.io/",
shortName: "xpla",
chainId: 37,
icon: {
url: "ipfs://Qmf4GoxfpeA5VGqu7KP5eyv1WKaCpNDbvMxq1MjQBwFWxq",
width: 512,
height: 512,
format: "png",
},
explorers: [
{
name: "XPLA Explorer",
url: "https://explorer.xpla.io",
standard: "none",
},
],
testnet: false,
};
+32
View File
@@ -0,0 +1,32 @@
// @ts-check
/** @typedef { import("../../src/types").Chain } Chain */
/** @type {Omit<Chain, "slug">} */
export default {
name: "Xpla Testnet",
chain: "XPLA",
rpc: ["https://cube-evm-rpc.xpla.dev"],
nativeCurrency: {
name: "XPLA",
symbol: "XPLA",
decimals: 18,
},
faucets: ["https://faucet.xpla.io/"],
infoURL: "https://xpla.io/",
shortName: "xpla-test",
chainId: 47,
icon: {
url: "ipfs://Qmf4GoxfpeA5VGqu7KP5eyv1WKaCpNDbvMxq1MjQBwFWxq",
width: 512,
height: 512,
format: "png",
},
explorers: [
{
name: "XPLA Explorer",
url: "https://explorer.xpla.io/testnet",
standard: "none",
},
],
testnet: true,
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/chains",
"version": "0.1.43",
"version": "0.1.45",
"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
+40
View File
@@ -1,5 +1,45 @@
# thirdweb
## 0.11.17
### Patch Changes
- Updated dependencies [[`f97ddf4c`](https://github.com/thirdweb-dev/js/commit/f97ddf4c7f14854f3b204ad9741b52ddb8dac736)]:
- @thirdweb-dev/chains@0.1.45
- @thirdweb-dev/sdk@3.10.53
## 0.11.16
### Patch Changes
- Updated dependencies [[`e1962641`](https://github.com/thirdweb-dev/js/commit/e19626417218767a0e44c00f440761d7b86d02eb), [`bd1fcbae`](https://github.com/thirdweb-dev/js/commit/bd1fcbae327e788124f5635673511f0b72e9d7ab)]:
- @thirdweb-dev/sdk@3.10.52
## 0.11.15
### Patch Changes
- [#1499](https://github.com/thirdweb-dev/js/pull/1499) [`8f3b685a`](https://github.com/thirdweb-dev/js/commit/8f3b685ad2bd73cb4d5d8c8aa25c04ffc10fb7cf) Thanks [@Marfuen](https://github.com/Marfuen)! - Update deploy command from CLI to use show a public gateway url temporarily. Updated chains too.
- Updated dependencies [[`d248aa2c`](https://github.com/thirdweb-dev/js/commit/d248aa2c5a89a297dd2623c961793026de1de346), [`d248aa2c`](https://github.com/thirdweb-dev/js/commit/d248aa2c5a89a297dd2623c961793026de1de346), [`8f3b685a`](https://github.com/thirdweb-dev/js/commit/8f3b685ad2bd73cb4d5d8c8aa25c04ffc10fb7cf), [`28975765`](https://github.com/thirdweb-dev/js/commit/2897576513eb6f497a9f92e3e473182b4fc9681b), [`b91d3e99`](https://github.com/thirdweb-dev/js/commit/b91d3e990198b77dc1358e738c11dc4acaa67491)]:
- @thirdweb-dev/sdk@3.10.51
- @thirdweb-dev/storage@1.2.6
- @thirdweb-dev/chains@0.1.44
## 0.11.14
### Patch Changes
- Updated dependencies [[`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117), [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
- @thirdweb-dev/sdk@3.10.50
## 0.11.13
### Patch Changes
- Updated dependencies [[`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696)]:
- @thirdweb-dev/sdk@3.10.49
## 0.11.12
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.11.12",
"version": "0.11.17",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+4 -1
View File
@@ -70,7 +70,10 @@ export async function deployApp(
try {
const uri = await upload(storage, distPath);
return storage.resolveScheme(uri);
return `${uri.replace(
"ipfs://",
"https://cf-ipfs.com/ipfs/",
)}`;
} catch (err) {
console.error("Can't upload project", err);
return Promise.reject("Can't upload project");
+5 -3
View File
@@ -199,13 +199,15 @@ export async function generate(options: GenerateOptions, apiSecretKey: string) {
}
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
if (packageJson.scripts?.postinstall?.includes("thirdweb generate") || packageJson.scripts?.postinstall?.includes("thirdweb@latest generate")) {
if (
packageJson.scripts?.postinstall?.includes("thirdweb generate") ||
packageJson.scripts?.postinstall?.includes("thirdweb@latest generate")
) {
return;
}
const postinstall = packageJson.scripts?.postinstall
? packageJson.scripts.postinstall +
`npx --yes thirdweb@latest generate`
? packageJson.scripts.postinstall + `npx --yes thirdweb@latest generate`
: `npx --yes thirdweb@latest generate`;
fs.writeFileSync(
+52
View File
@@ -1,5 +1,57 @@
# @thirdweb-dev/react-core
## 3.14.34
### Patch Changes
- Updated dependencies [[`f97ddf4c`](https://github.com/thirdweb-dev/js/commit/f97ddf4c7f14854f3b204ad9741b52ddb8dac736)]:
- @thirdweb-dev/chains@0.1.45
- @thirdweb-dev/sdk@3.10.53
- @thirdweb-dev/wallets@1.1.17
- @thirdweb-dev/auth@3.2.34
## 3.14.33
### Patch Changes
- Updated dependencies [[`e1962641`](https://github.com/thirdweb-dev/js/commit/e19626417218767a0e44c00f440761d7b86d02eb), [`bd1fcbae`](https://github.com/thirdweb-dev/js/commit/bd1fcbae327e788124f5635673511f0b72e9d7ab)]:
- @thirdweb-dev/sdk@3.10.52
- @thirdweb-dev/wallets@1.1.16
- @thirdweb-dev/auth@3.2.33
## 3.14.32
### Patch Changes
- [#1489](https://github.com/thirdweb-dev/js/pull/1489) [`07fb1b5f`](https://github.com/thirdweb-dev/js/commit/07fb1b5ffa4c170e252df31070852ddb9a81dec9) Thanks [@iketw](https://github.com/iketw)! - Cleanup and minor bug fix in ReactCore
- [#1486](https://github.com/thirdweb-dev/js/pull/1486) [`300a3c6f`](https://github.com/thirdweb-dev/js/commit/300a3c6f04d0ea7e25dfdb0a4c28b3a5796fcadf) Thanks [@iketw](https://github.com/iketw)! - Add better error messages when using hooks outside the <ThirdwebProvider />
- Updated dependencies [[`07fb1b5f`](https://github.com/thirdweb-dev/js/commit/07fb1b5ffa4c170e252df31070852ddb9a81dec9), [`d248aa2c`](https://github.com/thirdweb-dev/js/commit/d248aa2c5a89a297dd2623c961793026de1de346), [`d248aa2c`](https://github.com/thirdweb-dev/js/commit/d248aa2c5a89a297dd2623c961793026de1de346), [`8f3b685a`](https://github.com/thirdweb-dev/js/commit/8f3b685ad2bd73cb4d5d8c8aa25c04ffc10fb7cf), [`28975765`](https://github.com/thirdweb-dev/js/commit/2897576513eb6f497a9f92e3e473182b4fc9681b), [`b91d3e99`](https://github.com/thirdweb-dev/js/commit/b91d3e990198b77dc1358e738c11dc4acaa67491)]:
- @thirdweb-dev/wallets@1.1.15
- @thirdweb-dev/sdk@3.10.51
- @thirdweb-dev/storage@1.2.6
- @thirdweb-dev/chains@0.1.44
- @thirdweb-dev/auth@3.2.32
## 3.14.31
### Patch Changes
- Updated dependencies [[`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74), [`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117), [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
- @thirdweb-dev/wallets@1.1.14
- @thirdweb-dev/sdk@3.10.50
- @thirdweb-dev/auth@3.2.31
## 3.14.30
### Patch Changes
- Updated dependencies [[`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2), [`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696)]:
- @thirdweb-dev/wallets@1.1.13
- @thirdweb-dev/sdk@3.10.49
- @thirdweb-dev/auth@3.2.30
## 3.14.29
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "3.14.29",
"version": "3.14.34",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -229,7 +229,11 @@ export function ThirdwebWalletProvider(
try {
const parsedWallet = JSON.parse(lastConnectedWallet as string);
parsedWallet.connectParams.chainId = chainId;
if (parsedWallet.connectParams) {
parsedWallet.connectParams.chainId = chainId;
} else {
parsedWallet.connectParams = { chainId };
}
await lastConnectedWalletStorage.setItem(
LAST_CONNECTED_WALLET_STORAGE_KEY,
JSON.stringify(parsedWallet),
@@ -1,15 +1,15 @@
import { Chain, defaultChains } from "@thirdweb-dev/chains";
import { Chain } from "@thirdweb-dev/chains";
import React, { createContext, PropsWithChildren, useContext } from "react";
import invariant from "tiny-invariant";
interface ThirdwebConfigContext {
chains: Chain[];
clientId?: string;
}
const ThirdwebConfigContext =
/* @__PURE__ */ createContext<ThirdwebConfigContext>({
chains: defaultChains,
});
export const ThirdwebConfigContext = /* @__PURE__ */ createContext<
ThirdwebConfigContext | undefined
>(undefined);
export const ThirdwebConfigProvider: React.FC<
PropsWithChildren<{
@@ -22,5 +22,10 @@ export const ThirdwebConfigProvider: React.FC<
);
export function useThirdwebConfigContext() {
return useContext(ThirdwebConfigContext);
const context = useContext(ThirdwebConfigContext);
invariant(
context,
"useThirdwebConfigContext() hook must be used within a <ThirdwebProvider/>",
);
return context;
}
@@ -11,6 +11,7 @@ import React, {
useState,
} from "react";
import { useSDK } from "../hooks/useSDK";
import invariant from "tiny-invariant";
interface ThirdwebConnectedWalletContext {
wallet: UserWallet | undefined;
@@ -26,10 +27,9 @@ const INITIAL_CONTEXT_VALUE: ThirdwebConnectedWalletContext = {
signer: undefined,
};
const ThirdwebConnectedWalletContext =
/* @__PURE__ */ createContext<ThirdwebConnectedWalletContext>(
INITIAL_CONTEXT_VALUE,
);
export const ThirdwebConnectedWalletContext = /* @__PURE__ */ createContext<
ThirdwebConnectedWalletContext | undefined
>(undefined);
export const ThirdwebConnectedWalletProvider: React.FC<
PropsWithChildren<{ signer?: Signer }>
@@ -115,5 +115,10 @@ export const ThirdwebConnectedWalletProvider: React.FC<
};
export function useThirdwebConnectedWalletContext() {
return useContext(ThirdwebConnectedWalletContext);
const context = useContext(ThirdwebConnectedWalletContext);
invariant(
context,
"useThirdwebConnectedWalletContext() hook must be used within a <ThirdwebProvider/>",
);
return context;
}
@@ -22,6 +22,6 @@ export function useAuth() {
return undefined;
}
return new ThirdwebAuth(wallet, authConfig.domain)
}, [wallet, authConfig, authConfig?.domain])
return new ThirdwebAuth(wallet, authConfig.domain);
}, [wallet, authConfig?.domain]);
}
@@ -1,10 +1,17 @@
import { useThirdwebConnectedWalletContext } from "../contexts/thirdweb-wallet";
import { useContext } from "react";
import { ThirdwebConnectedWalletContext } from "../contexts/thirdweb-wallet";
import type { Signer } from "ethers";
import invariant from "tiny-invariant";
/**
*
* @internal
*/
export function useSigner(): Signer | undefined {
return useThirdwebConnectedWalletContext().signer;
const context = useContext(ThirdwebConnectedWalletContext);
invariant(
context,
"useSigner() hook must be used within a <ThirdwebProvider/>",
);
return context.signer;
}
@@ -1,5 +1,12 @@
import { useThirdwebConfigContext } from "../contexts/thirdweb-config";
import { useContext } from "react";
import invariant from "tiny-invariant";
import { ThirdwebConfigContext } from "../contexts/thirdweb-config";
export function useSupportedChains() {
return useThirdwebConfigContext().chains;
const context = useContext(ThirdwebConfigContext);
invariant(
context,
"useSupportedChains() hook must be used within a <ThirdwebProvider/>",
);
return context.chains;
}
+24 -5
View File
@@ -1,10 +1,14 @@
import { useThirdwebConnectedWalletContext } from "../contexts/thirdweb-wallet";
import invariant from "tiny-invariant";
import {
ThirdwebConnectedWalletContext,
useThirdwebConnectedWalletContext,
} from "../contexts/thirdweb-wallet";
import { ContractAddress } from "../types";
import { cacheKeys } from "../utils/cache-keys";
import { useSupportedChains } from "./useSupportedChains";
import { useQuery } from "@tanstack/react-query";
import { Chain, defaultChains } from "@thirdweb-dev/chains";
import { useMemo } from "react";
import { useContext, useMemo } from "react";
/**
* A hook to get the native or (optional) ERC20 token balance of the connected wallet.
@@ -40,7 +44,12 @@ export function useBalance(tokenAddress?: ContractAddress) {
* @internal
*/
export function useConnectedWallet() {
return useThirdwebConnectedWalletContext().wallet;
const context = useContext(ThirdwebConnectedWalletContext);
invariant(
context,
"useConnectedWallet() hook must be used within a <ThirdwebProvider/>",
);
return context.signer;
}
/**
@@ -71,7 +80,12 @@ export function useConnectedWallet() {
* @public
*/
export function useAddress(): string | undefined {
return useThirdwebConnectedWalletContext().address;
const context = useContext(ThirdwebConnectedWalletContext);
invariant(
context,
"useAddress() hook must be used within a <ThirdwebProvider/>",
);
return context.address;
}
/**
@@ -96,7 +110,12 @@ export function useAddress(): string | undefined {
* @public
*/
export function useChainId(): number | undefined {
return useThirdwebConnectedWalletContext().chainId;
const context = useContext(ThirdwebConnectedWalletContext);
invariant(
context,
"useChainId() hook must be used within a <ThirdwebProvider/>",
);
return context.chainId;
}
/**
+10
View File
@@ -1,5 +1,15 @@
# @thirdweb-dev/react-native-compat
## 0.2.53
## 0.2.52
## 0.2.51
## 0.2.50
## 0.2.49
## 0.2.48
## 0.2.47
+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.48",
"version": "0.2.53",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
+84
View File
@@ -1,5 +1,89 @@
# @thirdweb-dev/react-native
## 0.2.53
### Patch Changes
- Updated dependencies [[`f97ddf4c`](https://github.com/thirdweb-dev/js/commit/f97ddf4c7f14854f3b204ad9741b52ddb8dac736)]:
- @thirdweb-dev/chains@0.1.45
- @thirdweb-dev/react-core@3.14.34
- @thirdweb-dev/sdk@3.10.53
- @thirdweb-dev/wallets@1.1.17
## 0.2.52
### Patch Changes
- Updated dependencies [[`e1962641`](https://github.com/thirdweb-dev/js/commit/e19626417218767a0e44c00f440761d7b86d02eb), [`bd1fcbae`](https://github.com/thirdweb-dev/js/commit/bd1fcbae327e788124f5635673511f0b72e9d7ab)]:
- @thirdweb-dev/sdk@3.10.52
- @thirdweb-dev/react-core@3.14.33
- @thirdweb-dev/wallets@1.1.16
## 0.2.51
### Patch Changes
- [#1489](https://github.com/thirdweb-dev/js/pull/1489) [`07fb1b5f`](https://github.com/thirdweb-dev/js/commit/07fb1b5ffa4c170e252df31070852ddb9a81dec9) Thanks [@iketw](https://github.com/iketw)! - Adding walletConnect as a supportedWallet in React Native
```javascript
import {
localWallet,
ThirdwebProvider,
walletConnect,
} from "@thirdweb-dev/react-native";
import { Ethereum } from "@thirdweb-dev/chains";
const App = () => {
const activeChain = Ethereum;
return (
<ThirdwebProvider
activeChain={activeChain}
supportedChains={[activeChain]}
clientId="your-client-id"
supportedWallets={[
localWallet(),
walletConnect({
projectId: "your-wallet-connect-project-id", // optional, but we recommend you get your own for production
}),
]}
>
<AppInner />
</ThirdwebProvider>
);
};
```
- [#1496](https://github.com/thirdweb-dev/js/pull/1496) [`26349f31`](https://github.com/thirdweb-dev/js/commit/26349f311d144d692c69a6c2a7ec0f34949851d4) Thanks [@iketw](https://github.com/iketw)! - Improves loading states when disconnecting and fetching balances
- Updated dependencies [[`07fb1b5f`](https://github.com/thirdweb-dev/js/commit/07fb1b5ffa4c170e252df31070852ddb9a81dec9), [`300a3c6f`](https://github.com/thirdweb-dev/js/commit/300a3c6f04d0ea7e25dfdb0a4c28b3a5796fcadf), [`d248aa2c`](https://github.com/thirdweb-dev/js/commit/d248aa2c5a89a297dd2623c961793026de1de346), [`d248aa2c`](https://github.com/thirdweb-dev/js/commit/d248aa2c5a89a297dd2623c961793026de1de346), [`8f3b685a`](https://github.com/thirdweb-dev/js/commit/8f3b685ad2bd73cb4d5d8c8aa25c04ffc10fb7cf), [`28975765`](https://github.com/thirdweb-dev/js/commit/2897576513eb6f497a9f92e3e473182b4fc9681b), [`b91d3e99`](https://github.com/thirdweb-dev/js/commit/b91d3e990198b77dc1358e738c11dc4acaa67491)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.2.50
### Patch Changes
- Updated dependencies [[`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74), [`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117), [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.2.49
### Patch Changes
- [#1479](https://github.com/thirdweb-dev/js/pull/1479) [`14d9a71c`](https://github.com/thirdweb-dev/js/commit/14d9a71c928019907618cc8694af0bc631e94b68) Thanks [@iketw](https://github.com/iketw)! - Deprecates magicWallet in favor of magicLink for consistency with our React package
- [#1477](https://github.com/thirdweb-dev/js/pull/1477) [`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Pass unauth'd RPCs to wallets when adding new chains
- Updated dependencies [[`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2), [`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696)]:
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
- @thirdweb-dev/[email protected]
## 0.2.48
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.2.48",
"version": "0.2.53",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -128,7 +128,7 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
) {
return reject(
new Error(
`This looks like a network error, the endpoint might be blocked by an internet provider or a firewall. ${xhr.responseText}`,
`Upload failed due to a network error. ${xhr.responseText}`,
),
);
}
@@ -3,12 +3,13 @@ import BaseButton from "../base/BaseButton";
import { ChainIcon } from "../base/ChainIcon";
import Text from "../base/Text";
import { WalletIcon } from "../base/WalletIcon";
import { useWallet, useBalance, useChain } from "@thirdweb-dev/react-core";
import { useWallet, useChain } from "@thirdweb-dev/react-core";
import { StyleSheet, View } from "react-native";
import { LocalWallet } from "@thirdweb-dev/wallets";
import Box from "../base/Box";
import { ConnectWalletDetailsModal } from "./ConnectWalletDetailsModal";
import { useState } from "react";
import { TextBalance } from "../base/TextBalance";
export type ConnectWalletDetailsProps = {
address?: string;
@@ -25,7 +26,6 @@ export const WalletDetailsButton = ({
}: ConnectWalletDetailsProps) => {
const activeWallet = useWallet();
const chain = useChain();
const balanceQuery = useBalance();
const [isModalVisible, setIsModalVisible] = useState(false);
const onPress = () => {
@@ -64,12 +64,7 @@ export const WalletDetailsButton = ({
<ChainIcon size={32} chainIconUrl={chain?.icon?.url} />
</View>
<Box justifyContent="center" alignItems="flex-start">
<Text variant="bodySmall">
{balanceQuery.data
? Number(balanceQuery.data.displayValue).toFixed(3)
: ""}{" "}
{balanceQuery.data?.symbol}
</Text>
<TextBalance textVariant="bodySmall" />
{activeWallet?.walletId === LocalWallet.id ? (
<Text variant="error">Guest</Text>
) : (
@@ -2,14 +2,14 @@ import { Icon } from "../../assets/icon";
import { useAppTheme } from "../../styles/hooks";
import { AddressDisplay } from "../base/AddressDisplay";
import BaseButton from "../base/BaseButton";
import Text from "../base/Text";
import { WalletIcon } from "../base/WalletIcon";
import { useWallet, useBalance } from "@thirdweb-dev/react-core";
import { useWallet } from "@thirdweb-dev/react-core";
import { ActivityIndicator, StyleSheet, View } from "react-native";
import * as Clipboard from "expo-clipboard";
import Box from "../base/Box";
import CopyIcon from "../../assets/copy";
import { useState } from "react";
import { TextBalance } from "../base/TextBalance";
interface WalletDetailsModalHeaderProps {
address?: string;
@@ -24,7 +24,6 @@ export const WalletDetailsModalHeader = ({
onAddressCopied,
}: WalletDetailsModalHeaderProps) => {
const theme = useAppTheme();
const balanceQuery = useBalance();
const activeWallet = useWallet();
const [showLoading, setShowLoading] = useState(false);
@@ -38,7 +37,9 @@ export const WalletDetailsModalHeader = ({
const onDisconnectPressInternal = () => {
setShowLoading(true);
onDisconnectPress();
setTimeout(() => {
onDisconnectPress();
}, 0);
};
return (
@@ -65,12 +66,7 @@ export const WalletDetailsModalHeader = ({
color={theme.colors.textSecondary}
/>
</Box>
<Text variant="bodySmallSecondary">
{balanceQuery.data
? Number(balanceQuery.data.displayValue).toFixed(3)
: ""}{" "}
{balanceQuery.data?.symbol}
</Text>
<TextBalance textVariant="bodySmallSecondary" />
</BaseButton>
{showLoading ? (
<ActivityIndicator size="small" color={theme.colors.iconHighlight} />
@@ -89,6 +89,10 @@ export const ConnectWalletFlow = () => {
setIsConnecting(false);
};
const handleClose = useCallback(() => {
onClose(true);
}, [onClose]);
const getComponentForWallet = useCallback(() => {
switch (activeWallet?.id) {
case SmartWallet.id:
@@ -100,7 +104,7 @@ export const ConnectWalletFlow = () => {
<activeWallet.connectUI
theme={theme || "dark"}
goBack={onBackPress}
close={() => onClose(true)}
close={handleClose}
isOpen={modalVisible}
open={onOpenModal}
walletConfig={activeWallet}
@@ -112,6 +116,7 @@ export const ConnectWalletFlow = () => {
}
}, [
activeWallet,
handleClose,
modalVisible,
onBackPress,
onClose,
@@ -64,6 +64,7 @@ const styles = StyleSheet.create({
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
padding: 24,
paddingBottom: 40,
},
contentContainer: {
maxHeight: MODAL_HEIGHT,
@@ -3,6 +3,7 @@ import { useState } from "react";
import { Image } from "react-native";
import { SvgUri } from "react-native-svg";
import { isAppBundleIdPresentInGlobal } from "../../utils/global";
import Box from "./Box";
const ImageSvgUri = ({
imageUrl = "",
@@ -29,29 +30,29 @@ const ImageSvgUri = ({
return null;
}
if (error) {
return (
<SvgUri
width={width}
height={height}
uri={resolvedImageUrl}
onError={(err) => {
console.warn("Error loading an svg image: ", err);
}}
/>
);
} else {
// always try to render Image first, if error then try to render svg
// Image from RN handles onError better than SvgUri
return (
<Image
alt={imageAlt}
source={{ uri: resolvedImageUrl }}
style={[{ width: width, height: height }]}
onError={() => setError(true)}
/>
);
}
return (
<Box style={{ width: width, height: height }}>
{error ? (
<SvgUri
width={width}
height={height}
uri={resolvedImageUrl}
onError={(err) => {
console.warn("Error loading an svg image: ", err);
}}
/>
) : (
// always try to render Image first, if error then try to render svg
// Image from RN handles onError better than SvgUri
<Image
alt={imageAlt}
source={{ uri: resolvedImageUrl }}
style={[{ width: width, height: height }]}
onError={() => setError(true)}
/>
)}
</Box>
);
};
export default ImageSvgUri;
@@ -0,0 +1,67 @@
import React, { useCallback, useEffect, useRef } from "react";
import { View, Animated, Easing, StyleSheet } from "react-native";
import { baseTheme } from "../../styles/theme";
import { useAppTheme } from "../../styles/hooks";
type LoadingTextAnimationProps = {
text: string;
textVariant: keyof typeof baseTheme.textVariants;
};
const LoadingTextAnimation = ({
text,
textVariant,
}: LoadingTextAnimationProps) => {
const fadeAnim = useRef(new Animated.Value(0)).current;
const theme = useAppTheme();
const fadeInOut = useCallback(() => {
Animated.loop(
Animated.sequence([
Animated.timing(fadeAnim, {
toValue: 1,
duration: 600,
easing: Easing.linear,
useNativeDriver: true,
}),
Animated.timing(fadeAnim, {
toValue: 0,
duration: 600,
easing: Easing.linear,
useNativeDriver: true,
}),
]),
{ iterations: -1 },
).start();
}, [fadeAnim]);
useEffect(() => {
fadeInOut();
}, [fadeInOut]);
return (
<View style={styles.animatedView}>
<Animated.Text
style={{
color: theme.colors.textSecondary,
fontWeight: theme.textVariants[textVariant].fontWeight as any,
fontSize: theme.textVariants[textVariant].fontSize,
lineHeight: theme.textVariants[textVariant].lineHeight,
opacity: fadeAnim,
}}
>
{text}
</Animated.Text>
</View>
);
};
export default LoadingTextAnimation;
const styles = StyleSheet.create({
animatedView: {
flexDirection: "row",
justifyContent: "flex-start",
flex: 1,
},
});
@@ -0,0 +1,21 @@
import { useBalance } from "@thirdweb-dev/react-core";
import Text from "./Text";
import LoadingTextAnimation from "./LoadingTextAnimation";
import { baseTheme } from "../../styles/theme";
type TextBalance = {
textVariant: keyof typeof baseTheme.textVariants;
};
export const TextBalance = ({ textVariant }: TextBalance) => {
const balanceQuery = useBalance();
return !balanceQuery.data ? (
<LoadingTextAnimation text="Fetching..." textVariant={textVariant} />
) : (
<Text variant={textVariant}>
{Number(balanceQuery.data?.displayValue).toFixed(3)}{" "}
{balanceQuery.data.symbol}
</Text>
);
};
@@ -3,7 +3,12 @@ import Modal, { ModalProps } from "react-native-modal";
// Populate with the data...
export function TWModal(props: Partial<ModalProps>) {
return (
<Modal {...props} useNativeDriver hideModalContentWhileAnimating={true}>
<Modal
{...props}
avoidKeyboard
useNativeDriver
hideModalContentWhileAnimating={true}
>
{props.children}
</Modal>
);
@@ -1,2 +1,7 @@
export const TW_WC_PROJECT_ID = "145769e410f16970a79ff77b2d89a1e0";
export const WC_RELAY_URL = "wss://relay.walletconnect.com";
export const WC_LINKS = {
native: "wc://",
universal: "wc://",
};
@@ -0,0 +1,40 @@
import { useCallback, useEffect, useRef } from "react";
interface Props {
callback: (args: any) => void;
delay?: number;
}
export function useDebounceCallback({ callback, delay = 250 }: Props) {
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const callbackRef = useRef(callback);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
const debouncedCallback = useCallback(
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore args can be any in this case
(args) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
callbackRef.current(args);
}, delay);
},
[delay],
);
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
return debouncedCallback;
}
+7 -3
View File
@@ -15,9 +15,13 @@ export { TrustWallet, trustWallet } from "./wallets/wallets/trust-wallet";
export { smartWallet } from "./wallets/wallets/smart-wallet";
export { localWallet } from "./wallets/wallets/local-wallet";
export { LocalWallet } from "./wallets/wallets/LocalWallet";
export { magicWallet } from "./wallets/wallets/magic-wallet";
export { MagicWallet } from "./wallets/wallets/MagicWallet";
export * from "./wallets/wallets/WalletConnectV2";
export { magicWallet } from "./wallets/wallets/magic-link";
export { MagicWallet } from "./wallets/wallets/MagicLink";
export { MagicLink } from "./wallets/wallets/MagicLink";
export { magicLink } from "./wallets/wallets/magic-link";
export * from "./wallets/wallets/wallet-connect/WalletConnectBase";
export { WalletConnect } from "./wallets/wallets/wallet-connect/WalletConnect";
export { walletConnect } from "./wallets/wallets/wallet-connect/wallet-connect";
export type { WCMeta } from "./wallets/types/wc";
export { useCoinbaseWallet } from "./wallets/hooks/useCoinbaseWallet";
@@ -7,47 +7,47 @@ import {
useState,
} from "react";
import { useWalletConnectListener } from "../wallets/hooks/useWalletConnectListener";
import { MagicWallet } from "../wallets/wallets/MagicWallet";
import { MagicLink } from "../wallets/wallets/MagicLink";
import { useWalletContext } from "@thirdweb-dev/react-core";
type DappContextType = {
smartWallet?: SmartWallet;
setSmartWallet?: (value?: SmartWallet) => void;
magicWallet?: MagicWallet;
setMagicWallet?: (value?: MagicWallet) => void;
magicLink?: MagicLink;
setMagicLink?: (value?: MagicLink) => void;
};
const DappContext = createContext<DappContextType>({});
export const DappContextProvider = (props: React.PropsWithChildren) => {
const [smartWallet, setSmartWallet] = useState<SmartWallet | undefined>();
const [magicWallet, setMagicWallet] = useState<MagicWallet | undefined>();
const [magicLink, setMagicLink] = useState<MagicLink | undefined>();
const createdWalletInstance = useWalletContext().createdWalletInstance;
useEffect(() => {
if (createdWalletInstance?.walletId === walletIds.magicLink) {
setMagicWallet(createdWalletInstance as MagicWallet);
setMagicLink(createdWalletInstance as MagicLink);
}
}, [createdWalletInstance, setMagicWallet]);
}, [createdWalletInstance, setMagicLink]);
useWalletConnectListener();
const magicSDK = useCallback(() => {
if (magicWallet) {
const magic = magicWallet.getMagicSDK();
if (magicLink) {
const magic = magicLink.getMagicSDK();
return <magic.Relayer />;
}
return null;
}, [magicWallet]);
}, [magicLink]);
return (
<DappContext.Provider
value={{
smartWallet,
setSmartWallet,
magicWallet: magicWallet,
setMagicWallet: setMagicWallet,
magicLink: magicLink,
setMagicLink: setMagicLink,
}}
>
{props.children}
@@ -66,11 +66,11 @@ export const useSmartWallet = () => {
return [context.smartWallet, context.setSmartWallet] as const;
};
export const useMagicWallet = () => {
export const useMagicLink = () => {
const context = useContext(DappContext);
return {
magicWallet: context.magicWallet,
setMagicWallet: context.setMagicWallet,
magicLink: context.magicLink,
setMagicLink: context.setMagicLink,
} as const;
};
@@ -0,0 +1,8 @@
export type WCWallet = {
iconURL: string;
name: string;
links: {
native: string;
universal: string;
};
};
@@ -1,3 +1,4 @@
import { Chain, getValidChainRPCs } from "@thirdweb-dev/chains";
import { WCMeta } from "../wallets/types/wc";
/**
@@ -20,3 +21,19 @@ export function formatWalletConnectDisplayUri(
}wc?uri=${encodedUri}`
: `${uri}`;
}
export function getValidPublicRPCUrl(chain: Chain) {
return getValidChainRPCs(chain).map((rpc) => {
try {
const url = new URL(rpc);
// remove client id from url
if (url.hostname.endsWith(".thirdweb.com")) {
url.pathname = "";
url.search = "";
}
return url.toString();
} catch (e) {
return rpc;
}
});
}
@@ -4,7 +4,6 @@ import type {
WalletMobileSDKProviderOptions,
} from "@coinbase/wallet-mobile-sdk/build/WalletMobileSDKEVMProvider";
import type { ConfigurationParams } from "@coinbase/wallet-mobile-sdk/src/CoinbaseWalletSDK.types";
import { getValidChainRPCs } from "@thirdweb-dev/chains";
import {
UserRejectedRequestError,
ChainNotConfiguredError,
@@ -18,6 +17,7 @@ import {
} from "@thirdweb-dev/wallets";
import type { Address } from "abitype";
import { providers, utils } from "ethers";
import { getValidPublicRPCUrl } from "evm/utils/uri";
export type CoinbaseWalletConnectorOptions = WalletMobileSDKProviderOptions &
ConfigurationParams & {
@@ -227,7 +227,7 @@ export class CoinbaseWalletConnector extends WagmiConnector<
chainId: id,
chainName: chain.name,
nativeCurrency: chain.nativeCurrency,
rpcUrls: [getValidChainRPCs(chain)],
rpcUrls: getValidPublicRPCUrl(chain), // no client id on purpose here
blockExplorerUrls: this.getBlockExplorerUrls(chain),
},
],
@@ -2,7 +2,7 @@ export type WCMeta = {
name: string;
iconURL: string;
links: {
native?: string;
universal?: string;
native: string;
universal: string;
};
};
@@ -6,7 +6,7 @@ import {
import type { MagicConnector as MagicConnectorType } from "../connectors/magic/magic-connector";
import { MagicConnector } from "../connectors/magic/magic-connector";
export class MagicWallet extends AbstractClientWallet<
export class MagicLink extends AbstractClientWallet<
MagicLinkOptions,
MagicConnectorOptions
> {
@@ -68,3 +68,10 @@ export class MagicWallet extends AbstractClientWallet<
return super.disconnect();
}
}
/**
* @deprecated Use `MagicLink` instead
*
* Renamed for consistency with our React package
*/
export class MagicWallet extends MagicLink {}
@@ -10,22 +10,22 @@ import {
useWalletContext,
} from "@thirdweb-dev/react-core";
import { MagicLinkOptions } from "../connectors/magic/types";
import { MagicWallet } from "./MagicWallet";
import { MagicLink } from "./MagicLink";
import { useEffect, useRef } from "react";
import Box from "../../components/base/Box";
import { TextInput } from "../../components/base/TextInput";
import Text from "../../components/base/Text";
import { ActivityIndicator } from "react-native";
import { useMagicWallet } from "../../providers/context-provider";
import { useMagicLink } from "../../providers/context-provider";
export const magicWallet = (
export const magicLink = (
magicLinkOptions: MagicLinkOptions,
): WalletConfig<MagicWallet> => {
): WalletConfig<MagicLink> => {
return {
id: walletIds.magicLink,
meta: MagicWallet.meta,
meta: MagicLink.meta,
create: (options: WalletOptions) => {
return new MagicWallet({
return new MagicLink({
...options,
...magicLinkOptions,
chainId: options.chain.chainId,
@@ -42,7 +42,7 @@ export const magicWallet = (
/**
* UI for selecting wallet - this UI is rendered in the wallet selection screen
*/
selectUI: (props: SelectUIProps<MagicWallet>) => {
selectUI: (props: SelectUIProps<MagicLink>) => {
return (
<Box flex={1}>
<TextInput
@@ -71,23 +71,23 @@ export const magicWallet = (
};
const MagicConnectionUI: React.FC<
ConnectUIProps<MagicWallet> & { magicLinkOptions: MagicLinkOptions }
ConnectUIProps<MagicLink> & { magicLinkOptions: MagicLinkOptions }
> = ({ selectionData, walletConfig, close, magicLinkOptions }) => {
const createWalletInstance = useCreateWalletInstance();
const setConnectedWallet = useSetConnectedWallet();
const chainToConnect = useWalletContext().chainToConnect;
const setConnectionStatus = useSetConnectionStatus();
const { magicWallet: ctxMagicWallet, setMagicWallet } = useMagicWallet();
const { magicLink: ctxMagicLink, setMagicLink } = useMagicLink();
useEffect(() => {
const inst = createWalletInstance(walletConfig);
setMagicWallet?.(inst);
}, [createWalletInstance, setMagicWallet, walletConfig]);
setMagicLink?.(inst);
}, [createWalletInstance, setMagicLink, walletConfig]);
const connectPrompted = useRef(false);
useEffect(() => {
if (connectPrompted.current || !ctxMagicWallet) {
if (connectPrompted.current || !ctxMagicLink) {
return;
}
connectPrompted.current = true;
@@ -105,10 +105,10 @@ const MagicConnectionUI: React.FC<
: { phoneNumber: selectionData }),
};
close();
await ctxMagicWallet.connect(connectParams);
await ctxMagicWallet.getMagicSDK().user.getMetadata();
await ctxMagicLink.connect(connectParams);
await ctxMagicLink.getMagicSDK().user.getMetadata();
setConnectedWallet(ctxMagicWallet, connectParams);
setConnectedWallet(ctxMagicLink, connectParams);
setConnectionStatus("connected");
} catch (e) {
setConnectionStatus("disconnected");
@@ -119,7 +119,7 @@ const MagicConnectionUI: React.FC<
selectionData,
walletConfig,
close,
ctxMagicWallet,
ctxMagicLink,
setConnectedWallet,
setConnectionStatus,
chainToConnect?.chainId,
@@ -129,6 +129,14 @@ const MagicConnectionUI: React.FC<
return (
<Box minHeight={500}>
<ActivityIndicator size="small" />
<TextInput placeholder="Enter the OTP sent to your email / phone number" />
</Box>
);
};
/**
* @deprecated Use `magicLink()` instead
*
* Renamed for consistency with our React package
*/
export const magicWallet = magicLink;
@@ -1,9 +1,9 @@
import { walletIds } from "@thirdweb-dev/wallets";
import { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { WalletConnectV2 } from "./WalletConnectV2";
import { WCMeta } from "../types/wc";
import { WalletConnectBase } from "./wallet-connect/WalletConnectBase";
export class MetaMaskWallet extends WalletConnectV2 {
export class MetaMaskWallet extends WalletConnectBase {
static id = walletIds.metamask;
static meta = {
name: "MetaMask",
@@ -2,9 +2,9 @@ import {
WalletOptions as WalletOptionsRC,
WalletConfig,
} from "@thirdweb-dev/react-core";
import { WalletConnectV2 } from "./WalletConnectV2";
import { WalletConnectBase } from "./wallet-connect/WalletConnectBase";
export class RainbowWallet extends WalletConnectV2 {
export class RainbowWallet extends WalletConnectBase {
static id = "rainbow" as const;
static meta = {
name: "Rainbow",
@@ -1,8 +1,8 @@
import { WalletOptions, WalletConfig } from "@thirdweb-dev/react-core";
import { WalletConnectV2 } from "./WalletConnectV2";
import { WCMeta } from "../types/wc";
import { WalletConnectBase } from "./wallet-connect/WalletConnectBase";
export class TrustWallet extends WalletConnectV2 {
export class TrustWallet extends WalletConnectBase {
static id = "trust" as const;
static meta = {
name: "Trust Wallet",
@@ -31,5 +31,5 @@ export const trustWallet = (config?: TrustWalletConfig) => {
walletId: "trust",
projectId: config?.projectId,
}),
} satisfies WalletConfig<WalletConnectV2>;
} satisfies WalletConfig<WalletConnectBase>;
};
@@ -0,0 +1,48 @@
import { MMKV } from "react-native-mmkv";
import { WC2Options, WalletConnectBase } from "./WalletConnectBase";
const PREFIX = "__TW__WALLET__CONNECT__";
export class WalletConnect extends WalletConnectBase {
static id = "WalletConnect" as const;
static meta = {
name: "WalletConnect",
iconURL:
"ipfs://QmX58KPRaTC9JYZ7KriuBzeoEaV2P9eZcA3qbFnTHZazKw/wallet-connect.svg",
links: {
native: "wc:",
universal: "wc:",
},
};
storage: MMKV;
links: { native: string; universal: string };
constructor(options: WC2Options) {
super(options);
this.storage = new MMKV();
const storedLinksStr = this.storage.getString(PREFIX + "links");
this.links = storedLinksStr
? JSON.parse(storedLinksStr)
: {
native: "wc:",
universal: "wc:",
};
this.on("disconnect", () => {
this.storage.delete(PREFIX + "links");
});
}
getMeta() {
return { ...WalletConnect.meta, links: this.links };
}
setWCLinks(links: { native: string; universal: string }) {
this.storage.set(PREFIX + "links", JSON.stringify(links));
this.links = links;
}
}
@@ -1,13 +1,14 @@
import { TW_WC_PROJECT_ID } from "../../constants/walletConnect";
import { formatWalletConnectDisplayUri } from "../../utils/uri";
import type {
WalletConnectOptions,
WalletOptions,
} from "@thirdweb-dev/wallets";
import { WalletConnect as WalletConnectV2Wallets } from "@thirdweb-dev/wallets";
import { Linking } from "react-native";
import { WCMeta } from "../types/wc";
import { createAsyncLocalStorage } from "../../../core/AsyncStorage";
import { TW_WC_PROJECT_ID } from "../../../constants/walletConnect";
import { createAsyncLocalStorage } from "../../../../core/AsyncStorage";
import { WCMeta } from "../../types/wc";
import { formatWalletConnectDisplayUri } from "../../../utils/uri";
import AsyncStorage from "@react-native-async-storage/async-storage";
export type WC2Options = {
/**
@@ -24,7 +25,7 @@ export type WC2Options = {
walletId: NonNullable<WalletOptions["walletId"]>;
};
export abstract class WalletConnectV2 extends WalletConnectV2Wallets {
export abstract class WalletConnectBase extends WalletConnectV2Wallets {
constructor(options: WC2Options) {
super({
...options,
@@ -41,9 +42,21 @@ export abstract class WalletConnectV2 extends WalletConnectV2Wallets {
this.on("disconnect", () => {
this.removeListener("display_uri", this._onWCOpenWallet);
this.removeListener("wc_session_request_sent", this._onWCOpenWallet);
this.cleanAsyncStorage();
});
}
async cleanAsyncStorage() {
const allKeys = await AsyncStorage.getAllKeys();
for (const key of allKeys) {
if (key.includes("wc@2")) {
await AsyncStorage.removeItem(key);
}
}
await AsyncStorage.removeItem(".sdk-comm");
}
abstract getMeta(): WCMeta;
_onWCOpenWallet(uri?: string) {
@@ -60,3 +73,10 @@ export abstract class WalletConnectV2 extends WalletConnectV2Wallets {
}
}
}
/**
* @deprecated There's no longer the need to define V1/V2 for walletConnect.
*
* Use WalletConnectBase instead.
*/
export abstract class WalletConnectV2 extends WalletConnectBase {}
@@ -0,0 +1,82 @@
import { Image, StyleSheet } from "react-native";
import BaseButton from "../../../components/base/BaseButton";
import { WCWallet } from "../../../types/wc";
import Text from "../../../components/base/Text";
import { WCMeta } from "../../types/wc";
import { useState } from "react";
import WalletLoadingThumbnail from "./WalletLoadingThumbnail";
export const WALLET_MARGIN = 8;
export const WALLET_HEIGHT = 80;
export const WalletConnectButton = ({
item,
onPress,
}: {
item: WCWallet;
onPress: (item: WCMeta) => void;
}) => {
const [isAnimating, setIsAnimating] = useState(false);
const onWalletPress = () => {
setIsAnimating(true);
onPress(item);
};
return (
<BaseButton
flexDirection="column"
style={styles.buttonContainer}
onPress={onWalletPress}
>
{isAnimating ? (
<WalletLoadingThumbnail>
<Image
alt={item.name}
source={{ uri: item.iconURL }}
style={[
{
borderWidth: 1,
height: 50,
width: 50,
borderRadius: 30 / 3.5,
},
]}
/>
</WalletLoadingThumbnail>
) : (
<Image
alt={item.name}
source={{ uri: item.iconURL }}
style={[
{
borderWidth: 0,
height: 50,
width: 50,
borderRadius: 30 / 3.5,
},
]}
/>
)}
<Text variant="bodySmallSecondary" fontSize={12} numberOfLines={1}>
{item.name.split(" ")[0]}
</Text>
</BaseButton>
);
};
const styles = StyleSheet.create({
imageWrapper: {
width: 50,
height: 50,
overflow: "hidden",
position: "relative",
},
buttonContainer: {
flex: 1,
height: WALLET_HEIGHT,
justifyContent: "center",
alignItems: "center",
marginVertical: WALLET_MARGIN,
},
});
@@ -0,0 +1,264 @@
import { useEffect, useState } from "react";
import {
ActivityIndicator,
Dimensions,
FlatList,
Platform,
StyleSheet,
TextInput,
View,
} from "react-native";
import Box from "../../../components/base/Box";
import Text from "../../../components/base/Text";
import { useDebounceCallback } from "../../../hooks/useDebounceCallback";
import { TWModal } from "../../../components/base/modal/TWModal";
import {
ConnectUIProps,
useCreateWalletInstance,
useSetConnectedWallet,
useSetConnectionStatus,
} from "@thirdweb-dev/react-core";
import { WalletConnect } from "./WalletConnect";
import { useAppTheme } from "../../../styles/hooks";
import { ConnectWalletHeader } from "../../../components/ConnectWalletFlow/ConnectingWallet/ConnectingWalletHeader";
import { WalletConnectButton } from "./WalletConnectButton";
type WCWallet = {
iconURL: string;
name: string;
links: {
native: string;
universal: string;
};
};
const DEVICE_WIDTH = Dimensions.get("window").width;
const MODAL_HEIGHT = Dimensions.get("window").height * 0.5;
export function WalletConnectUI({
close,
walletConfig,
goBack,
projectId,
}: ConnectUIProps<WalletConnect> & { projectId: string }) {
const theme = useAppTheme();
const [wallets, setWallets] = useState<WCWallet[]>([]);
const [error, setError] = useState<string | undefined>();
const [loading, setLoading] = useState<boolean>(true);
const [search, setSearch] = useState<string>("");
const [searchWallets, setSearchWallets] = useState<WCWallet[]>([]);
const createWalletInstance = useCreateWalletInstance();
const setConnectedWallet = useSetConnectedWallet();
const setConnectionStatus = useSetConnectionStatus();
const onChangeText = useDebounceCallback({ callback: setSearch });
useEffect(() => {
const abortController = new AbortController();
const signal = abortController.signal;
async function fetchAsync() {
const platformOS =
Platform.OS.toLowerCase() === "ios" ? "iOS" : "Android";
let response;
try {
response = await fetch(
`https://explorer-api.walletconnect.com/w3m/v1/get${platformOS}Listings?projectId=${projectId}&chains=eip155:1`,
{ signal },
);
} catch (err) {
console.error("Failed to fetch wallets", err);
setError(`Failed to fetch wallets: ${err}`);
}
if (response?.ok) {
const data = await response.json();
if (data) {
const walletsB = [];
const listings: {
[key: string]: {
name: string;
image_id: string;
mobile: {
native: string;
universal: string;
};
};
} = data.listings;
for (const listing of Object.values(listings)) {
walletsB.push({
name: listing.name,
iconURL: `https://explorer-api.walletconnect.com/w3m/v1/getWalletImage/${listing.image_id}?projectId=${projectId}`,
links: listing.mobile
? listing.mobile
: {
native: "wc://",
universal: "wc://",
},
});
}
setWallets(walletsB);
setSearchWallets(walletsB);
setLoading(false);
}
}
}
fetchAsync();
return () => {
abortController.abort();
};
}, [projectId]);
useEffect(() => {
if (wallets && search) {
setSearchWallets(
wallets.filter((w) => {
return w.name.toLowerCase().includes(search.toLowerCase());
}),
);
} else if (search.length === 0) {
setSearchWallets(wallets);
}
}, [search, wallets]);
const onChooseWalletItem = (walletMeta: WCWallet) => {
const wcWallet = createWalletInstance(walletConfig);
wcWallet.setWCLinks(walletMeta.links);
setConnectionStatus("connecting");
wcWallet
.connect()
.then(() => {
setConnectedWallet(wcWallet);
})
.catch((e) => {
setConnectionStatus("disconnected");
console.error(`Error connecting with WalletConnect: ${e}`);
})
.finally(() => {
close();
});
};
const onClosePress = () => {
setConnectionStatus("disconnected");
close();
};
return (
<TWModal
isVisible={true}
onBackdropPress={onClosePress}
backdropOpacity={0.7}
>
<View
style={[styles.modal, { backgroundColor: theme.colors.background }]}
>
<ConnectWalletHeader
subHeaderText={""}
onBackPress={goBack}
onClose={onClosePress}
/>
<View style={styles.explorerContainer}>
{loading ? (
<ActivityIndicator size={"small"} />
) : (
<FlatList
data={searchWallets}
contentContainerStyle={styles.listContentContainer}
showsVerticalScrollIndicator={false}
numColumns={4}
fadingEdgeLength={20}
stickyHeaderIndices={[0]}
ListHeaderComponent={
<Box
borderColor="border"
borderWidth={0.5}
backgroundColor="background"
flex={1}
flexDirection="row"
borderRadius="md"
alignItems="center"
mb="sm"
padding="xs"
width={"100%"}
>
<TextInput
onChangeText={onChangeText}
placeholder="Search Wallets"
placeholderTextColor={theme.colors.textSecondary}
style={{
...styles.textInput,
color: theme.colors.textSecondary,
}}
/>
</Box>
}
ListEmptyComponent={
error ? (
<Text variant="error">{error}</Text>
) : (
<View
style={[
styles.emptyContainer,
{ height: Math.round(MODAL_HEIGHT) },
]}
>
<Text variant="bodySmall">No results found</Text>
</View>
)
}
renderItem={({ item }) => (
<WalletConnectButton item={item} onPress={onChooseWalletItem} />
)}
/>
)}
</View>
</View>
</TWModal>
);
}
const styles = StyleSheet.create({
modal: {
position: "absolute",
bottom: -20,
left: -20,
width: DEVICE_WIDTH,
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
paddingHorizontal: 16,
paddingTop: 24,
paddingBottom: 12,
},
listContentContainer: {
paddingBottom: 12,
flexGrow: 1,
},
emptyContainer: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
textInput: {
margin: 0,
fontSize: 16,
fontWeight: "500",
padding: 2,
flex: 1,
},
explorerContainer: {
display: "flex",
height: MODAL_HEIGHT,
width: "100%",
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "center",
alignItems: "center",
marginTop: 25,
},
});
@@ -0,0 +1,87 @@
import { useEffect, useRef } from "react";
import { Animated, Easing, StyleSheet, View } from "react-native";
import Svg, { Rect } from "react-native-svg";
import { useAppTheme } from "../../../styles/hooks";
const AnimatedRect = Animated.createAnimatedComponent(Rect);
interface Props {
children?: React.ReactNode;
imageSize?: number;
showError?: boolean;
}
function WalletLoadingThumbnail({ children, showError }: Props) {
const Theme = useAppTheme();
const spinValue = useRef(new Animated.Value(0));
useEffect(() => {
const animation = Animated.timing(spinValue.current, {
toValue: 1,
duration: 1150,
useNativeDriver: true,
easing: Easing.linear,
});
const loop = Animated.loop(animation);
loop.start();
return () => {
loop.stop();
};
}, [spinValue]);
const spin = spinValue.current.interpolate({
inputRange: [0, 1],
outputRange: [0, -371],
});
return (
<View style={styles.container}>
<Svg width={60} height={60} viewBox="0 0 60 60" style={styles.loader}>
<AnimatedRect
x="2"
y="2"
width={56}
height={56}
rx={15}
stroke={showError ? "transparent" : Theme.colors.linkPrimary}
strokeWidth={2}
fill="transparent"
strokeDasharray={"116 255"}
strokeDashoffset={spin}
/>
</Svg>
{showError && (
<View
style={[
styles.error,
{
borderColor: Theme.colors.border,
},
]}
/>
)}
{children}
</View>
);
}
const styles = StyleSheet.create({
container: {
alignItems: "center",
justifyContent: "center",
},
loader: {
position: "absolute",
},
error: {
position: "absolute",
borderWidth: 2,
height: 56,
width: 56,
borderRadius: 30 / 3.5,
},
});
export default WalletLoadingThumbnail;
@@ -0,0 +1,28 @@
import { WalletConfig, WalletOptions } from "@thirdweb-dev/react-core";
import { WalletConnect } from "./WalletConnect";
import { WalletConnectUI } from "./WalletConnectUI";
import { TW_WC_PROJECT_ID } from "../../../constants/walletConnect";
import { walletIds } from "@thirdweb-dev/wallets";
export type WalletConnectConfig = {
projectId?: string;
};
export const walletConnect = (
config?: WalletConnectConfig,
): WalletConfig<WalletConnect> => {
const projectId = config?.projectId || TW_WC_PROJECT_ID;
return {
id: WalletConnect.id,
meta: WalletConnect.meta,
create: (options: WalletOptions) =>
new WalletConnect({
...options,
walletId: walletIds.walletConnect,
projectId,
}),
connectUI(props) {
return <WalletConnectUI {...props} projectId={projectId} />;
},
};
};
+47
View File
@@ -1,5 +1,52 @@
# @thirdweb-dev/react
## 3.14.34
### Patch Changes
- Updated dependencies [[`f97ddf4c`](https://github.com/thirdweb-dev/js/commit/f97ddf4c7f14854f3b204ad9741b52ddb8dac736)]:
- @thirdweb-dev/chains@0.1.45
- @thirdweb-dev/react-core@3.14.34
- @thirdweb-dev/sdk@3.10.53
- @thirdweb-dev/wallets@1.1.17
## 3.14.33
### Patch Changes
- Updated dependencies [[`e1962641`](https://github.com/thirdweb-dev/js/commit/e19626417218767a0e44c00f440761d7b86d02eb), [`bd1fcbae`](https://github.com/thirdweb-dev/js/commit/bd1fcbae327e788124f5635673511f0b72e9d7ab)]:
- @thirdweb-dev/sdk@3.10.52
- @thirdweb-dev/react-core@3.14.33
- @thirdweb-dev/wallets@1.1.16
## 3.14.32
### Patch Changes
- Updated dependencies [[`07fb1b5f`](https://github.com/thirdweb-dev/js/commit/07fb1b5ffa4c170e252df31070852ddb9a81dec9), [`300a3c6f`](https://github.com/thirdweb-dev/js/commit/300a3c6f04d0ea7e25dfdb0a4c28b3a5796fcadf), [`d248aa2c`](https://github.com/thirdweb-dev/js/commit/d248aa2c5a89a297dd2623c961793026de1de346), [`8f3b685a`](https://github.com/thirdweb-dev/js/commit/8f3b685ad2bd73cb4d5d8c8aa25c04ffc10fb7cf), [`28975765`](https://github.com/thirdweb-dev/js/commit/2897576513eb6f497a9f92e3e473182b4fc9681b), [`b91d3e99`](https://github.com/thirdweb-dev/js/commit/b91d3e990198b77dc1358e738c11dc4acaa67491)]:
- @thirdweb-dev/react-core@3.14.32
- @thirdweb-dev/wallets@1.1.15
- @thirdweb-dev/sdk@3.10.51
- @thirdweb-dev/chains@0.1.44
## 3.14.31
### Patch Changes
- Updated dependencies [[`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74), [`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117), [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74)]:
- @thirdweb-dev/wallets@1.1.14
- @thirdweb-dev/sdk@3.10.50
- @thirdweb-dev/react-core@3.14.31
## 3.14.30
### Patch Changes
- Updated dependencies [[`34b31599`](https://github.com/thirdweb-dev/js/commit/34b315993c0abaccb9640a5d5804c2c93af569c2), [`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696)]:
- @thirdweb-dev/wallets@1.1.13
- @thirdweb-dev/sdk@3.10.49
- @thirdweb-dev/react-core@3.14.30
## 3.14.29
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "3.14.29",
"version": "3.14.34",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+43
View File
@@ -1,5 +1,48 @@
# @thirdweb-dev/sdk
## 3.10.53
### Patch Changes
- Updated dependencies [[`f97ddf4c`](https://github.com/thirdweb-dev/js/commit/f97ddf4c7f14854f3b204ad9741b52ddb8dac736)]:
- @thirdweb-dev/chains@0.1.45
## 3.10.52
### Patch Changes
- [#1503](https://github.com/thirdweb-dev/js/pull/1503) [`e1962641`](https://github.com/thirdweb-dev/js/commit/e19626417218767a0e44c00f440761d7b86d02eb) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Pass custom storage instances to internal SDKs
- [#1505](https://github.com/thirdweb-dev/js/pull/1505) [`bd1fcbae`](https://github.com/thirdweb-dev/js/commit/bd1fcbae327e788124f5635673511f0b72e9d7ab) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose Transaction.populateTransaction()
## 3.10.51
### Patch Changes
- [#1500](https://github.com/thirdweb-dev/js/pull/1500) [`d248aa2c`](https://github.com/thirdweb-dev/js/commit/d248aa2c5a89a297dd2623c961793026de1de346) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Pass sdk options to fetch contract metadata where possible
- [#1495](https://github.com/thirdweb-dev/js/pull/1495) [`28975765`](https://github.com/thirdweb-dev/js/commit/2897576513eb6f497a9f92e3e473182b4fc9681b) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Custom gas price for Create2 factory deployment on Cronos testnet
- [#1490](https://github.com/thirdweb-dev/js/pull/1490) [`b91d3e99`](https://github.com/thirdweb-dev/js/commit/b91d3e990198b77dc1358e738c11dc4acaa67491) Thanks [@kien-ngo](https://github.com/kien-ngo)! - Small fix for Promise.all()"
- Updated dependencies [[`d248aa2c`](https://github.com/thirdweb-dev/js/commit/d248aa2c5a89a297dd2623c961793026de1de346), [`8f3b685a`](https://github.com/thirdweb-dev/js/commit/8f3b685ad2bd73cb4d5d8c8aa25c04ffc10fb7cf)]:
- @thirdweb-dev/storage@1.2.6
- @thirdweb-dev/chains@0.1.44
## 3.10.50
### Patch Changes
- [#1480](https://github.com/thirdweb-dev/js/pull/1480) [`a5ba9e4f`](https://github.com/thirdweb-dev/js/commit/a5ba9e4fbfee228e5b9ac27cd0157187e3a50117) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Separate Account and AccountPermissions extensions
- [#1484](https://github.com/thirdweb-dev/js/pull/1484) [`39e2af6f`](https://github.com/thirdweb-dev/js/commit/39e2af6f2f4d933dca1b3de4a37de76375bafd74) Thanks [@joaquim-verges](https://github.com/joaquim-verges)! - Expose getPolygonGasPriorityFee()
## 3.10.49
### Patch Changes
- [#1468](https://github.com/thirdweb-dev/js/pull/1468) [`7f531122`](https://github.com/thirdweb-dev/js/commit/7f5311222b14da04877df056baae36409dff4696) Thanks [@kumaryash90](https://github.com/kumaryash90)! - Enable dynamic contract deployment with any evm
## 3.10.48
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/sdk",
"version": "3.10.48",
"version": "3.10.53",
"description": "The main thirdweb SDK.",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
"license": "Apache-2.0",
@@ -49,6 +49,10 @@ export const CUSTOM_GAS_FOR_CHAIN: Record<number, CustomChain> = {
name: "Canto Testnet",
gasPrice: 1000 * 10 ** 9,
},
[338]: {
name: "Cronos Testnet",
gasPrice: 2000 * 10 ** 9,
},
};
/* eslint-enable no-useless-computed-key */
@@ -9,8 +9,13 @@ import { fetchAndCacheDeployMetadata } from "./fetchAndCacheDeployMetadata";
import { getCreate2FactoryAddress } from "./getCreate2FactoryAddress";
import { caches } from "./caches";
import { computeDeploymentInfo } from "./computeDeploymentInfo";
import { generatePluginFunctions } from "../plugin/generatePluginFunctions";
import {
generateExtensionFunctions,
generatePluginFunctions,
} from "../plugin/generatePluginFunctions";
import { getMetadataForPlugins } from "../plugin/getMetadataForPlugins";
import { getMetadataForExtensions } from "../plugin/getMetadataForExtensions";
import { Extension } from "../../types/extensions";
/**
*
* Returns txn data for keyless deploys as well as signer deploys.
@@ -42,6 +47,10 @@ export async function getDeploymentInfo(
storage,
);
const pluginMetadata = await getMetadataForPlugins(metadataUri, storage);
const extensionMetadata = await getMetadataForExtensions(
metadataUri,
storage,
);
// if pluginMetadata is not empty, then it's a plugin-pattern router contract
if (pluginMetadata.length > 0) {
@@ -87,6 +96,42 @@ export async function getDeploymentInfo(
};
finalDeploymentInfo.push(...pluginDeploymentInfo, pluginMapTransaction);
} else if (extensionMetadata.length > 0) {
// get deployment info for all extensions
const extensionDeploymentInfo = await Promise.all(
extensionMetadata.map(async (metadata) => {
const info = await computeDeploymentInfo(
"extension",
provider,
storage,
create2FactoryAddress,
{ metadata: metadata },
);
return info;
}),
);
// create constructor param input for BaseRouter
const routerInput: Extension[] = [];
extensionMetadata.forEach((metadata, index) => {
const extensionFunctions = generateExtensionFunctions(metadata.abi);
routerInput.push({
metadata: {
name: metadata.name,
metadataURI: "",
implementation:
extensionDeploymentInfo[index].transaction.predictedAddress,
},
functions: extensionFunctions,
});
});
// routerInput as constructor param for BaseRouter
customParams["_extensions"] = {
value: routerInput,
};
finalDeploymentInfo.push(...extensionDeploymentInfo);
}
const implementationDeployInfo = await computeDeploymentInfo(
+1
View File
@@ -2,6 +2,7 @@ export * from "./error";
export * from "./snapshots";
export * from "./role";
export * from "./metadata-resolver";
export * from "./gas-price";
// feature detection
export * from "./feature-detection/matchesPrebuiltAbi";
@@ -8,6 +8,7 @@ import TWRegistryABI from "@thirdweb-dev/contracts-js/dist/abis/TWMultichainRegi
import { getMultichainRegistryAddress } from "../constants/addresses/getMultichainRegistryAddress";
import { getChainProvider } from "../constants/urls";
import type { TWMultichainRegistryLogic } from "@thirdweb-dev/contracts-js";
import { SDKOptions } from "../schema";
// Internal static cache
const metadataCache: Record<string, PublishedMetadata> = {};
@@ -39,6 +40,7 @@ export async function fetchContractMetadataFromAddress(
address: Address,
provider: providers.Provider,
storage: ThirdwebStorage,
sdkOptions: SDKOptions = {},
) {
const chainId = (await provider.getNetwork()).chainId;
const cached = getFromCache(address, chainId);
@@ -57,16 +59,17 @@ export async function fetchContractMetadataFromAddress(
metadata = await fetchContractMetadata(compilerMetadataUri, storage);
} catch (e) {
console.debug(
"Failed to get Contract Metadata from IPFS, defaulting to onchain registry",
"Contract Metadata not found on IPFS, defaulting to onchain registry. Original error:",
(e as any)?.message,
);
try {
// try from multichain registry
if (!multichainRegistry) {
// TODO enforce always passing sdk options for clientId/secretKey
multichainRegistry = new Contract(
getMultichainRegistryAddress(),
TWRegistryABI,
getChainProvider("polygon", {}),
getChainProvider("polygon", sdkOptions),
) as TWMultichainRegistryLogic;
}
@@ -1,13 +1,18 @@
import { Abi } from "../../schema/contracts/custom";
import { utils } from "ethers";
import { Plugin } from "../../types/plugins";
import { ExtensionFunction } from "../../types/extensions";
function getFunctionSignature(fnInputs: any): string {
return (
"(" +
fnInputs
.map((i: any) => {
return i.type === "tuple" ? getFunctionSignature(i.components) : i.type;
return i.type === "tuple"
? getFunctionSignature(i.components)
: i.type === "tuple[]"
? getFunctionSignature(i.components) + `[]`
: i.type;
})
.join(",") +
")"
@@ -34,3 +39,25 @@ export function generatePluginFunctions(
}
return pluginFunctions;
}
export function generateExtensionFunctions(
extensionAbi: any,
): ExtensionFunction[] {
const extensionInterface = new utils.Interface(extensionAbi);
const extensionFunctions: ExtensionFunction[] = [];
// TODO - filter out common functions like _msgSender(), contractType(), etc.
for (const fnFragment of Object.values(extensionInterface.functions)) {
const fn = extensionInterface.getFunction(
extensionInterface.getSighash(fnFragment),
);
if (fn.name.startsWith("_")) {
continue;
}
extensionFunctions.push({
functionSelector: extensionInterface.getSighash(fn),
functionSignature: fn.name + getFunctionSignature(fn.inputs),
});
}
return extensionFunctions;
}
@@ -0,0 +1,74 @@
import { getAllExtensionsAbi } from "../../constants/thirdweb-features";
import { ContractWrapper } from "../../core/classes/contract-wrapper";
import {
AbiSchema,
PreDeployMetadataFetched,
} from "../../schema/contracts/custom";
import { isFeatureEnabled } from "../feature-detection/isFeatureEnabled";
import { ThirdwebStorage } from "@thirdweb-dev/storage";
import { getChainProvider } from "../../constants/urls";
import { fetchAndCachePublishedContractURI } from "../any-evm-utils/fetchAndCachePublishedContractURI";
import { fetchAndCacheDeployMetadata } from "../any-evm-utils/fetchAndCacheDeployMetadata";
import { Extension } from "../../types/extensions";
export async function getMetadataForExtensions(
publishedMetadataUri: string,
storage: ThirdwebStorage,
): Promise<PreDeployMetadataFetched[]> {
let extensionMetadata: PreDeployMetadataFetched[] = [];
const { compilerMetadata, extendedMetadata } =
await fetchAndCacheDeployMetadata(publishedMetadataUri, storage);
// check if contract is extension-pattern
const isExtensionRouter: boolean = isFeatureEnabled(
AbiSchema.parse(compilerMetadata.abi),
"ExtensionRouter",
);
if (isExtensionRouter) {
if (
extendedMetadata &&
extendedMetadata.factoryDeploymentData?.implementationAddresses
) {
const implementationsAddresses = Object.entries(
extendedMetadata.factoryDeploymentData.implementationAddresses,
);
try {
const entry = implementationsAddresses.find(
([, implementation]) => implementation !== "",
);
const [network, implementation] = entry ? entry : [];
if (network && implementation) {
const provider = getChainProvider(parseInt(network), {});
const contract = new ContractWrapper(
provider,
implementation,
getAllExtensionsAbi,
{},
storage,
);
const extensions = await contract.call("getAllExtensions");
const extensionNames: string[] = Array.from(
new Set(extensions.map((item: Extension) => item.metadata.name)),
);
const extensionUris = await Promise.all(
extensionNames.map((name) => {
return fetchAndCachePublishedContractURI(name);
}),
);
extensionMetadata = (
await Promise.all(
extensionUris.map(async (uri) => {
return fetchAndCacheDeployMetadata(uri, storage);
}),
)
).map((fetchedMetadata) => fetchedMetadata.compilerMetadata);
}
} catch {}
}
}
return extensionMetadata;
}
@@ -67,6 +67,7 @@ import {
FEATURE_EXTENSION_ROUTER,
FEATURE_ACCOUNT,
FEATURE_ACCOUNT_FACTORY,
FEATURE_ACCOUNT_PERMISSIONS,
} from "./thirdweb-features";
/**
@@ -134,6 +135,7 @@ export type Feature =
| typeof FEATURE_ENGLISH_AUCTIONS
| typeof FEATURE_OFFERS
| typeof FEATURE_ACCOUNT
| typeof FEATURE_ACCOUNT_PERMISSIONS
| typeof FEATURE_ACCOUNT_FACTORY;
export type Extension = Feature;
@@ -12,7 +12,8 @@ import IThirdwebPrimarySaleAbi from "@thirdweb-dev/contracts-js/dist/abis/IPrima
import IThirdwebRoyaltyAbi from "@thirdweb-dev/contracts-js/dist/abis/IRoyalty.json";
import IOwnableAbi from "@thirdweb-dev/contracts-js/dist/abis/Ownable.json";
import IAccountFactory from "@thirdweb-dev/contracts-js/dist/abis/IAccountFactory.json";
import IAccountCore from "@thirdweb-dev/contracts-js/dist/abis/IAccountCore.json";
import IAccountPermissions from "@thirdweb-dev/contracts-js/dist/abis/IAccountPermissions.json";
import IAccount from "@thirdweb-dev/contracts-js/dist/abis/IAccount.json";
export const getAllPluginsAbi = [
{
@@ -291,6 +292,18 @@ export const FEATURE_ACCOUNT_FACTORY = {
features: {},
} as const;
export const FEATURE_ACCOUNT_PERMISSIONS = {
name: "AccountPermissions",
namespace: "accountPermissions",
docLinks: {
// TODO
sdk: "sdk.account",
contracts: "",
},
abis: [IAccountPermissions],
features: {},
} as const;
export const FEATURE_ACCOUNT = {
name: "Account",
namespace: "account",
@@ -299,6 +312,6 @@ export const FEATURE_ACCOUNT = {
sdk: "sdk.account",
contracts: "",
},
abis: [IAccountCore],
features: {},
abis: [IAccount],
features: { [FEATURE_ACCOUNT_PERMISSIONS.name]: FEATURE_ACCOUNT_PERMISSIONS },
} as const;
+1 -1
View File
@@ -67,7 +67,7 @@ export const EditionDropInitializer = {
) => {
const [, provider] = getSignerAndProvider(network, options);
const [abi, contract, _network] = await Promise.all([
await EditionDropInitializer.getAbi(address, provider, storage),
EditionDropInitializer.getAbi(address, provider, storage),
import("./prebuilt-implementations/edition-drop"),
provider.getNetwork(),
]);
@@ -0,0 +1,727 @@
import { DetectableFeature } from "../interfaces/DetectableFeature";
import { ContractWrapper } from "./contract-wrapper";
import { FEATURE_ACCOUNT_PERMISSIONS } from "../../constants/thirdweb-features";
import { utils, BigNumber } from "ethers";
import { Transaction } from "./transactions";
import type {
IAccountCore,
IAccountPermissions,
} from "@thirdweb-dev/contracts-js";
import {
SignedSignerPermissionsPayload,
SignerPermissionsOutput,
SignerWithPermissions,
SignerPermissions,
SignerPermissionsInput,
SignerPermissionRequest,
SignerPermissionsSchema,
PermissionSnapshotInput,
PermissionSnapshotSchema,
PermissionSnapshotOutput,
} from "../../types";
import invariant from "tiny-invariant";
import { buildTransactionFunction } from "../../common/transactions";
import { resolveOrGenerateId } from "../../common/signature-minting";
import { AddressOrEns } from "../../schema";
import { resolveAddress } from "../../common";
export class AccountPermissions implements DetectableFeature {
featureName = FEATURE_ACCOUNT_PERMISSIONS.name;
private contractWrapper: ContractWrapper<IAccountCore>;
constructor(contractWrapper: ContractWrapper<IAccountCore>) {
this.contractWrapper = contractWrapper;
}
getAddress(): string {
return this.contractWrapper.readContract.address;
}
/*********************************
* HELPER FUNCTIONS
********************************/
private hasDuplicateSigners(snapshot: PermissionSnapshotOutput): boolean {
const checkedSigner: Record<string, boolean> = {};
const signers = snapshot.map((item) => item.signer);
for (const signer of signers) {
if (!checkedSigner[signer]) {
checkedSigner[signer] = true;
} else {
return true;
}
}
return false;
}
/**
* Format the access restrictions for a given role
*
* @param restrictions - The access restrictions for a given role
* @returns formatted role restrictions
*
*/
private parseSignerPermissionsStruct(
permissions: IAccountPermissions.SignerPermissionsStruct,
): SignerPermissions {
return {
startDate: new Date(
parseInt(permissions.startTimestamp.toString()) * 1000,
),
expirationDate: new Date(
parseInt(permissions.endTimestamp.toString()) * 1000,
),
nativeTokenLimitPerTransaction: BigNumber.from(
permissions.nativeTokenLimitPerTransaction,
),
approvedCallTargets: permissions.approvedTargets,
};
}
private async sendSignerPermissionRequest(
signerAddress: string,
permissions: SignerPermissionsOutput,
): Promise<Transaction> {
const { payload, signature } = await this.generatePayload(
signerAddress,
permissions,
);
const { success } =
await this.contractWrapper.readContract.verifySignerPermissionRequest(
payload,
signature,
);
if (!success) {
throw new Error(`Invalid signature.`);
}
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "setPermissionsForSigner",
args: [payload, signature],
});
}
private async buildSignerPermissionRequest(
signerAddress: string,
permissions: SignerPermissionsOutput,
): Promise<string> {
const { payload, signature } = await this.generatePayload(
signerAddress,
permissions,
);
const isValidSigner =
await this.contractWrapper.readContract.verifySignerPermissionRequest(
payload,
signature,
);
if (!isValidSigner) {
throw new Error(`Invalid signature.`);
}
return this.contractWrapper.writeContract.interface.encodeFunctionData(
"setPermissionsForSigner",
[payload, signature],
);
}
/**
* Generate and sign a payload to grant or revoke a signer's access to the account.
*
* @param signer - The address of the signer
* @param roleAction - The address of the signer
* @returns The generated payload and signature produced on signing that payload.
*
*/
private async generatePayload(
signerAddress: string,
permissions: SignerPermissionsOutput,
): Promise<SignedSignerPermissionsPayload> {
// Get payload struct.
const payload: IAccountPermissions.SignerPermissionRequestStruct = {
signer: signerAddress,
approvedTargets: permissions.approvedCallTargets,
nativeTokenLimitPerTransaction: utils.parseEther(
permissions.nativeTokenLimitPerTransaction,
),
permissionStartTimestamp: permissions.startDate,
permissionEndTimestamp: permissions.expirationDate,
reqValidityStartTimestamp: 0,
// Req validity ends 10 years from now.
reqValidityEndTimestamp: BigNumber.from(
Math.floor(
new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10).getTime() /
1000,
),
),
uid: resolveOrGenerateId(undefined),
};
// Generate signature
const chainId = await this.contractWrapper.getChainID();
const connectedSigner = this.contractWrapper.getSigner();
invariant(connectedSigner, "No signer available");
const signature = await this.contractWrapper.signTypedData(
connectedSigner,
{
name: "Account",
version: "1",
chainId,
verifyingContract: this.getAddress(),
},
{ SignerPermissionRequest },
payload,
);
return { payload, signature };
}
/*********************************
* READ FUNCTIONS
********************************/
/**
* Get whether a signer is an admin on the account.
*
* @example
* ```javascript
* const isAdmin = await contract.account.isAdmin(signer);
* ```
* @param signer - The address of a signer of the account.
* @returns whether a signer is an admin on the account.
*
* @twfeature AccountPermissions
*/
public async isAdmin(signerAddress: AddressOrEns): Promise<boolean> {
const resolvedSignerAddress = await resolveAddress(signerAddress);
return await this.contractWrapper.readContract.isAdmin(
resolvedSignerAddress,
);
}
/**
* Get whether a signer has permissions to use the account.
*
* @example
* ```javascript
* const isAdmin = await contract.account.isSigner(signer);
* ```
* @param signer - The address of a signer of the account.
* @returns whether a signer has permissions to use the account.
*
* @twfeature AccountPermissions
*/
public async isSigner(signerAddress: AddressOrEns): Promise<boolean> {
const resolvedSignerAddress = await resolveAddress(signerAddress);
return await this.contractWrapper.readContract.isActiveSigner(
resolvedSignerAddress,
);
}
/**
* Get all admins of the account.
*
* @example
* ```javascript
* const allAdmins = await contract.account.getAllAdmins();
* ```
*
* @returns all admins of the account.
*
* @twfeature AccountPermissions
*/
public async getAllAdmins(): Promise<string[]> {
return await this.contractWrapper.readContract.getAllAdmins();
}
/**
* Get all (non-admin) signers with permissions to use the account.
*
* @example
* ```javascript
* const allSigners = await contract.account.getAllSigners();
* ```
*
* @returns all (non-admin) signers with permissions to use the account.
*
* @twfeature AccountPermissions
*/
public async getAllSigners(): Promise<SignerWithPermissions[]> {
const activeSignersWithPerms: IAccountPermissions.SignerPermissionsStruct[] =
await this.contractWrapper.readContract.getAllActiveSigners();
return await Promise.all(
activeSignersWithPerms.map(async (signerWithPermissions) => {
const signer = signerWithPermissions.signer;
const permissions = this.parseSignerPermissionsStruct(
signerWithPermissions,
);
return { signer, permissions };
}),
);
}
/**
* Get all admins and non-admin signers with permissions to use the account.
*
* @example
* ```javascript
* const allAdminsAndSigners = await contract.account.getAllAdminsAndSigners();
* ```
*
* @returns all admins and non-admin signers with permissions to use the account.
*
* @twfeature AccountPermissions
*/
public async getAllAdminsAndSigners(): Promise<SignerWithPermissions[]> {
const allAdmins = await this.getAllAdmins();
const transformedAdmins: SignerWithPermissions[] = allAdmins.map(
(admin) => {
return {
isAdmin: true,
signer: admin,
permissions: {
startDate: new Date(0),
expirationDate: new Date(0),
nativeTokenLimitPerTransaction: BigNumber.from(0),
approvedCallTargets: [],
},
};
},
);
const allSigners = await this.getAllSigners();
return [...transformedAdmins, ...allSigners];
}
/*********************************
* WRITE FUNCTIONS
********************************/
/**
* Grant an address admin access to the account.
*
* @remarks Grants an address admin access to the account. The admin will have complete authority over the account.
*
* @param signer - The address to be granted admin access to the account.
*
* @example
* ```javascript
* const tx = await contract.account.grantAdminAccess(signer);
* const receipt = tx.receipt();
* ```
*
* @twfeature AccountPermissions
*/
grantAdminPermissions = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "setAdmin",
args: [resolvedSignerAddress, true],
});
},
);
/**
* Revoke an address' admin access to the account.
*
* @remarks Revokes an address' admin access to the account.
*
* @param signer - The address of an admin of the account.
*
* @example
* ```javascript
* const tx = await contract.account.revokeAdminAccess(signer);
* const receipt = tx.receipt();
* ```
*
* @twfeature AccountPermissions
*/
revokeAdminPermissions = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "setAdmin",
args: [resolvedSignerAddress, false],
});
},
);
/**
* Grant a signer permissions to use the account.
*
* @remarks Grants a signer permissions to use the account.
*
* @param signer - The signer to be granted permissions to use the account.
* @param permissions - The permissions to be applied to the signer's use of the account.
*
* @example
* ```javascript
* const tx = await contract.account.grantPermissions(signer, permissions);
* const receipt = tx.receipt();
* ```
*
* @twfeature AccountPermissions
*/
grantPermissions = /* @__PURE__ */ buildTransactionFunction(
async (
signerAddress: AddressOrEns,
permissions: SignerPermissionsInput,
): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
const resolvedPermissions = await SignerPermissionsSchema.parseAsync(
permissions,
);
if (await this.isAdmin(resolvedSignerAddress)) {
throw new Error(
"Signer is already an admin. Cannot grant permissions to an existing admin.",
);
}
if (await this.isSigner(resolvedSignerAddress)) {
throw new Error(
"Signer already has permissions. Cannot grant permissions to an existing signer. You can update permissions using `updatePermissions`.",
);
}
return await this.sendSignerPermissionRequest(
resolvedSignerAddress,
resolvedPermissions,
);
},
);
/**
* Update the permissions of a signer for using the account.
*
* @remarks Updates the permissions of a signer for using the account.
*
* @param signer - The signer whose permissions to use the account are to be updated.
* @param permissions - The permissions to be applied to the signer's use of the account.
*
* @example
* ```javascript
* const tx = await contract.account.updateAccess(signer, restrictions);
* const receipt = tx.receipt();
* ```
*
* @twfeature AccountPermissions
*/
updatePermissions = /* @__PURE__ */ buildTransactionFunction(
async (
signerAddress: AddressOrEns,
permissions: SignerPermissionsInput,
): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
const resolvedPermissions = await SignerPermissionsSchema.parseAsync(
permissions,
);
if (await this.isAdmin(resolvedSignerAddress)) {
throw new Error(
"Signer is already an admin. Cannot update permissions of an existing admin.",
);
}
if (!(await this.isSigner(resolvedSignerAddress))) {
throw new Error(
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
);
}
return await this.sendSignerPermissionRequest(
resolvedSignerAddress,
resolvedPermissions,
);
},
);
/**
* Revoke a scoped access address to the account
*
* @remarks Revokes an address' access to the account.
*
* @param signer - The address whose access to the account is to be revoked.
*
* @example
* ```javascript
* const tx = await contract.account.revokeAccess(signer);
* const receipt = tx.receipt();
* ```
*
* @twfeature AccountPermissions
*/
revokeAccess = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
if (await this.isAdmin(resolvedSignerAddress)) {
throw new Error(
"Signer is already an admin. Cannot revoke permissions of an admin.",
);
}
if (!(await this.isSigner(resolvedSignerAddress))) {
throw new Error(
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
);
}
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
startDate: BigNumber.from(0),
expirationDate: BigNumber.from(0),
approvedCallTargets: [],
nativeTokenLimitPerTransaction: "0",
});
},
);
/**
* Approve an address as a call target for a given signer on the account
*
* @remarks Approves an address as a call target for a given signer on the account.
*
* @param signer - A signer with restricted access to the account.
* @param target - The address to approve as a call target for the signer.
*
* @example
* ```javascript
* const tx = await contract.account.approveTargetForSigner(signer, target);
* const receipt = tx.receipt();
* ```
*
* @twfeature AccountPermissions
*/
approveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
async (
signerAddress: AddressOrEns,
target: AddressOrEns,
): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
const resolvedTarget = await resolveAddress(target);
if (await this.isAdmin(resolvedSignerAddress)) {
throw new Error(
"Signer is already an admin. Cannot approve targets for an admin.",
);
}
if (!(await this.isSigner(resolvedSignerAddress))) {
throw new Error(
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
);
}
const permissions: IAccountPermissions.SignerPermissionsStruct =
await this.contractWrapper.readContract.getPermissionsForSigner(
resolvedSignerAddress,
);
if (permissions.approvedTargets.includes(target)) {
throw new Error("Target is already approved");
}
const newTargets = [...permissions.approvedTargets, resolvedTarget];
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
startDate: BigNumber.from(permissions.startTimestamp),
expirationDate: BigNumber.from(permissions.endTimestamp),
approvedCallTargets: newTargets,
nativeTokenLimitPerTransaction:
permissions.nativeTokenLimitPerTransaction.toString(),
});
},
);
/**
* Disapprove an address as a call target for a given signer on the account
*
* @remarks Disapprove an address as a call target for a given signer on the account.
*
* @param signer - A signer with restricted access to the account.
* @param target - The address to disapprove as a call target for the signer.
*
* @example
* ```javascript
* const tx = await contract.account.disapproveTargetForSigner(signer, target);
* const receipt = tx.receipt();
* ```
*
* @twfeature AccountPermissions
*/
disapproveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
async (
signerAddress: AddressOrEns,
target: AddressOrEns,
): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
const resolvedTarget = await resolveAddress(target);
if (await this.isAdmin(resolvedSignerAddress)) {
throw new Error(
"Signer is already an admin. Cannot approve targets for an admin.",
);
}
if (!(await this.isSigner(resolvedSignerAddress))) {
throw new Error(
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
);
}
const permissions: IAccountPermissions.SignerPermissionsStruct =
await this.contractWrapper.readContract.getPermissionsForSigner(
resolvedSignerAddress,
);
if (!permissions.approvedTargets.includes(resolvedTarget)) {
throw new Error("Target is currently not approved");
}
const newTargets = permissions.approvedTargets.filter(
(approvedTarget) =>
utils.getAddress(approvedTarget) !== utils.getAddress(resolvedTarget),
);
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
startDate: BigNumber.from(permissions.startTimestamp),
expirationDate: BigNumber.from(permissions.endTimestamp),
approvedCallTargets: newTargets,
nativeTokenLimitPerTransaction:
permissions.nativeTokenLimitPerTransaction.toString(),
});
},
);
/**
* Set the account's entire snapshot of permissions.
*
* @remarks Sets the account's entire snapshot of permissions.
*
* @param permissionSnapshot - the snapshot to set as the account's entire permission snapshot.
*
* @example
* ```javascript
* const tx = await contract.account.setAccess(permissionSnapshot);
* const receipt = tx.receipt();
* ```
*
* @twfeature AccountPermissions
*/
resetAllPermissions = /* @__PURE__ */ buildTransactionFunction(
async (
permissionSnapshot: PermissionSnapshotInput,
): Promise<Transaction> => {
const resolvedSnapshot = await PermissionSnapshotSchema.parseAsync(
permissionSnapshot,
);
/**
* All cases
*
* - Add new admin :check:
* - Remove current admin :check:
* - Add new scoped :check:
* - Remove current scoped :check:
* - Update current scoped :check:
* - Current admin -> new scoped :check:
* - Current scoped -> new admin :check:
**/
// No duplicate signers in input!
if (this.hasDuplicateSigners(resolvedSnapshot)) {
throw new Error("Duplicate signers found in input.");
}
const addAdminData: string[] = [];
const removeAdminData: string[] = [];
const addOrUpdateSignerData: string[] = [];
const removeSignerData: string[] = [];
// Remove all existing admins not included in the passed snapshot.
const allAdmins = await this.getAllAdmins();
const allToMakeAdmin = resolvedSnapshot
.filter((item) => item.makeAdmin)
.map((item) => item.signer);
allAdmins.forEach((admin) => {
if (!allToMakeAdmin.includes(admin)) {
removeAdminData.push(
this.contractWrapper.writeContract.interface.encodeFunctionData(
"setAdmin",
[admin, false],
),
);
}
});
// Remove all existing signers not included in the passed snapshot.
const allSigners = await this.getAllSigners();
const allToMakeSigners = resolvedSnapshot
.filter((item) => {
return !item.makeAdmin;
})
.map((item) => item.signer);
await Promise.all(
allSigners.map(async (item) => {
if (!allToMakeSigners.includes(item.signer)) {
const data = await this.buildSignerPermissionRequest(item.signer, {
startDate: BigNumber.from(0),
expirationDate: BigNumber.from(0),
approvedCallTargets: [],
nativeTokenLimitPerTransaction: "0",
});
removeSignerData.push(data);
}
}),
);
for (const member of resolvedSnapshot) {
// Add new admin
if (member.makeAdmin) {
addAdminData.push(
this.contractWrapper.writeContract.interface.encodeFunctionData(
"setAdmin",
[member.signer, true],
),
);
} else {
// Add new scoped
const data = await this.buildSignerPermissionRequest(
member.signer,
member.permissions,
);
addOrUpdateSignerData.push(data);
}
}
const data: string[] = [];
removeAdminData.forEach((item) => {
data.push(item);
});
removeSignerData.forEach((item) => {
data.push(item);
});
addOrUpdateSignerData.forEach((item) => {
data.push(item);
});
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "multicall",
args: [data],
});
},
);
}
+117 -504
View File
@@ -1,190 +1,48 @@
import { DetectableFeature } from "../interfaces/DetectableFeature";
import { ContractWrapper } from "./contract-wrapper";
import { FEATURE_ACCOUNT } from "../../constants/thirdweb-features";
import { utils, BigNumber } from "ethers";
import { Transaction } from "./transactions";
import type {
IAccountCore,
IAccountPermissions,
} from "@thirdweb-dev/contracts-js";
import {
SignedSignerPermissionsPayload,
SignerPermissionsOutput,
FEATURE_ACCOUNT,
FEATURE_ACCOUNT_PERMISSIONS,
} from "../../constants/thirdweb-features";
import type { IAccountCore } from "@thirdweb-dev/contracts-js";
import {
SignerWithPermissions,
SignerPermissions,
SignerPermissionsInput,
SignerPermissionRequest,
SignerPermissionsSchema,
PermissionSnapshotInput,
PermissionSnapshotSchema,
PermissionSnapshotOutput,
} from "../../types";
import invariant from "tiny-invariant";
import { buildTransactionFunction } from "../../common/transactions";
import { resolveOrGenerateId } from "../../common/signature-minting";
import { AddressOrEns } from "../../schema";
import { resolveAddress } from "../../common";
import { assertEnabled, detectContractFeature } from "../../common";
import { AccountPermissions } from "./account-permissions";
export class Account<TContract extends IAccountCore>
implements DetectableFeature
{
featureName = FEATURE_ACCOUNT.name;
private contractWrapper: ContractWrapper<IAccountCore>;
private accountPermissions: AccountPermissions | undefined;
constructor(contractWrapper: ContractWrapper<TContract>) {
this.contractWrapper = contractWrapper;
this.accountPermissions = this.detectAccountPermissions();
}
private detectAccountPermissions(): AccountPermissions | undefined {
if (
detectContractFeature<IAccountCore>(
this.contractWrapper,
"AccountPermissions",
)
) {
return new AccountPermissions(this.contractWrapper);
}
return undefined;
}
getAddress(): string {
return this.contractWrapper.readContract.address;
}
/*********************************
* HELPER FUNCTIONS
********************************/
private hasDuplicateSigners(snapshot: PermissionSnapshotOutput): boolean {
const checkedSigner: Record<string, boolean> = {};
const signers = snapshot.map((item) => item.signer);
for (const signer of signers) {
if (!checkedSigner[signer]) {
checkedSigner[signer] = true;
} else {
return true;
}
}
return false;
}
/**
* Format the access restrictions for a given role
*
* @param restrictions - The access restrictions for a given role
* @returns formatted role restrictions
*
*/
private parseSignerPermissionsStruct(
permissions: IAccountPermissions.SignerPermissionsStruct,
): SignerPermissions {
return {
startDate: new Date(
parseInt(permissions.startTimestamp.toString()) * 1000,
),
expirationDate: new Date(
parseInt(permissions.endTimestamp.toString()) * 1000,
),
nativeTokenLimitPerTransaction: BigNumber.from(
permissions.nativeTokenLimitPerTransaction,
),
approvedCallTargets: permissions.approvedTargets,
};
}
private async sendSignerPermissionRequest(
signerAddress: string,
permissions: SignerPermissionsOutput,
): Promise<Transaction> {
const { payload, signature } = await this.generatePayload(
signerAddress,
permissions,
);
const { success } =
await this.contractWrapper.readContract.verifySignerPermissionRequest(
payload,
signature,
);
if (!success) {
throw new Error(`Invalid signature.`);
}
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "setPermissionsForSigner",
args: [payload, signature],
});
}
private async buildSignerPermissionRequest(
signerAddress: string,
permissions: SignerPermissionsOutput,
): Promise<string> {
const { payload, signature } = await this.generatePayload(
signerAddress,
permissions,
);
const isValidSigner =
await this.contractWrapper.readContract.verifySignerPermissionRequest(
payload,
signature,
);
if (!isValidSigner) {
throw new Error(`Invalid signature.`);
}
return this.contractWrapper.writeContract.interface.encodeFunctionData(
"setPermissionsForSigner",
[payload, signature],
);
}
/**
* Generate and sign a payload to grant or revoke a signer's access to the account.
*
* @param signer - The address of the signer
* @param roleAction - The address of the signer
* @returns The generated payload and signature produced on signing that payload.
*
*/
private async generatePayload(
signerAddress: string,
permissions: SignerPermissionsOutput,
): Promise<SignedSignerPermissionsPayload> {
// Get payload struct.
const payload: IAccountPermissions.SignerPermissionRequestStruct = {
signer: signerAddress,
approvedTargets: permissions.approvedCallTargets,
nativeTokenLimitPerTransaction: utils.parseEther(
permissions.nativeTokenLimitPerTransaction,
),
permissionStartTimestamp: permissions.startDate,
permissionEndTimestamp: permissions.expirationDate,
reqValidityStartTimestamp: 0,
// Req validity ends 10 years from now.
reqValidityEndTimestamp: BigNumber.from(
Math.floor(
new Date(Date.now() + 1000 * 60 * 60 * 24 * 365 * 10).getTime() /
1000,
),
),
uid: resolveOrGenerateId(undefined),
};
// Generate signature
const chainId = await this.contractWrapper.getChainID();
const connectedSigner = this.contractWrapper.getSigner();
invariant(connectedSigner, "No signer available");
const signature = await this.contractWrapper.signTypedData(
connectedSigner,
{
name: "Account",
version: "1",
chainId,
verifyingContract: this.getAddress(),
},
{ SignerPermissionRequest },
payload,
);
return { payload, signature };
}
/*********************************
* READ FUNCTIONS
********************************/
@@ -199,13 +57,13 @@ export class Account<TContract extends IAccountCore>
* @param signer - The address of a signer of the account.
* @returns whether a signer is an admin on the account.
*
* @twfeature Account
* @twfeature AccountPermissions
*/
public async isAdmin(signerAddress: AddressOrEns): Promise<boolean> {
const resolvedSignerAddress = await resolveAddress(signerAddress);
return await this.contractWrapper.readContract.isAdmin(
resolvedSignerAddress,
);
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).isAdmin(signerAddress);
}
/**
@@ -218,13 +76,13 @@ export class Account<TContract extends IAccountCore>
* @param signer - The address of a signer of the account.
* @returns whether a signer has permissions to use the account.
*
* @twfeature Account
* @twfeature AccountPermissions
*/
public async isSigner(signerAddress: AddressOrEns): Promise<boolean> {
const resolvedSignerAddress = await resolveAddress(signerAddress);
return await this.contractWrapper.readContract.isActiveSigner(
resolvedSignerAddress,
);
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).isSigner(signerAddress);
}
/**
@@ -237,10 +95,13 @@ export class Account<TContract extends IAccountCore>
*
* @returns all admins of the account.
*
* @twfeature Account
* @twfeature AccountPermissions
*/
public async getAllAdmins(): Promise<string[]> {
return await this.contractWrapper.readContract.getAllAdmins();
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).getAllAdmins();
}
/**
@@ -253,21 +114,13 @@ export class Account<TContract extends IAccountCore>
*
* @returns all (non-admin) signers with permissions to use the account.
*
* @twfeature Account
* @twfeature AccountPermissions
*/
public async getAllSigners(): Promise<SignerWithPermissions[]> {
const activeSignersWithPerms: IAccountPermissions.SignerPermissionsStruct[] =
await this.contractWrapper.readContract.getAllActiveSigners();
return await Promise.all(
activeSignersWithPerms.map(async (signerWithPermissions) => {
const signer = signerWithPermissions.signer;
const permissions = this.parseSignerPermissionsStruct(
signerWithPermissions,
);
return { signer, permissions };
}),
);
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).getAllSigners();
}
/**
@@ -280,27 +133,13 @@ export class Account<TContract extends IAccountCore>
*
* @returns all admins and non-admin signers with permissions to use the account.
*
* @twfeature Account
* @twfeature AccountPermissions
*/
public async getAllAdminsAndSigners(): Promise<SignerWithPermissions[]> {
const allAdmins = await this.getAllAdmins();
const transformedAdmins: SignerWithPermissions[] = allAdmins.map(
(admin) => {
return {
isAdmin: true,
signer: admin,
permissions: {
startDate: new Date(0),
expirationDate: new Date(0),
nativeTokenLimitPerTransaction: BigNumber.from(0),
approvedCallTargets: [],
},
};
},
);
const allSigners = await this.getAllSigners();
return [...transformedAdmins, ...allSigners];
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).getAllAdminsAndSigners();
}
/*********************************
@@ -320,18 +159,14 @@ export class Account<TContract extends IAccountCore>
* const receipt = tx.receipt();
* ```
*
* @twfeature Account
* @twfeature AccountPermissions
*/
grantAdminPermissions = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "setAdmin",
args: [resolvedSignerAddress, true],
});
},
);
public async grantAdminPermissions(signerAddress: AddressOrEns) {
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).grantAdminPermissions(signerAddress);
}
/**
* Revoke an address' admin access to the account.
@@ -346,18 +181,14 @@ export class Account<TContract extends IAccountCore>
* const receipt = tx.receipt();
* ```
*
* @twfeature Account
* @twfeature AccountPermissions
*/
revokeAdminPermissions = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "setAdmin",
args: [resolvedSignerAddress, false],
});
},
);
public async revokeAdminPermissions(signerAddress: AddressOrEns) {
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).revokeAdminPermissions(signerAddress);
}
/**
* Grant a signer permissions to use the account.
@@ -373,36 +204,17 @@ export class Account<TContract extends IAccountCore>
* const receipt = tx.receipt();
* ```
*
* @twfeature Account
* @twfeature AccountPermissions
*/
grantPermissions = /* @__PURE__ */ buildTransactionFunction(
async (
signerAddress: AddressOrEns,
permissions: SignerPermissionsInput,
): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
const resolvedPermissions = await SignerPermissionsSchema.parseAsync(
permissions,
);
if (await this.isAdmin(resolvedSignerAddress)) {
throw new Error(
"Signer is already an admin. Cannot grant permissions to an existing admin.",
);
}
if (await this.isSigner(resolvedSignerAddress)) {
throw new Error(
"Signer already has permissions. Cannot grant permissions to an existing signer. You can update permissions using `updatePermissions`.",
);
}
return await this.sendSignerPermissionRequest(
resolvedSignerAddress,
resolvedPermissions,
);
},
);
public async grantPermissions(
signerAddress: AddressOrEns,
permissions: SignerPermissionsInput,
) {
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).grantPermissions(signerAddress, permissions);
}
/**
* Update the permissions of a signer for using the account.
@@ -418,36 +230,17 @@ export class Account<TContract extends IAccountCore>
* const receipt = tx.receipt();
* ```
*
* @twfeature Account
* @twfeature AccountPermissions
*/
updatePermissions = /* @__PURE__ */ buildTransactionFunction(
async (
signerAddress: AddressOrEns,
permissions: SignerPermissionsInput,
): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
const resolvedPermissions = await SignerPermissionsSchema.parseAsync(
permissions,
);
if (await this.isAdmin(resolvedSignerAddress)) {
throw new Error(
"Signer is already an admin. Cannot update permissions of an existing admin.",
);
}
if (!(await this.isSigner(resolvedSignerAddress))) {
throw new Error(
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
);
}
return await this.sendSignerPermissionRequest(
resolvedSignerAddress,
resolvedPermissions,
);
},
);
public async updatePermissions(
signerAddress: AddressOrEns,
permissions: SignerPermissionsInput,
) {
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).updatePermissions(signerAddress, permissions);
}
/**
* Revoke a scoped access address to the account
@@ -462,32 +255,14 @@ export class Account<TContract extends IAccountCore>
* const receipt = tx.receipt();
* ```
*
* @twfeature Account
* @twfeature AccountPermissions
*/
revokeAccess = /* @__PURE__ */ buildTransactionFunction(
async (signerAddress: AddressOrEns): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
if (await this.isAdmin(resolvedSignerAddress)) {
throw new Error(
"Signer is already an admin. Cannot revoke permissions of an admin.",
);
}
if (!(await this.isSigner(resolvedSignerAddress))) {
throw new Error(
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
);
}
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
startDate: BigNumber.from(0),
expirationDate: BigNumber.from(0),
approvedCallTargets: [],
nativeTokenLimitPerTransaction: "0",
});
},
);
public async revokeAccess(signerAddress: AddressOrEns) {
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).revokeAccess(signerAddress);
}
/**
* Approve an address as a call target for a given signer on the account
@@ -503,48 +278,17 @@ export class Account<TContract extends IAccountCore>
* const receipt = tx.receipt();
* ```
*
* @twfeature Account
* @twfeature AccountPermissions
*/
approveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
async (
signerAddress: AddressOrEns,
target: AddressOrEns,
): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
const resolvedTarget = await resolveAddress(target);
if (await this.isAdmin(resolvedSignerAddress)) {
throw new Error(
"Signer is already an admin. Cannot approve targets for an admin.",
);
}
if (!(await this.isSigner(resolvedSignerAddress))) {
throw new Error(
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
);
}
const permissions: IAccountPermissions.SignerPermissionsStruct =
await this.contractWrapper.readContract.getPermissionsForSigner(
resolvedSignerAddress,
);
if (permissions.approvedTargets.includes(target)) {
throw new Error("Target is already approved");
}
const newTargets = [...permissions.approvedTargets, resolvedTarget];
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
startDate: BigNumber.from(permissions.startTimestamp),
expirationDate: BigNumber.from(permissions.endTimestamp),
approvedCallTargets: newTargets,
nativeTokenLimitPerTransaction:
permissions.nativeTokenLimitPerTransaction.toString(),
});
},
);
public async approveTargetForSigner(
signerAddress: AddressOrEns,
target: AddressOrEns,
) {
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).approveTargetForSigner(signerAddress, target);
}
/**
* Disapprove an address as a call target for a given signer on the account
@@ -560,51 +304,17 @@ export class Account<TContract extends IAccountCore>
* const receipt = tx.receipt();
* ```
*
* @twfeature Account
* @twfeature AccountPermissions
*/
disapproveTargetForSigner = /* @__PURE__ */ buildTransactionFunction(
async (
signerAddress: AddressOrEns,
target: AddressOrEns,
): Promise<Transaction> => {
const resolvedSignerAddress = await resolveAddress(signerAddress);
const resolvedTarget = await resolveAddress(target);
if (await this.isAdmin(resolvedSignerAddress)) {
throw new Error(
"Signer is already an admin. Cannot approve targets for an admin.",
);
}
if (!(await this.isSigner(resolvedSignerAddress))) {
throw new Error(
"Signer does not already have permissions. You can grant permissions using `grantPermissions`.",
);
}
const permissions: IAccountPermissions.SignerPermissionsStruct =
await this.contractWrapper.readContract.getPermissionsForSigner(
resolvedSignerAddress,
);
if (!permissions.approvedTargets.includes(resolvedTarget)) {
throw new Error("Target is currently not approved");
}
const newTargets = permissions.approvedTargets.filter(
(approvedTarget) =>
utils.getAddress(approvedTarget) !== utils.getAddress(resolvedTarget),
);
return await this.sendSignerPermissionRequest(resolvedSignerAddress, {
startDate: BigNumber.from(permissions.startTimestamp),
expirationDate: BigNumber.from(permissions.endTimestamp),
approvedCallTargets: newTargets,
nativeTokenLimitPerTransaction:
permissions.nativeTokenLimitPerTransaction.toString(),
});
},
);
public async disapproveTargetForSigner(
signerAddress: AddressOrEns,
target: AddressOrEns,
) {
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).disapproveTargetForSigner(signerAddress, target);
}
/**
* Set the account's entire snapshot of permissions.
@@ -619,111 +329,14 @@ export class Account<TContract extends IAccountCore>
* const receipt = tx.receipt();
* ```
*
* @twfeature Account
* @twfeature AccountPermissions
*/
resetAllPermissions = /* @__PURE__ */ buildTransactionFunction(
async (
permissionSnapshot: PermissionSnapshotInput,
): Promise<Transaction> => {
const resolvedSnapshot = await PermissionSnapshotSchema.parseAsync(
permissionSnapshot,
);
/**
* All cases
*
* - Add new admin :check:
* - Remove current admin :check:
* - Add new scoped :check:
* - Remove current scoped :check:
* - Update current scoped :check:
* - Current admin -> new scoped :check:
* - Current scoped -> new admin :check:
**/
// No duplicate signers in input!
if (this.hasDuplicateSigners(resolvedSnapshot)) {
throw new Error("Duplicate signers found in input.");
}
const addAdminData: string[] = [];
const removeAdminData: string[] = [];
const addOrUpdateSignerData: string[] = [];
const removeSignerData: string[] = [];
// Remove all existing admins not included in the passed snapshot.
const allAdmins = await this.getAllAdmins();
const allToMakeAdmin = resolvedSnapshot
.filter((item) => item.makeAdmin)
.map((item) => item.signer);
allAdmins.forEach((admin) => {
if (!allToMakeAdmin.includes(admin)) {
removeAdminData.push(
this.contractWrapper.writeContract.interface.encodeFunctionData(
"setAdmin",
[admin, false],
),
);
}
});
// Remove all existing signers not included in the passed snapshot.
const allSigners = await this.getAllSigners();
const allToMakeSigners = resolvedSnapshot
.filter((item) => {
return !item.makeAdmin;
})
.map((item) => item.signer);
await Promise.all(
allSigners.map(async (item) => {
if (!allToMakeSigners.includes(item.signer)) {
const data = await this.buildSignerPermissionRequest(item.signer, {
startDate: BigNumber.from(0),
expirationDate: BigNumber.from(0),
approvedCallTargets: [],
nativeTokenLimitPerTransaction: "0",
});
removeSignerData.push(data);
}
}),
);
for (const member of resolvedSnapshot) {
// Add new admin
if (member.makeAdmin) {
addAdminData.push(
this.contractWrapper.writeContract.interface.encodeFunctionData(
"setAdmin",
[member.signer, true],
),
);
} else {
// Add new scoped
const data = await this.buildSignerPermissionRequest(
member.signer,
member.permissions,
);
addOrUpdateSignerData.push(data);
}
}
const data: string[] = [];
removeAdminData.forEach((item) => {
data.push(item);
});
removeSignerData.forEach((item) => {
data.push(item);
});
addOrUpdateSignerData.forEach((item) => {
data.push(item);
});
return Transaction.fromContractWrapper({
contractWrapper: this.contractWrapper,
method: "multicall",
args: [data],
});
},
);
public async resetAllPermissions(
permissionSnapshot: PermissionSnapshotInput,
) {
return assertEnabled(
this.accountPermissions,
FEATURE_ACCOUNT_PERMISSIONS,
).resetAllPermissions(permissionSnapshot);
}
}
@@ -121,6 +121,7 @@ export class ContractMetadata<
this.contractWrapper.readContract.address,
this.contractWrapper.getProvider(),
this.storage,
this.contractWrapper.options,
);
} catch (err) {}
data = {
@@ -41,6 +41,7 @@ export class ContractPublishedMetadata<TContract extends BaseContract> {
this.contractWrapper.readContract.address,
this.contractWrapper.getProvider(),
this.storage,
this.contractWrapper.options,
);
return this._cachedMetadata;
}
@@ -142,6 +142,7 @@ export class ContractPublisher extends RPCConnectionHandler {
resolvedAddress,
this.getProvider(),
this.storage,
this.options,
);
}
@@ -519,6 +519,7 @@ export class ContractWrapper<
this.readContract.address,
this.getProvider(),
this.storage,
this.options,
);
if (metadata.name) {
@@ -750,7 +750,7 @@ export class MarketplaceV3EnglishAuctions<
const [currentBidBufferBps, winningBid, auction] = await Promise.all([
this.getBidBufferBps(auctionId),
this.getWinningBid(auctionId),
await this.validateAuction(BigNumber.from(auctionId)),
this.validateAuction(BigNumber.from(auctionId)),
]);
const currentBidOrReservePrice = winningBid

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