Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement metamask snap #11

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
fb39181
feat: add metamask support
wantedsystem Oct 25, 2023
252d1a1
fix: fix return types
Oct 27, 2023
1cd0024
Merge pull request #4 from bajpai244/feat/fix-return-types
bajpai244 Oct 30, 2023
149d902
feat: add signer
stanleyyconsensys Nov 13, 2023
34554ee
fix: fix type error
wantedsystem Nov 13, 2023
c932a58
feat: implement signer interface
stanleyyconsensys Nov 14, 2023
1c7b77d
feat: add refactor snap class
stanleyyconsensys Nov 14, 2023
c0fcd2d
feat: add missing methods to request in MetaMaskSnapWallet
wantedsystem Nov 14, 2023
766c209
refactor: improve chainId handling in MetaMaskSnapWallet.enable function
wantedsystem Nov 14, 2023
ae60717
fix: add network manage method
stanleyyconsensys Nov 15, 2023
f952165
feat: add method in snap to support install determination
stanleyyconsensys Nov 15, 2023
fa73e02
feat: inject metamask wallet
stanleyyconsensys Nov 15, 2023
f206c49
fix: fix typescript errors
wantedsystem Nov 15, 2023
72599de
feat: enable popup in switch network
stanleyyconsensys Nov 16, 2023
9017717
feat: add demo page
stanleyyconsensys Nov 16, 2023
9d14216
feat: convert sign format to sign object
stanleyyconsensys Nov 16, 2023
e55ab0b
fix: some refactoring and fix metamask icon
wantedsystem Nov 17, 2023
a4feaaf
feat: implement watchAsset method
wantedsystem Nov 17, 2023
4059111
chore: some refactoring on main.ts
wantedsystem Nov 17, 2023
4715c3a
feat: add switchNetwork to the demo page
wantedsystem Nov 17, 2023
8bc277f
fix: small fix
wantedsystem Nov 17, 2023
0454b5b
feat: support re-select address and not throw error in event
stanleyyconsensys Nov 20, 2023
c31790a
feat: add is installed
stanleyyconsensys Nov 20, 2023
9fb95a9
feat: always return true to enhance speed
stanleyyconsensys Nov 20, 2023
a5748e5
feat: reset object when network switch
stanleyyconsensys Nov 20, 2023
9d09e30
fix: fix call of switchNetwork
wantedsystem Nov 20, 2023
12ae904
fix: fix typescript errors
wantedsystem Nov 20, 2023
4614cc8
fix: clean up unused file
stanleyyconsensys Nov 23, 2023
33fdc45
feat: rollback lock file
stanleyyconsensys Nov 23, 2023
174f6c6
feat: remove extra package
stanleyyconsensys Nov 23, 2023
79fdf37
fix: rollback lock file spacing
stanleyyconsensys Nov 23, 2023
eb6f79b
fix: update switch network rpc result
stanleyyconsensys Nov 28, 2023
69bf6cc
fix: remove wallet
stanleyyconsensys Jan 5, 2024
a2b5536
fix: rollback file
stanleyyconsensys Jan 5, 2024
7bb2415
feat: sync with naor repo
stanleyyconsensys Jan 8, 2024
f6ad995
fix: sync lock
stanleyyconsensys Jan 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 100 additions & 6 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
import "./App.css"
import * as util from "./lib"
import {
type ConnectOptions,
type DisconnectOptions,
StarknetWindowObject,
connect,
disconnect,
} from "get-starknet"
import { useState } from "react"

