Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66c53360cd | ||
|
|
bd1fcbae32 | ||
|
|
e196264172 | ||
|
|
c9deda6f4b | ||
|
|
b91d3e9901 | ||
|
|
d248aa2c5a | ||
|
|
8f3b685ad2 | ||
|
|
26349f311d | ||
|
|
2897576513 | ||
|
|
07fb1b5ffa | ||
|
|
300a3c6f04 | ||
|
|
26367c04f3 | ||
|
|
39e2af6f2f | ||
|
|
a5ba9e4fbf |
@@ -1,5 +1,26 @@
|
||||
# @thirdweb-dev/auth
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/auth",
|
||||
"version": "3.2.30",
|
||||
"version": "3.2.33",
|
||||
"main": "dist/thirdweb-dev-auth.cjs.js",
|
||||
"module": "dist/thirdweb-dev-auth.esm.js",
|
||||
"browser": {
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @thirdweb-dev/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
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/chains",
|
||||
"version": "0.1.43",
|
||||
"version": "0.1.44",
|
||||
"main": "dist/thirdweb-dev-chains.cjs.js",
|
||||
"module": "dist/thirdweb-dev-chains.esm.js",
|
||||
"browser": {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,30 @@
|
||||
# thirdweb
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "thirdweb",
|
||||
"main": "dist/cli/index.js",
|
||||
"version": "0.11.13",
|
||||
"version": "0.11.16",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
# @thirdweb-dev/react-core
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-core",
|
||||
"version": "3.14.30",
|
||||
"version": "3.14.33",
|
||||
"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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @thirdweb-dev/react-native-compat
|
||||
|
||||
## 0.2.52
|
||||
|
||||
## 0.2.51
|
||||
|
||||
## 0.2.50
|
||||
|
||||
## 0.2.49
|
||||
|
||||
## 0.2.48
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-native-compat",
|
||||
"description": "Shims for Thirdweb in React Native Projects",
|
||||
"version": "0.2.49",
|
||||
"version": "0.2.52",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,66 @@
|
||||
# @thirdweb-dev/react-native
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react-native",
|
||||
"version": "0.2.49",
|
||||
"version": "0.2.52",
|
||||
"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
-8
@@ -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>
|
||||
) : (
|
||||
|
||||
+6
-10
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -19,7 +19,9 @@ 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/WalletConnectV2";
|
||||
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";
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
export type WCWallet = {
|
||||
iconURL: string;
|
||||
name: string;
|
||||
links: {
|
||||
native: string;
|
||||
universal: string;
|
||||
};
|
||||
};
|
||||
@@ -2,7 +2,7 @@ export type WCMeta = {
|
||||
name: string;
|
||||
iconURL: string;
|
||||
links: {
|
||||
native?: string;
|
||||
universal?: string;
|
||||
native: string;
|
||||
universal: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+25
-5
@@ -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,
|
||||
},
|
||||
});
|
||||
+87
@@ -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} />;
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,33 @@
|
||||
# @thirdweb-dev/react
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/react",
|
||||
"version": "3.14.30",
|
||||
"version": "3.14.33",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
|
||||
"author": "thirdweb eng <[email protected]>",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
# @thirdweb-dev/sdk
|
||||
|
||||
## 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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/sdk",
|
||||
"version": "3.10.49",
|
||||
"version": "3.10.52",
|
||||
"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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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],
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -413,6 +413,12 @@ export class Transaction<
|
||||
* Get the signed transaction
|
||||
*/
|
||||
async sign(): Promise<string> {
|
||||
const populatedTx = await this.populateTransaction();
|
||||
const signedTx = await this.contract.signer.signTransaction(populatedTx);
|
||||
return signedTx;
|
||||
}
|
||||
|
||||
async populateTransaction(): Promise<providers.TransactionRequest> {
|
||||
const gasOverrides = await this.getGasOverrides();
|
||||
const overrides: CallOverrides = { ...gasOverrides, ...this.overrides };
|
||||
|
||||
@@ -426,8 +432,7 @@ export class Transaction<
|
||||
overrides,
|
||||
);
|
||||
const populatedTx = await this.contract.signer.populateTransaction(tx);
|
||||
const signedTx = await this.contract.signer.signTransaction(populatedTx);
|
||||
return signedTx;
|
||||
return populatedTx;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -845,7 +850,7 @@ export class DeployTransaction extends TransactionContext {
|
||||
return contractAddress;
|
||||
}
|
||||
|
||||
private async populateTransaction(): Promise<providers.TransactionRequest> {
|
||||
public async populateTransaction(): Promise<providers.TransactionRequest> {
|
||||
const gasOverrides = await this.getGasOverrides();
|
||||
const overrides: CallOverrides = { ...gasOverrides, ...this.overrides };
|
||||
|
||||
|
||||
@@ -738,13 +738,17 @@ export class ThirdwebSDK extends RPCConnectionHandler {
|
||||
try {
|
||||
let chainSDK = sdkMap[chainId];
|
||||
if (!chainSDK) {
|
||||
chainSDK = new ThirdwebSDK(chainId, {
|
||||
...this.options,
|
||||
// need to disable readonly settings for this to work
|
||||
readonlySettings: undefined,
|
||||
// @ts-expect-error - zod doesn't like this
|
||||
supportedChains: chains,
|
||||
});
|
||||
chainSDK = new ThirdwebSDK(
|
||||
chainId,
|
||||
{
|
||||
...this.options,
|
||||
// need to disable readonly settings for this to work
|
||||
readonlySettings: undefined,
|
||||
// @ts-expect-error - zod doesn't like this
|
||||
supportedChains: chains,
|
||||
},
|
||||
this.storage,
|
||||
);
|
||||
sdkMap[chainId] = chainSDK;
|
||||
}
|
||||
|
||||
@@ -1756,6 +1760,7 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
resolvedCustomFactoryAddress,
|
||||
this.getProvider(),
|
||||
this.storage,
|
||||
this.options,
|
||||
);
|
||||
|
||||
const factoryFunctionParamTypes = extractFunctionParamsFromAbi(
|
||||
@@ -2198,10 +2203,14 @@ export class ContractDeployer extends RPCConnectionHandler {
|
||||
version: string,
|
||||
) {
|
||||
const address = await resolveAddress(publisherAddress);
|
||||
const publishedContract = await new ThirdwebSDK("polygon", {
|
||||
clientId: this.options.clientId,
|
||||
secretKey: this.options.secretKey,
|
||||
})
|
||||
const publishedContract = await new ThirdwebSDK(
|
||||
"polygon",
|
||||
{
|
||||
clientId: this.options.clientId,
|
||||
secretKey: this.options.secretKey,
|
||||
},
|
||||
this.storage,
|
||||
)
|
||||
.getPublisher()
|
||||
.getVersion(address, contractName, version);
|
||||
if (!publishedContract) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { ThirdwebSDK } from "../../src/evm";
|
||||
import { expectError, sdk } from "./before-setup";
|
||||
import { expect } from "chai";
|
||||
import { Wallet } from "ethers";
|
||||
import { MockStorage } from "./mock/MockStorage";
|
||||
|
||||
describe("Error Handling", async () => {
|
||||
it("should throw proper error on account with no balance", async () => {
|
||||
@@ -9,6 +10,7 @@ describe("Error Handling", async () => {
|
||||
Wallet.createRandom().connect(sdk.getProvider()),
|
||||
undefined,
|
||||
{ secretKey: process.env.TW_SECRET_KEY },
|
||||
MockStorage(),
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { signers } from "./before-setup";
|
||||
import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers";
|
||||
import { expect, assert } from "chai";
|
||||
|
||||
// TODO mock upload metadata to deploy this contract isntead of relying on IPFS
|
||||
describe("Tiered Drop Contract", async () => {
|
||||
let contract: SmartContract;
|
||||
let sdk: ThirdwebSDK;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @thirdweb-dev/storage
|
||||
|
||||
## 1.2.6
|
||||
|
||||
### 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)! - Let users pass their own timeout value, fix fallback gateway logic
|
||||
|
||||
## 1.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/storage",
|
||||
"version": "1.2.5",
|
||||
"version": "1.2.6",
|
||||
"main": "dist/thirdweb-dev-storage.cjs.js",
|
||||
"module": "dist/thirdweb-dev-storage.esm.js",
|
||||
"browser": {
|
||||
|
||||
@@ -28,12 +28,17 @@ import pkg from "../../../package.json";
|
||||
* @public
|
||||
*/
|
||||
export class StorageDownloader implements IStorageDownloader {
|
||||
DEFAULT_TIMEOUT_IN_SECONDS = 10;
|
||||
|
||||
private secretKey?: string;
|
||||
private clientId?: string;
|
||||
private timeoutInSeconds: number;
|
||||
|
||||
constructor(options: IpfsDownloaderOptions) {
|
||||
this.secretKey = options.secretKey;
|
||||
this.clientId = options.clientId;
|
||||
this.timeoutInSeconds =
|
||||
options.timeoutInSeconds || this.DEFAULT_TIMEOUT_IN_SECONDS;
|
||||
}
|
||||
|
||||
async download(
|
||||
@@ -119,7 +124,10 @@ export class StorageDownloader implements IStorageDownloader {
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 5000);
|
||||
const timeout = setTimeout(
|
||||
() => controller.abort(),
|
||||
this.timeoutInSeconds * 1000,
|
||||
);
|
||||
const resOrErr: Response | Error = await fetch(resolvedUri, {
|
||||
headers,
|
||||
signal: controller.signal,
|
||||
@@ -131,7 +139,13 @@ export class StorageDownloader implements IStorageDownloader {
|
||||
|
||||
if (!("status" in resOrErr)) {
|
||||
// early exit if we don't have a status code
|
||||
return this.download(uri, gatewayUrls, attempts + 1);
|
||||
throw new Error(
|
||||
`Request timed out after ${this.timeoutInSeconds} seconds. ${
|
||||
isTwGatewayUrl(resolvedUri)
|
||||
? "You can update the timeoutInSeconds option to increase the timeout."
|
||||
: "You're using a public IPFS gateway, pass in a clientId or secretKey for a reliable IPFS gateway."
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
// if the request is good we can skip everything else
|
||||
@@ -172,6 +186,7 @@ export class StorageDownloader implements IStorageDownloader {
|
||||
resOrErr.status !== 429 &&
|
||||
resOrErr.status < 500
|
||||
) {
|
||||
return resOrErr;
|
||||
}
|
||||
|
||||
// Since the current gateway failed, recursively try the next one we know about
|
||||
|
||||
@@ -75,6 +75,7 @@ export class ThirdwebStorage<T extends UploadOptions = IpfsUploadBatchOptions>
|
||||
new StorageDownloader({
|
||||
secretKey: options?.secretKey,
|
||||
clientId: options?.clientId,
|
||||
timeoutInSeconds: options?.downloadTimeoutInSeconds,
|
||||
});
|
||||
this.gatewayUrls = prepareGatewayUrls(
|
||||
parseGatewayUrls(options?.gatewayUrls),
|
||||
|
||||
@@ -261,11 +261,7 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
|
||||
(xhr.readyState !== 0 && xhr.readyState !== 4) ||
|
||||
xhr.status === 0
|
||||
) {
|
||||
return reject(
|
||||
new Error(
|
||||
"This looks like a network error, the endpoint might be blocked by an internet provider or a firewall.",
|
||||
),
|
||||
);
|
||||
return reject(new Error("Upload failed due to a network error."));
|
||||
}
|
||||
|
||||
return reject(new Error("Unknown upload error occured"));
|
||||
|
||||
@@ -27,6 +27,10 @@ export type IpfsDownloaderOptions = {
|
||||
* You can get a clientId here: https://thirdweb.com/create-api-key
|
||||
*/
|
||||
clientId?: string;
|
||||
/**
|
||||
* Optional timeout in seconds for the download request
|
||||
*/
|
||||
timeoutInSeconds?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,7 @@ export type ThirdwebStorageOptions<T extends UploadOptions> = {
|
||||
uploader?: IStorageUploader<T>;
|
||||
downloader?: IStorageDownloader;
|
||||
gatewayUrls?: GatewayUrls | string[];
|
||||
downloadTimeoutInSeconds?: number;
|
||||
uploadServerUrl?: string;
|
||||
clientId?: string;
|
||||
secretKey?: string;
|
||||
|
||||
@@ -97,38 +97,6 @@ describe("IPFS", async () => {
|
||||
expect(data.properties.length).to.equal(1);
|
||||
});
|
||||
|
||||
it("Should retry download if gateways fail", async () => {
|
||||
const storageWithBadGateways = new ThirdwebStorage({
|
||||
gatewayUrls: {
|
||||
"ipfs://": [
|
||||
"http://thisshouldneverwork/",
|
||||
"http://thisshouldalsoneverwork/",
|
||||
...DEFAULT_GATEWAY_URLS["ipfs://"],
|
||||
],
|
||||
},
|
||||
secretKey,
|
||||
});
|
||||
|
||||
const uri = await storageWithBadGateways.upload(
|
||||
{
|
||||
name: "Goku",
|
||||
description: "The strongest human in the world",
|
||||
properties: [
|
||||
{
|
||||
name: "Strength",
|
||||
value: "100",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
alwaysUpload: true,
|
||||
},
|
||||
);
|
||||
|
||||
const data = await storageWithBadGateways.download(uri);
|
||||
expect(data.status).to.equal(200);
|
||||
});
|
||||
|
||||
it("Should batch upload strings with names", async () => {
|
||||
const uris = await storage.uploadBatch(
|
||||
[
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"declaration": true,
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": false,
|
||||
"jsx": "react-jsx",
|
||||
"jsx": "react-native",
|
||||
"lib": ["es6", "es2015", "es2016", "es2017", "es2018"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
|
||||
@@ -1,5 +1,34 @@
|
||||
# @thirdweb-dev/unity-js-bridge
|
||||
|
||||
## 0.2.70
|
||||
|
||||
### 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
|
||||
|
||||
## 0.2.69
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 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
|
||||
|
||||
## 0.2.68
|
||||
|
||||
### 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
|
||||
|
||||
## 0.2.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/unity-js-bridge",
|
||||
"version": "0.2.67",
|
||||
"version": "0.2.70",
|
||||
"main": "dist/thirdweb-unity-bridge.js",
|
||||
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# @thirdweb-dev/wallets
|
||||
|
||||
## 1.1.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
|
||||
|
||||
## 1.1.15
|
||||
|
||||
### 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
|
||||
|
||||
- Updated dependencies [[`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/chains@0.1.44
|
||||
|
||||
## 1.1.14
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [#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)! - Correct gas estimation for polygon/mumbai
|
||||
|
||||
- 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
|
||||
|
||||
## 1.1.13
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@thirdweb-dev/wallets",
|
||||
"version": "1.1.13",
|
||||
"version": "1.1.16",
|
||||
"main": "dist/thirdweb-dev-wallets.cjs.js",
|
||||
"module": "dist/thirdweb-dev-wallets.esm.js",
|
||||
"types": "dist/thirdweb-dev-wallets.cjs.d.ts",
|
||||
|
||||
@@ -15,7 +15,7 @@ import { assertWindowEthereum } from "../../utils/assertWindowEthereum";
|
||||
import { getInjectedName } from "../../utils/getInjectedName";
|
||||
import { getValidPublicRPCUrl } from "../../utils/url";
|
||||
import { Ethereum } from "./types";
|
||||
import { getValidChainRPCs, type Chain } from "@thirdweb-dev/chains";
|
||||
import { type Chain } from "@thirdweb-dev/chains";
|
||||
import { utils, providers } from "ethers";
|
||||
|
||||
export type InjectedConnectorOptions = {
|
||||
@@ -341,7 +341,6 @@ export class InjectedConnector extends WagmiConnector<
|
||||
?.originalError?.code === 4902
|
||||
) {
|
||||
try {
|
||||
console.log("adding chain", getValidChainRPCs(chain));
|
||||
// request provider to add chain
|
||||
await provider.request({
|
||||
method: "wallet_addEthereumChain",
|
||||
|
||||
@@ -18,7 +18,10 @@ import {
|
||||
CeloAlfajoresTestnet,
|
||||
CeloBaklavaTestnet,
|
||||
Celo,
|
||||
Mumbai,
|
||||
Polygon,
|
||||
} from "@thirdweb-dev/chains";
|
||||
import { getPolygonGasPriorityFee } from "@thirdweb-dev/sdk";
|
||||
|
||||
export interface BaseApiParams {
|
||||
provider: providers.Provider;
|
||||
@@ -291,6 +294,9 @@ export abstract class BaseAccountAPI {
|
||||
let { maxFeePerGas, maxPriorityFeePerGas } = info;
|
||||
if (!maxFeePerGas || !maxPriorityFeePerGas) {
|
||||
const feeData = await this.provider.getFeeData();
|
||||
if (!maxPriorityFeePerGas) {
|
||||
maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined;
|
||||
}
|
||||
if (!maxFeePerGas) {
|
||||
maxFeePerGas = feeData.maxFeePerGas ?? undefined;
|
||||
const network = await this.provider.getNetwork();
|
||||
@@ -303,9 +309,22 @@ export abstract class BaseAccountAPI {
|
||||
) {
|
||||
maxPriorityFeePerGas = maxFeePerGas;
|
||||
}
|
||||
}
|
||||
if (!maxPriorityFeePerGas) {
|
||||
maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? undefined;
|
||||
|
||||
if (
|
||||
maxPriorityFeePerGas &&
|
||||
(chainId === Mumbai.chainId || chainId === Polygon.chainId)
|
||||
) {
|
||||
// for polygon/mumbai, override fee data from gas station
|
||||
const block = await this.provider.getBlock("latest");
|
||||
const baseBlockFee =
|
||||
block && block.baseFeePerGas
|
||||
? block.baseFeePerGas
|
||||
: utils.parseUnits("1", "gwei");
|
||||
maxPriorityFeePerGas = await getPolygonGasPriorityFee(chainId);
|
||||
// See: https://eips.ethereum.org/EIPS/eip-1559 for formula
|
||||
const baseMaxFeePerGas = baseBlockFee.mul(2);
|
||||
maxFeePerGas = baseMaxFeePerGas.add(maxPriorityFeePerGas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user