function App() {
const [walletName, setWalletName] = useState("")
const [walletInfo, setWalletInfo] = useState("")
const [wallet, setWallet] = useState<StarknetWindowObject | null>(null)

function handleConnect(options?: ConnectOptions) {
return async () => {
const res = await connect(options)
console.log(res)
setWalletName(res?.name || "")
console.log("wallet", res)
setWallet(res)
setWalletInfo(
`Name: ${res?.name || ""}, Address: ${
res?.selectedAddress || ""
}, ChainId: ${res?.chainId || ""}`,
)
}
}

function handleDisconnect(options?: DisconnectOptions) {
return async () => {
await disconnect(options)
setWalletName("")
setWalletInfo("")
}
}

Expand Down Expand Up @@ -55,13 +63,99 @@ function App() {
Disconnect and reset
</button>
</div>
{walletName && (
{walletInfo && (
<div>
<h2>
Selected Wallet: <pre>{walletName}</pre>
Selected Wallet: <pre>{walletInfo}</pre>
</h2>
</div>
)}
<ul>
<li>
<button
onClick={() => {
wallet && util.sendErc20Transaction(wallet)
}}>
send erc20 transaction
</button>
</li>

<li>
<button
onClick={() => {
wallet && util.deployContract(wallet)
}}>
deploy contract
</button>
</li>

<li>
<button
onClick={() => {
wallet && util.signMessage(wallet)
}}>
sign message
</button>
</li>

<li>
<button
onClick={() => {
wallet && util.signMessageSlient(wallet)
}}>
sign message (slient)
</button>
</li>
<li>
<button
onClick={() => {
wallet && util.signDeployAccountTransaction(wallet)
}}>
sign deploy account transaction
</button>
</li>
<li>
<button
onClick={() => {
wallet && util.signDeclareTransaction(wallet)
}}>
sign delcare transaction
</button>
</li>
<li>
<button
onClick={() => {
wallet && util.signTransaction(wallet)
}}>
sign transaction
</button>
</li>
<li>
<button
onClick={() => {
wallet && util.getPubKey(wallet)
}}>
get public key
</button>
</li>

<li>
<button
onClick={() => {
wallet && util.getNonce(wallet)
}}>
get Nonce
</button>
</li>
<li>
<button
onClick={() => {
wallet && util.switchNetwork(wallet)
}}>
Switch Network
</button>
</li>
</ul>
</div>
)
}
Expand Down
197 changes: 197 additions & 0 deletions example/src/lib.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { StarknetWindowObject } from "get-starknet"

enum StarknetChainId {
SN_MAIN = "0x534e5f4d41494e",
SN_GOERLI = "0x534e5f474f45524c49",
}
const signMessagePayload = {
types: {
StarkNetDomain: [
{ name: "name", type: "felt" },
{ name: "version", type: "felt" },
{ name: "chainId", type: "felt" },
],
Person: [
{ name: "name", type: "felt" },
{ name: "wallet", type: "felt" },
],
Mail: [
{ name: "from", type: "Person" },
{ name: "to", type: "Person" },
{ name: "contents", type: "felt" },
],
},
primaryType: "Mail",
domain: {
name: "StarkNet Mail",
version: "1",
chainId: 1,
},
message: {
from: {
name: "Cow",
wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
},
to: {
name: "Bob",
wallet: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
},
contents: "Hello, Bob!",
},
}

const transactionPayload = {
contractAddress:
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
calldata: [
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
"10",
"0",
],
entrypoint: "transfer",
}

const signTransactionsDetail = {
walletAddress:
"0x00b28a089e7fb83debee4607b6334d687918644796b47d9e9e38ea8213833137",
chainId: StarknetChainId.SN_GOERLI,
cairoVersion: "0",
nonce: "0x1",
version: "0x0",
maxFee: 100,
}

const signDeployAccountTransactionPayload = {
classHash:
"0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918",
contractAddress:
"0x00b28a089e7fb83debee4607b6334d687918644796b47d9e9e38ea8213833137",
constructorCalldata: [],
addressSalt:
"0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918",
chainId: "0x534e5f474f45524c49",
nonce: "0x1",
version: "0x0",
maxFee: 100,
}

const deployContractPayload = {
classHash:
"0x189ce59d98d8d3883a5a9fc7026cc94519ca099147196680734ec46aee5e750",
constructorCalldata: [],
}

const delcareContractPayload = {
classHash:
"0x025ec026985a3bf9d0cc1fe17326b245dfdc3ff89b8fde106542a3ea56c5a918",
senderAddress:
"0x00b28a089e7fb83debee4607b6334d687918644796b47d9e9e38ea8213833137",
chainId: StarknetChainId.SN_GOERLI,
nonce: "0x1",
version: "0x0",
maxFee: 100,
}

export const sendErc20Transaction = async (wallet: StarknetWindowObject) => {
try {
const response = await wallet.account?.execute(transactionPayload)
console.log("response is ----> ", response)
} catch (err) {
console.log("error", err)
}
}

export const deployContract = async (wallet: StarknetWindowObject) => {
try {
const response = await wallet.account?.deployContract(
deployContractPayload,
{},
)
console.log("response is ----> ", response)
} catch (err) {
console.log("error", err)
}
}

export const getPubKey = async (wallet: StarknetWindowObject) => {
try {
const response = await wallet.account?.signer.getPubKey()
console.log("response is ----> ", response)
} catch (err) {
console.log("error", err)
}
}

export const signMessage = async (wallet: StarknetWindowObject) => {
try {
const response = await wallet.account?.signMessage(signMessagePayload)
console.log("response is ----> ", response)
} catch (err) {
console.log("error", err)
}
}

export const signMessageSlient = async (wallet: StarknetWindowObject) => {
try {
const response = await wallet.account?.signer.signMessage(
signMessagePayload,
wallet.account?.address,
)
console.log("sign is ----> ", response)

if (response) {
const verify = await wallet.account?.verifyMessage(
signMessagePayload,
response as any,
)

console.log("verify result is ----> ", verify)
}
} catch (err) {
console.log("error", err)
}
}

export const signTransaction = async (wallet: StarknetWindowObject) => {
const response = await wallet.account?.signer.signTransaction(
[transactionPayload],
signTransactionsDetail as any,
)
console.log("sign is ----> ", response)
}

export const signDeployAccountTransaction = async (
wallet: StarknetWindowObject,
) => {
const response = await wallet.account?.signer.signDeployAccountTransaction(
signDeployAccountTransactionPayload as any,
)
console.log("sign is ----> ", response)
}

export const signDeclareTransaction = async (wallet: StarknetWindowObject) => {
const response = await wallet.account?.signer.signDeclareTransaction(
delcareContractPayload as any,
)

console.log("sign is ----> ", response)
}

export const getNonce = async (wallet: StarknetWindowObject) => {
const response = await wallet.account?.getNonce()
console.log("response is ----> ", response)
}

export const switchNetwork = async (wallet: StarknetWindowObject) => {
const chainId =
wallet.chainId == StarknetChainId.SN_MAIN
? StarknetChainId.SN_GOERLI
: StarknetChainId.SN_MAIN
const response = await wallet.request({
type: "wallet_switchStarknetChain",
params: {
chainId,
},
})
console.log("response is ----> ", response)
}
4 changes: 4 additions & 0 deletions example/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ import { defineConfig } from "vite"

// https://vitejs.dev/config/
export default defineConfig({
build: {
target: "es2020",
},
plugins: [react()],
optimizeDeps: { esbuildOptions: { target: "es2020" } },
})
4 changes: 4 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
"dev": "vite build --watch",
"test": "vitest"
},
"dependencies": {
"@consensys/get-starknet": "1.0.0-dev-fc04076-202312205118",
"@metamask/detect-provider": "^2.0.0"
},
"devDependencies": {
"c8": "^7.12.0",
"happy-dom": "^6.0.4",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/__test__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ describe("getDiscoveryWallets()", () => {
it("should return all discovery wallets", async () => {
const sn = getWallet({})
const discoveryWallets = await sn.getDiscoveryWallets()
expect(discoveryWallets.length).toBe(2)
expect(discoveryWallets.length).toBe(3)
expect(discoveryWallets.map((w) => w.id)).contains(ArgentXMock.id)
expect(discoveryWallets.map((w) => w.id)).contains(BraavosMock.id)
})
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ const wallets: WalletProvider[] = [
edge: "https://microsoftedge.microsoft.com/addons/detail/braavos-wallet/hkkpjehhcnhgefhbdcgfkeegglpjchdc",
},
},
{
id: "metamask",
name: "MetaMask",
icon: `data:image/svg+xml;utf8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMTIiIGhlaWdodD0iMTg5IiB2aWV3Qm94PSIwIDAgMjEyIDE4OSI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48cG9seWdvbiBmaWxsPSIjQ0RCREIyIiBwb2ludHM9IjYwLjc1IDE3My4yNSA4OC4zMTMgMTgwLjU2MyA4OC4zMTMgMTcxIDkwLjU2MyAxNjguNzUgMTA2LjMxMyAxNjguNzUgMTA2LjMxMyAxODAgMTA2LjMxMyAxODcuODc1IDg5LjQzOCAxODcuODc1IDY4LjYyNSAxNzguODc1Ii8+PHBvbHlnb24gZmlsbD0iI0NEQkRCMiIgcG9pbnRzPSIxMDUuNzUgMTczLjI1IDEzMi43NSAxODAuNTYzIDEzMi43NSAxNzEgMTM1IDE2OC43NSAxNTAuNzUgMTY4Ljc1IDE1MC43NSAxODAgMTUwLjc1IDE4Ny44NzUgMTMzLjg3NSAxODcuODc1IDExMy4wNjMgMTc4Ljg3NSIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMjU2LjUgMCkiLz48cG9seWdvbiBmaWxsPSIjMzkzOTM5IiBwb2ludHM9IjkwLjU2MyAxNTIuNDM4IDg4LjMxMyAxNzEgOTEuMTI1IDE2OC43NSAxMjAuMzc1IDE2OC43NSAxMjMuNzUgMTcxIDEyMS41IDE1Mi40MzggMTE3IDE0OS42MjUgOTQuNSAxNTAuMTg4Ii8+PHBvbHlnb24gZmlsbD0iI0Y4OUMzNSIgcG9pbnRzPSI3NS4zNzUgMjcgODguODc1IDU4LjUgOTUuMDYzIDE1MC4xODggMTE3IDE1MC4xODggMTIzLjc1IDU4LjUgMTM2LjEyNSAyNyIvPjxwb2x5Z29uIGZpbGw9IiNGODlEMzUiIHBvaW50cz0iMTYuMzEzIDk2LjE4OCAuNTYzIDE0MS43NSAzOS45MzggMTM5LjUgNjUuMjUgMTM5LjUgNjUuMjUgMTE5LjgxMyA2NC4xMjUgNzkuMzEzIDU4LjUgODMuODEzIi8+PHBvbHlnb24gZmlsbD0iI0Q4N0MzMCIgcG9pbnRzPSI0Ni4xMjUgMTAxLjI1IDkyLjI1IDEwMi4zNzUgODcuMTg4IDEyNiA2NS4yNSAxMjAuMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VBOEQzQSIgcG9pbnRzPSI0Ni4xMjUgMTAxLjgxMyA2NS4yNSAxMTkuODEzIDY1LjI1IDEzNy44MTMiLz48cG9seWdvbiBmaWxsPSIjRjg5RDM1IiBwb2ludHM9IjY1LjI1IDEyMC4zNzUgODcuNzUgMTI2IDk1LjA2MyAxNTAuMTg4IDkwIDE1MyA2NS4yNSAxMzguMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VCOEYzNSIgcG9pbnRzPSI2NS4yNSAxMzguMzc1IDYwLjc1IDE3My4yNSA5MC41NjMgMTUyLjQzOCIvPjxwb2x5Z29uIGZpbGw9IiNFQThFM0EiIHBvaW50cz0iOTIuMjUgMTAyLjM3NSA5NS4wNjMgMTUwLjE4OCA4Ni42MjUgMTI1LjcxOSIvPjxwb2x5Z29uIGZpbGw9IiNEODdDMzAiIHBvaW50cz0iMzkuMzc1IDEzOC45MzggNjUuMjUgMTM4LjM3NSA2MC43NSAxNzMuMjUiLz48cG9seWdvbiBmaWxsPSIjRUI4RjM1IiBwb2ludHM9IjEyLjkzOCAxODguNDM4IDYwLjc1IDE3My4yNSAzOS4zNzUgMTM4LjkzOCAuNTYzIDE0MS43NSIvPjxwb2x5Z29uIGZpbGw9IiNFODgyMUUiIHBvaW50cz0iODguODc1IDU4LjUgNjQuNjg4IDc4Ljc1IDQ2LjEyNSAxMDEuMjUgOTIuMjUgMTAyLjkzOCIvPjxwb2x5Z29uIGZpbGw9IiNERkNFQzMiIHBvaW50cz0iNjAuNzUgMTczLjI1IDkwLjU2MyAxNTIuNDM4IDg4LjMxMyAxNzAuNDM4IDg4LjMxMyAxODAuNTYzIDY4LjA2MyAxNzYuNjI1Ii8+PHBvbHlnb24gZmlsbD0iI0RGQ0VDMyIgcG9pbnRzPSIxMjEuNSAxNzMuMjUgMTUwLjc1IDE1Mi40MzggMTQ4LjUgMTcwLjQzOCAxNDguNSAxODAuNTYzIDEyOC4yNSAxNzYuNjI1IiB0cmFuc2Zvcm09Im1hdHJpeCgtMSAwIDAgMSAyNzIuMjUgMCkiLz48cG9seWdvbiBmaWxsPSIjMzkzOTM5IiBwb2ludHM9IjcwLjMxMyAxMTIuNSA2NC4xMjUgMTI1LjQzOCA4Ni4wNjMgMTE5LjgxMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMTUwLjE4OCAwKSIvPjxwb2x5Z29uIGZpbGw9IiNFODhGMzUiIHBvaW50cz0iMTIuMzc1IC41NjMgODguODc1IDU4LjUgNzUuOTM4IDI3Ii8+PHBhdGggZmlsbD0iIzhFNUEzMCIgZD0iTTEyLjM3NTAwMDIsMC41NjI1MDAwMDggTDIuMjUwMDAwMDMsMzEuNTAwMDAwNSBMNy44NzUwMDAxMiw2NS4yNTAwMDEgTDMuOTM3NTAwMDYsNjcuNTAwMDAxIEw5LjU2MjUwMDE0LDcyLjU2MjUgTDUuMDYyNTAwMDgsNzYuNTAwMDAxMSBMMTEuMjUsODIuMTI1MDAxMiBMNy4zMTI1MDAxMSw4NS41MDAwMDEzIEwxNi4zMTI1MDAyLDk2Ljc1MDAwMTQgTDU4LjUwMDAwMDksODMuODEyNTAxMiBDNzkuMTI1MDAxMiw2Ny4zMTI1MDA0IDg5LjI1MDAwMTMsNTguODc1MDAwMyA4OC44NzUwMDEzLDU4LjUwMDAwMDkgQzg4LjUwMDAwMTMsNTguMTI1MDAwOSA2My4wMDAwMDA5LDM4LjgxMjUwMDYgMTIuMzc1MDAwMiwwLjU2MjUwMDAwOCBaIi8+PGcgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMjExLjUgMCkiPjxwb2x5Z29uIGZpbGw9IiNGODlEMzUiIHBvaW50cz0iMTYuMzEzIDk2LjE4OCAuNTYzIDE0MS43NSAzOS45MzggMTM5LjUgNjUuMjUgMTM5LjUgNjUuMjUgMTE5LjgxMyA2NC4xMjUgNzkuMzEzIDU4LjUgODMuODEzIi8+PHBvbHlnb24gZmlsbD0iI0Q4N0MzMCIgcG9pbnRzPSI0Ni4xMjUgMTAxLjI1IDkyLjI1IDEwMi4zNzUgODcuMTg4IDEyNiA2NS4yNSAxMjAuMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VBOEQzQSIgcG9pbnRzPSI0Ni4xMjUgMTAxLjgxMyA2NS4yNSAxMTkuODEzIDY1LjI1IDEzNy44MTMiLz48cG9seWdvbiBmaWxsPSIjRjg5RDM1IiBwb2ludHM9IjY1LjI1IDEyMC4zNzUgODcuNzUgMTI2IDk1LjA2MyAxNTAuMTg4IDkwIDE1MyA2NS4yNSAxMzguMzc1Ii8+PHBvbHlnb24gZmlsbD0iI0VCOEYzNSIgcG9pbnRzPSI2NS4yNSAxMzguMzc1IDYwLjc1IDE3My4yNSA5MCAxNTMiLz48cG9seWdvbiBmaWxsPSIjRUE4RTNBIiBwb2ludHM9IjkyLjI1IDEwMi4zNzUgOTUuMDYzIDE1MC4xODggODYuNjI1IDEyNS43MTkiLz48cG9seWdvbiBmaWxsPSIjRDg3QzMwIiBwb2ludHM9IjM5LjM3NSAxMzguOTM4IDY1LjI1IDEzOC4zNzUgNjAuNzUgMTczLjI1Ii8+PHBvbHlnb24gZmlsbD0iI0VCOEYzNSIgcG9pbnRzPSIxMi45MzggMTg4LjQzOCA2MC43NSAxNzMuMjUgMzkuMzc1IDEzOC45MzggLjU2MyAxNDEuNzUiLz48cG9seWdvbiBmaWxsPSIjRTg4MjFFIiBwb2ludHM9Ijg4Ljg3NSA1OC41IDY0LjY4OCA3OC43NSA0Ni4xMjUgMTAxLjI1IDkyLjI1IDEwMi45MzgiLz48cG9seWdvbiBmaWxsPSIjMzkzOTM5IiBwb2ludHM9IjcwLjMxMyAxMTIuNSA2NC4xMjUgMTI1LjQzOCA4Ni4wNjMgMTE5LjgxMyIgdHJhbnNmb3JtPSJtYXRyaXgoLTEgMCAwIDEgMTUwLjE4OCAwKSIvPjxwb2x5Z29uIGZpbGw9IiNFODhGMzUiIHBvaW50cz0iMTIuMzc1IC41NjMgODguODc1IDU4LjUgNzUuOTM4IDI3Ii8+PHBhdGggZmlsbD0iIzhFNUEzMCIgZD0iTTEyLjM3NTAwMDIsMC41NjI1MDAwMDggTDIuMjUwMDAwMDMsMzEuNTAwMDAwNSBMNy44NzUwMDAxMiw2NS4yNTAwMDEgTDMuOTM3NTAwMDYsNjcuNTAwMDAxIEw5LjU2MjUwMDE0LDcyLjU2MjUgTDUuMDYyNTAwMDgsNzYuNTAwMDAxMSBMMTEuMjUsODIuMTI1MDAxMiBMNy4zMTI1MDAxMSw4NS41MDAwMDEzIEwxNi4zMTI1MDAyLDk2Ljc1MDAwMTQgTDU4LjUwMDAwMDksODMuODEyNTAxMiBDNzkuMTI1MDAxMiw2Ny4zMTI1MDA0IDg5LjI1MDAwMTMsNTguODc1MDAwMyA4OC44NzUwMDEzLDU4LjUwMDAwMDkgQzg4LjUwMDAwMTMsNTguMTI1MDAwOSA2My4wMDAwMDA5LDM4LjgxMjUwMDYgMTIuMzc1MDAwMiwwLjU2MjUwMDAwOCBaIi8+PC9nPjwvZz48L3N2Zz4=`,
downloads: {
chrome:
"https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn",
firefox: "https://addons.mozilla.org/en-US/firefox/addon/ether-metamask/",
edge: "https://microsoftedge.microsoft.com/addons/detail/metamask/ejbalbakoplchlghecdalmeeeajnimhm?hl=en-US",
},
},
]

export default wallets
Loading