Revert "Mango Heroes NFT Integration"
This commit is contained in:
parent
0aa16f0a8d
commit
8f380933bb
|
@ -1 +0,0 @@
|
|||
custom: ['https://mangoheroes.com']
|
|
@ -1,151 +0,0 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { FunctionComponent } from 'react'
|
||||
import Modal from './Modal'
|
||||
import { ElementTitle } from './styles'
|
||||
import { Responsive, WidthProvider } from 'react-grid-layout'
|
||||
import _ from 'lodash'
|
||||
import Button from './Button'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import { notify } from '../utils/notifications'
|
||||
import { NFT } from '../utils/metaplex/models'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const ResponsiveGridLayout = WidthProvider(Responsive)
|
||||
|
||||
interface ChangeAvatarModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
currentAvatar: NFT
|
||||
}
|
||||
|
||||
const ChangeAvatarModal: FunctionComponent<ChangeAvatarModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
currentAvatar,
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const nfts = [...useMangoStore((s) => s.settings.nfts)]
|
||||
const currentIndex = nfts.indexOf(currentAvatar)
|
||||
if (currentIndex != -1) nfts.splice(currentIndex, 1)
|
||||
|
||||
const listOfNFTs = [currentAvatar, ...nfts]
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const set = useMangoStore((state) => state.set)
|
||||
const [layouts] = useState(
|
||||
listOfNFTs.map((nft, key) => {
|
||||
return {
|
||||
i: String(key),
|
||||
x: key % 3,
|
||||
y: Math.floor(key / 3),
|
||||
w: 1,
|
||||
h: 1,
|
||||
nft: nft,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
// Save selected profile picture
|
||||
const saveSelection = () => {
|
||||
const nftMintAddress = listOfNFTs[selectedIndex].mintAddress.toBase58()
|
||||
|
||||
set((state) => {
|
||||
state.settings.avatar = nftMintAddress
|
||||
})
|
||||
localStorage.setItem('profilePic', nftMintAddress)
|
||||
|
||||
notify({
|
||||
title: t('avatar-success'),
|
||||
})
|
||||
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<Modal.Header>
|
||||
<ElementTitle noMarignBottom>{t('change-avatar')}</ElementTitle>
|
||||
</Modal.Header>
|
||||
<div
|
||||
className="border border-th-bkg-4 bg-th-bkg-1"
|
||||
style={{ height: 300, width: '100%', overflowY: 'auto' }}
|
||||
>
|
||||
<ResponsiveGridLayout
|
||||
className="layout"
|
||||
cols={{ xl: 3, lg: 3, md: 3, sm: 3, xs: 1, xxs: 1 }}
|
||||
breakpoints={{ lg: 1200 }}
|
||||
rowHeight={120}
|
||||
isDraggable={false}
|
||||
isResizable={false}
|
||||
>
|
||||
{_.map(layouts, (layout) => {
|
||||
return (
|
||||
<div
|
||||
key={layout.i}
|
||||
data-grid={layout}
|
||||
className="hover:cursor-pointer"
|
||||
onClick={() => setSelectedIndex(+layout.i)}
|
||||
>
|
||||
<NFTDisplay
|
||||
nft={layout.nft}
|
||||
selected={selectedIndex === +layout.i}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</ResponsiveGridLayout>
|
||||
</div>
|
||||
<div className="align-middle flex justify-center">
|
||||
<Button
|
||||
disabled={selectedIndex === 0}
|
||||
className="mt-4"
|
||||
onClick={saveSelection}
|
||||
>
|
||||
<div className="flex items-center justify-center">{t('okay')}</div>
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
const NFTDisplay = ({ nft, selected }) => {
|
||||
const [imageUri, setImageUri] = useState()
|
||||
|
||||
useEffect(() => {
|
||||
if (nft.imageUri) {
|
||||
setImageUri(nft.imageUri)
|
||||
} else {
|
||||
try {
|
||||
fetch(nft.metadataUri).then(async (_) => {
|
||||
try {
|
||||
const data = await _.json()
|
||||
nft.imageUri = data['image']
|
||||
setImageUri(nft.imageUri)
|
||||
} catch (ex) {
|
||||
console.error('Error trying to parse JSON: ' + ex)
|
||||
}
|
||||
})
|
||||
} catch (ex) {
|
||||
console.error('Error trying to fetch metadata: ' + ex)
|
||||
}
|
||||
}
|
||||
}, [imageUri])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`hover:scale-110 ${
|
||||
nft.imageUri == undefined
|
||||
? 'bg-th-bkg-4 h-full w-full rounded-lg animate-pulse'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
className={`border ${
|
||||
selected ? 'border-th-primary' : 'border-th-bkg-4'
|
||||
} h-full w-full rounded-lg`}
|
||||
src={nft.imageUri}
|
||||
></img>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(ChangeAvatarModal)
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useCallback, useState } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import { Menu } from '@headlessui/react'
|
||||
import {
|
||||
|
@ -14,72 +14,29 @@ import {
|
|||
import useLocalStorageState from '../hooks/useLocalStorageState'
|
||||
import { abbreviateAddress, copyToClipboard } from '../utils'
|
||||
import WalletSelect from './WalletSelect'
|
||||
import { ProfileIcon, WalletIcon } from './icons'
|
||||
import { WalletIcon, ProfileIcon } from './icons'
|
||||
import AccountsModal from './AccountsModal'
|
||||
import { useEffect } from 'react'
|
||||
import SettingsModal from './SettingsModal'
|
||||
import { UserCircleIcon } from '@heroicons/react/solid'
|
||||
import ChangeAvatarModal from './ChangeAvatarModal'
|
||||
import { NFT } from '../utils/metaplex/models'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const ConnectWalletButton = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const wallet = useMangoStore((s) => s.wallet.current)
|
||||
const connected = useMangoStore((s) => s.wallet.connected)
|
||||
const nfts = useMangoStore((s) => s.settings.nfts)
|
||||
const profilePicMintAddress = useMangoStore((s) => s.settings.avatar)
|
||||
const set = useMangoStore((s) => s.set)
|
||||
const [showAccountsModal, setShowAccountsModal] = useState(false)
|
||||
const [showSettingsModal, setShowSettingsModal] = useState(false)
|
||||
const [showChangeAvatarModal, setChangeAvatarModal] = useState(false)
|
||||
const [selectedWallet, setSelectedWallet] = useState(DEFAULT_PROVIDER.url)
|
||||
const [profilePicture, setProfilePicture] = useState(new NFT())
|
||||
const [imageUrl, setImageUrl] = useState('')
|
||||
const [savedProviderUrl] = useLocalStorageState(
|
||||
PROVIDER_LOCAL_STORAGE_KEY,
|
||||
DEFAULT_PROVIDER.url
|
||||
)
|
||||
|
||||
// update in useEffect to prevent SRR error from next.js
|
||||
useEffect(() => {
|
||||
setSelectedWallet(savedProviderUrl)
|
||||
}, [savedProviderUrl])
|
||||
|
||||
useEffect(() => {
|
||||
if (!profilePicMintAddress && nfts.length == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// Verify this wallet still has NFT
|
||||
const profilePics = nfts.filter(
|
||||
(nft) => nft.mintAddress.toBase58() == profilePicMintAddress
|
||||
)
|
||||
const nft = profilePics.length == 1 ? profilePics[0] : nfts[0]
|
||||
|
||||
if (nft.imageUri) {
|
||||
setImageUrl(nft.imageUri)
|
||||
} else {
|
||||
try {
|
||||
fetch(nft.metadataUri).then(async (_) => {
|
||||
try {
|
||||
const data = await _.json()
|
||||
console.log('Metadata: ', data)
|
||||
|
||||
nft.imageUri = data['image']
|
||||
setImageUrl(nft.imageUri)
|
||||
} catch (ex) {
|
||||
console.error('Error trying to parse JSON: ' + ex)
|
||||
}
|
||||
})
|
||||
} catch (ex) {
|
||||
console.error('Error trying to fetch metadata: ' + ex)
|
||||
}
|
||||
}
|
||||
|
||||
setProfilePicture(nft)
|
||||
}, [nfts, profilePicMintAddress])
|
||||
|
||||
const handleWalletConnect = () => {
|
||||
const handleWalletConect = () => {
|
||||
wallet.connect()
|
||||
set((state) => {
|
||||
state.selectedMangoAccount.initialLoad = true
|
||||
|
@ -90,28 +47,13 @@ const ConnectWalletButton = () => {
|
|||
setShowAccountsModal(false)
|
||||
}, [])
|
||||
|
||||
const disconnectWallet = () => {
|
||||
setImageUrl('')
|
||||
wallet.disconnect()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{connected && wallet?.publicKey ? (
|
||||
<Menu>
|
||||
<div className="relative" id="profile-menu-tip">
|
||||
<Menu.Button
|
||||
className="bg-th-bkg-4 flex items-center justify-center rounded-full w-10 h-10 text-white focus:outline-none hover:bg-th-bkg-4 hover:text-th-fgd-3"
|
||||
style={
|
||||
imageUrl != ''
|
||||
? {
|
||||
backgroundImage: `url(${imageUrl})`,
|
||||
backgroundSize: 'cover',
|
||||
}
|
||||
: null
|
||||
}
|
||||
>
|
||||
{imageUrl == '' ? <ProfileIcon className="h-6 w-6" /> : null}
|
||||
<Menu.Button className="bg-th-bkg-4 flex items-center justify-center rounded-full w-10 h-10 text-white focus:outline-none hover:bg-th-bkg-4 hover:text-th-fgd-3">
|
||||
<ProfileIcon className="h-6 w-6" />
|
||||
</Menu.Button>
|
||||
<Menu.Items className="bg-th-bkg-1 mt-2 p-1 absolute right-0 shadow-lg outline-none rounded-md w-48 z-20">
|
||||
<Menu.Item>
|
||||
|
@ -123,17 +65,6 @@ const ConnectWalletButton = () => {
|
|||
<div className="pl-2 text-left">{t('accounts')}</div>
|
||||
</button>
|
||||
</Menu.Item>
|
||||
{imageUrl != '' ? (
|
||||
<Menu.Item>
|
||||
<button
|
||||
className="flex flex-row font-normal items-center rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none"
|
||||
onClick={() => setChangeAvatarModal(true)}
|
||||
>
|
||||
<UserCircleIcon className="h-4 w-4" />
|
||||
<div className="pl-2 text-left">{t('change-avatar')}</div>
|
||||
</button>
|
||||
</Menu.Item>
|
||||
) : null}
|
||||
<Menu.Item>
|
||||
<button
|
||||
className="flex flex-row font-normal items-center rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none"
|
||||
|
@ -146,7 +77,7 @@ const ConnectWalletButton = () => {
|
|||
<Menu.Item>
|
||||
<button
|
||||
className="flex flex-row font-normal items-center rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none"
|
||||
onClick={() => disconnectWallet()}
|
||||
onClick={() => wallet.disconnect()}
|
||||
>
|
||||
<LogoutIcon className="h-4 w-4" />
|
||||
<div className="pl-2 text-left">
|
||||
|
@ -166,7 +97,7 @@ const ConnectWalletButton = () => {
|
|||
id="connect-wallet-tip"
|
||||
>
|
||||
<button
|
||||
onClick={handleWalletConnect}
|
||||
onClick={handleWalletConect}
|
||||
disabled={!wallet}
|
||||
className="rounded-none text-th-primary hover:bg-th-bkg-4 focus:outline-none disabled:text-th-fgd-4 disabled:cursor-wait"
|
||||
>
|
||||
|
@ -191,19 +122,6 @@ const ConnectWalletButton = () => {
|
|||
isOpen={showAccountsModal}
|
||||
/>
|
||||
) : null}
|
||||
{showSettingsModal ? (
|
||||
<SettingsModal
|
||||
onClose={() => setShowSettingsModal(false)}
|
||||
isOpen={showSettingsModal}
|
||||
/>
|
||||
) : null}
|
||||
{showChangeAvatarModal ? (
|
||||
<ChangeAvatarModal
|
||||
onClose={() => setChangeAvatarModal(false)}
|
||||
isOpen={showChangeAvatarModal}
|
||||
currentAvatar={profilePicture}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ export default function useWallet() {
|
|||
actions.reloadOrders()
|
||||
actions.fetchTradeHistory()
|
||||
actions.fetchWalletTokens()
|
||||
actions.fetchMangoHeroesNFTs()
|
||||
|
||||
// notify({
|
||||
// title: t('wallet-connected'),
|
||||
// description:
|
||||
|
@ -135,13 +135,10 @@ export default function useWallet() {
|
|||
})
|
||||
wallet.on('disconnect', () => {
|
||||
console.log('disconnecting wallet')
|
||||
|
||||
setMangoStore((state) => {
|
||||
state.wallet.connected = false
|
||||
state.mangoAccounts = []
|
||||
state.selectedMangoAccount.current = null
|
||||
state.settings.nfts = []
|
||||
state.settings.avatar = ''
|
||||
state.tradeHistory = []
|
||||
})
|
||||
notify({
|
||||
|
@ -154,7 +151,6 @@ export default function useWallet() {
|
|||
useInterval(() => {
|
||||
if (connected && mangoAccount) {
|
||||
actions.fetchWalletTokens()
|
||||
actions.fetchMangoHeroesNFTs()
|
||||
actions.fetchTradeHistory()
|
||||
}
|
||||
}, 90 * SECONDS)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -35,7 +35,6 @@
|
|||
"@tippyjs/react": "^4.2.5",
|
||||
"big.js": "^6.1.1",
|
||||
"bn.js": "^5.2.0",
|
||||
"borsh": "^0.6.0",
|
||||
"bs58": "^4.0.1",
|
||||
"buffer-layout": "^1.2.0",
|
||||
"dayjs": "^1.10.4",
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"assets": "Assets",
|
||||
"assets-liabilities": "Assets & Liabilities",
|
||||
"available-balance": "Available Balance",
|
||||
"avatar-success": "Avatar changed successfully",
|
||||
"average-borrow": "Average Borrow Rates",
|
||||
"average-deposit": "Average Deposit Rates",
|
||||
"average-entry": "Avg Entry Price",
|
||||
|
@ -44,7 +43,6 @@
|
|||
"cancel-error": "Error cancelling order",
|
||||
"cancel-success": "Successfully cancelled order",
|
||||
"change-account": "Change Account",
|
||||
"change-avatar": "Change Avatar",
|
||||
"change-language": "Change Language",
|
||||
"change-theme": "Change Theme",
|
||||
"character-limit": "Account name must be 32 characters or less",
|
||||
|
@ -210,7 +208,6 @@
|
|||
"not-enough-balance": "Insufficient wallet balance",
|
||||
"not-enough-sol": "You may not have enough SOL for this transaction",
|
||||
"notional-size": "Notional Size",
|
||||
"okay": "Okay",
|
||||
"open-interest": "Open Interest",
|
||||
"open-orders": "Open Orders",
|
||||
"optional": "(Optional)",
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 49 KiB |
Binary file not shown.
Before Width: | Height: | Size: 57 KiB |
Binary file not shown.
Before Width: | Height: | Size: 189 KiB |
|
@ -32,11 +32,6 @@ import {
|
|||
initialMarket,
|
||||
NODE_URL_KEY,
|
||||
} from '../components/SettingsModal'
|
||||
import { TOKEN_PROGRAM_ID } from '../utils/tokens'
|
||||
import { findProgramAddress } from '../utils/metaplex/utils'
|
||||
import * as borsh from 'borsh'
|
||||
import { Metadata, METADATA_SCHEMA, NFT } from '../utils/metaplex/models'
|
||||
import { METADATA_KEY, METADATA_PREFIX } from '../utils/metaplex/types'
|
||||
import { MSRM_DECIMALS } from '@project-serum/serum/lib/token-instructions'
|
||||
|
||||
export const ENDPOINTS: EndpointInfo[] = [
|
||||
|
@ -174,8 +169,6 @@ interface MangoStore extends State {
|
|||
}
|
||||
settings: {
|
||||
uiLocked: boolean
|
||||
nfts: NFT[]
|
||||
avatar: string
|
||||
}
|
||||
tradeHistory: any[]
|
||||
set: (x: any) => void
|
||||
|
@ -248,8 +241,6 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
wallet: INITIAL_STATE.WALLET,
|
||||
settings: {
|
||||
uiLocked: true,
|
||||
nfts: [],
|
||||
avatar: '',
|
||||
},
|
||||
tradeHistory: [],
|
||||
set: (fn) => set(produce(fn)),
|
||||
|
@ -297,72 +288,6 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
})
|
||||
}
|
||||
},
|
||||
async fetchMangoHeroesNFTs() {
|
||||
const wallet = get().wallet.current
|
||||
const connected = get().wallet.connected
|
||||
const connection = get().connection.current
|
||||
const set = get().set
|
||||
|
||||
if (wallet?.publicKey && connected) {
|
||||
const tokenAccounts = await connection.getParsedTokenAccountsByOwner(
|
||||
wallet.publicKey,
|
||||
{ programId: TOKEN_PROGRAM_ID }
|
||||
)
|
||||
|
||||
const walletNFTs = []
|
||||
|
||||
tokenAccounts.value.forEach((token) => {
|
||||
const tokenAccount = token.account.data.parsed.info
|
||||
|
||||
if (
|
||||
parseFloat(tokenAccount.tokenAmount.amount) == 1 &&
|
||||
tokenAccount.tokenAmount.decimals == 0
|
||||
) {
|
||||
const nft = new NFT()
|
||||
nft.mintAddress = new PublicKey(tokenAccount.mint)
|
||||
walletNFTs.push(nft)
|
||||
}
|
||||
})
|
||||
|
||||
if (walletNFTs.length == 0) return
|
||||
|
||||
const metadataProgramId = new PublicKey(METADATA_KEY)
|
||||
|
||||
const nfts = []
|
||||
|
||||
for (const nft of walletNFTs) {
|
||||
// The return value is [programDerivedAddress, bytes] but we only care about the address
|
||||
|
||||
const [pda] = await findProgramAddress(
|
||||
[
|
||||
Buffer.from(METADATA_PREFIX),
|
||||
metadataProgramId.toBuffer(),
|
||||
nft.mintAddress.toBuffer(),
|
||||
],
|
||||
metadataProgramId
|
||||
)
|
||||
|
||||
const accountInfo = await connection.getAccountInfo(
|
||||
pda,
|
||||
'processed'
|
||||
)
|
||||
const metadata = borsh.deserializeUnchecked(
|
||||
METADATA_SCHEMA,
|
||||
Metadata,
|
||||
accountInfo!.data
|
||||
)
|
||||
|
||||
const uri = metadata.data.uri.replace(/\0/g, '')
|
||||
nft.metadataUri = uri
|
||||
nfts.push(nft)
|
||||
}
|
||||
|
||||
set((state) => {
|
||||
;(state.settings.nfts = nfts),
|
||||
(state.settings.avatar = localStorage.getItem('profilePic'))
|
||||
})
|
||||
}
|
||||
},
|
||||
async fetchAllMangoAccounts() {
|
||||
const set = get().set
|
||||
const mangoGroup = get().selectedMangoGroup.current
|
||||
|
|
|
@ -1,334 +0,0 @@
|
|||
/*
|
||||
Taken from: https://github.com/metaplex-foundation/metaplex/blob/master/js/packages/common/src/actions/metadata.ts
|
||||
*/
|
||||
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import BN from 'bn.js'
|
||||
import { BinaryReader, BinaryWriter } from 'borsh'
|
||||
import {
|
||||
StringPublicKey,
|
||||
EDITION_MARKER_BIT_SIZE,
|
||||
MetadataKey,
|
||||
FileOrString,
|
||||
MetadataCategory,
|
||||
MetaplexKey,
|
||||
} from './types'
|
||||
import base58 from 'bs58'
|
||||
|
||||
export class MasterEditionV1 {
|
||||
key: MetadataKey
|
||||
supply: BN
|
||||
maxSupply?: BN
|
||||
/// Can be used to mint tokens that give one-time permission to mint a single limited edition.
|
||||
printingMint: StringPublicKey
|
||||
/// If you don't know how many printing tokens you are going to need, but you do know
|
||||
/// you are going to need some amount in the future, you can use a token from this mint.
|
||||
/// Coming back to token metadata with one of these tokens allows you to mint (one time)
|
||||
/// any number of printing tokens you want. This is used for instance by Auction Manager
|
||||
/// with participation NFTs, where we dont know how many people will bid and need participation
|
||||
/// printing tokens to redeem, so we give it ONE of these tokens to use after the auction is over,
|
||||
/// because when the auction begins we just dont know how many printing tokens we will need,
|
||||
/// but at the end we will. At the end it then burns this token with token-metadata to
|
||||
/// get the printing tokens it needs to give to bidders. Each bidder then redeems a printing token
|
||||
/// to get their limited editions.
|
||||
oneTimePrintingAuthorizationMint: StringPublicKey
|
||||
|
||||
constructor(args: {
|
||||
key: MetadataKey
|
||||
supply: BN
|
||||
maxSupply?: BN
|
||||
printingMint: StringPublicKey
|
||||
oneTimePrintingAuthorizationMint: StringPublicKey
|
||||
}) {
|
||||
this.key = MetadataKey.MasterEditionV1
|
||||
this.supply = args.supply
|
||||
this.maxSupply = args.maxSupply
|
||||
this.printingMint = args.printingMint
|
||||
this.oneTimePrintingAuthorizationMint =
|
||||
args.oneTimePrintingAuthorizationMint
|
||||
}
|
||||
}
|
||||
|
||||
export class MasterEditionV2 {
|
||||
key: MetadataKey
|
||||
supply: BN
|
||||
maxSupply?: BN
|
||||
|
||||
constructor(args: { key: MetadataKey; supply: BN; maxSupply?: BN }) {
|
||||
this.key = MetadataKey.MasterEditionV2
|
||||
this.supply = args.supply
|
||||
this.maxSupply = args.maxSupply
|
||||
}
|
||||
}
|
||||
|
||||
export class EditionMarker {
|
||||
key: MetadataKey
|
||||
ledger: number[]
|
||||
|
||||
constructor(args: { key: MetadataKey; ledger: number[] }) {
|
||||
this.key = MetadataKey.EditionMarker
|
||||
this.ledger = args.ledger
|
||||
}
|
||||
|
||||
editionTaken(edition: number) {
|
||||
const editionOffset = edition % EDITION_MARKER_BIT_SIZE
|
||||
const indexOffset = Math.floor(editionOffset / 8)
|
||||
|
||||
if (indexOffset > 30) {
|
||||
throw Error('bad index for edition')
|
||||
}
|
||||
|
||||
const positionInBitsetFromRight = 7 - (editionOffset % 8)
|
||||
|
||||
const mask = Math.pow(2, positionInBitsetFromRight)
|
||||
|
||||
const appliedMask = this.ledger[indexOffset] & mask
|
||||
|
||||
return appliedMask !== 0
|
||||
}
|
||||
}
|
||||
|
||||
export class Edition {
|
||||
key: MetadataKey
|
||||
/// Points at MasterEdition struct
|
||||
parent: StringPublicKey
|
||||
/// Starting at 0 for master record, this is incremented for each edition minted.
|
||||
edition: BN
|
||||
|
||||
constructor(args: {
|
||||
key: MetadataKey
|
||||
parent: StringPublicKey
|
||||
edition: BN
|
||||
}) {
|
||||
this.key = MetadataKey.EditionV1
|
||||
this.parent = args.parent
|
||||
this.edition = args.edition
|
||||
}
|
||||
}
|
||||
export class Creator {
|
||||
address: StringPublicKey
|
||||
verified: boolean
|
||||
share: number
|
||||
|
||||
constructor(args: {
|
||||
address: StringPublicKey
|
||||
verified: boolean
|
||||
share: number
|
||||
}) {
|
||||
this.address = args.address
|
||||
this.verified = args.verified
|
||||
this.share = args.share
|
||||
}
|
||||
}
|
||||
|
||||
export class Data {
|
||||
name: string
|
||||
symbol: string
|
||||
uri: string
|
||||
sellerFeeBasisPoints: number
|
||||
creators: Creator[] | null
|
||||
constructor(args: {
|
||||
name: string
|
||||
symbol: string
|
||||
uri: string
|
||||
sellerFeeBasisPoints: number
|
||||
creators: Creator[] | null
|
||||
}) {
|
||||
this.name = args.name
|
||||
this.symbol = args.symbol
|
||||
this.uri = args.uri
|
||||
this.sellerFeeBasisPoints = args.sellerFeeBasisPoints
|
||||
this.creators = args.creators
|
||||
}
|
||||
}
|
||||
|
||||
export class Metadata {
|
||||
key: MetadataKey
|
||||
updateAuthority: StringPublicKey
|
||||
mint: StringPublicKey
|
||||
data: Data
|
||||
primarySaleHappened: boolean
|
||||
isMutable: boolean
|
||||
editionNonce: number | null
|
||||
|
||||
constructor(args: {
|
||||
updateAuthority: StringPublicKey
|
||||
mint: StringPublicKey
|
||||
data: Data
|
||||
primarySaleHappened: boolean
|
||||
isMutable: boolean
|
||||
editionNonce: number | null
|
||||
}) {
|
||||
this.key = MetadataKey.MetadataV1
|
||||
this.updateAuthority = args.updateAuthority
|
||||
this.mint = args.mint
|
||||
this.data = args.data
|
||||
this.primarySaleHappened = args.primarySaleHappened
|
||||
this.isMutable = args.isMutable
|
||||
this.editionNonce = args.editionNonce
|
||||
}
|
||||
}
|
||||
|
||||
export interface IMetadataExtension {
|
||||
name: string
|
||||
symbol: string
|
||||
|
||||
creators: Creator[] | null
|
||||
description: string
|
||||
// preview image absolute URI
|
||||
image: string
|
||||
animation_url?: string
|
||||
|
||||
// stores link to item on meta
|
||||
external_url: string
|
||||
|
||||
seller_fee_basis_points: number
|
||||
|
||||
properties: {
|
||||
files?: FileOrString[]
|
||||
category: MetadataCategory
|
||||
maxSupply?: number
|
||||
creators?: {
|
||||
address: string
|
||||
shares: number
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
export const METADATA_SCHEMA = new Map<any, any>([
|
||||
[
|
||||
MasterEditionV1,
|
||||
{
|
||||
kind: 'struct',
|
||||
fields: [
|
||||
['key', 'u8'],
|
||||
['supply', 'u64'],
|
||||
['maxSupply', { kind: 'option', type: 'u64' }],
|
||||
['printingMint', 'pubkeyAsString'],
|
||||
['oneTimePrintingAuthorizationMint', 'pubkeyAsString'],
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
MasterEditionV2,
|
||||
{
|
||||
kind: 'struct',
|
||||
fields: [
|
||||
['key', 'u8'],
|
||||
['supply', 'u64'],
|
||||
['maxSupply', { kind: 'option', type: 'u64' }],
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
Edition,
|
||||
{
|
||||
kind: 'struct',
|
||||
fields: [
|
||||
['key', 'u8'],
|
||||
['parent', 'pubkeyAsString'],
|
||||
['edition', 'u64'],
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
Data,
|
||||
{
|
||||
kind: 'struct',
|
||||
fields: [
|
||||
['name', 'string'],
|
||||
['symbol', 'string'],
|
||||
['uri', 'string'],
|
||||
['sellerFeeBasisPoints', 'u16'],
|
||||
['creators', { kind: 'option', type: [Creator] }],
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
Creator,
|
||||
{
|
||||
kind: 'struct',
|
||||
fields: [
|
||||
['address', 'pubkeyAsString'],
|
||||
['verified', 'u8'],
|
||||
['share', 'u8'],
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
Metadata,
|
||||
{
|
||||
kind: 'struct',
|
||||
fields: [
|
||||
['key', 'u8'],
|
||||
['updateAuthority', 'pubkeyAsString'],
|
||||
['mint', 'pubkeyAsString'],
|
||||
['data', Data],
|
||||
['primarySaleHappened', 'u8'], // bool
|
||||
['isMutable', 'u8'], // bool
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
EditionMarker,
|
||||
{
|
||||
kind: 'struct',
|
||||
fields: [
|
||||
['key', 'u8'],
|
||||
['ledger', [31]],
|
||||
],
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
export class WhitelistedCreator {
|
||||
key = MetaplexKey.WhitelistedCreatorV1
|
||||
address: StringPublicKey
|
||||
activated = true
|
||||
|
||||
// Populated from name service
|
||||
twitter?: string
|
||||
name?: string
|
||||
image?: string
|
||||
description?: string
|
||||
|
||||
constructor(args: { address: string; activated: boolean }) {
|
||||
this.address = args.address
|
||||
this.activated = args.activated
|
||||
}
|
||||
}
|
||||
|
||||
export class NFT {
|
||||
mintAddress: PublicKey
|
||||
imageUri: string
|
||||
metadataUri: string
|
||||
}
|
||||
|
||||
// Required to properly serialize and deserialize pubKeyAsString types
|
||||
const extendBorsh = () => {
|
||||
;(BinaryReader.prototype as any).readPubkey = function () {
|
||||
const reader = this as unknown as BinaryReader
|
||||
const array = reader.readFixedArray(32)
|
||||
return new PublicKey(array)
|
||||
}
|
||||
|
||||
;(BinaryWriter.prototype as any).writePubkey = function (value: any) {
|
||||
const writer = this as unknown as BinaryWriter
|
||||
writer.writeFixedArray(value.toBuffer())
|
||||
}
|
||||
|
||||
;(BinaryReader.prototype as any).readPubkeyAsString = function () {
|
||||
const reader = this as unknown as BinaryReader
|
||||
const array = reader.readFixedArray(32)
|
||||
return base58.encode(array) as StringPublicKey
|
||||
}
|
||||
|
||||
;(BinaryWriter.prototype as any).writePubkeyAsString = function (
|
||||
value: StringPublicKey
|
||||
) {
|
||||
const writer = this as unknown as BinaryWriter
|
||||
writer.writeFixedArray(base58.decode(value))
|
||||
}
|
||||
}
|
||||
|
||||
extendBorsh()
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
Taken from: https://github.com/metaplex-foundation/metaplex/blob/master/js/packages/common/src/actions/metadata.ts
|
||||
*/
|
||||
|
||||
export type StringPublicKey = string
|
||||
|
||||
export const EDITION = 'edition'
|
||||
export const METADATA_PREFIX = 'metadata'
|
||||
export const METADATA_KEY = 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'
|
||||
export const MANGO_HEROES_MINT_AUTHORITY =
|
||||
'3RVBxNcF1LBAVbxPz1KsueWspASJhqz4ZJGDbxy2mKE3'
|
||||
|
||||
export const MAX_AUCTION_DATA_EXTENDED_SIZE = 8 + 9 + 2 + 200
|
||||
|
||||
export const MAX_NAME_LENGTH = 32
|
||||
export const MAX_SYMBOL_LENGTH = 10
|
||||
export const MAX_URI_LENGTH = 200
|
||||
export const MAX_CREATOR_LIMIT = 5
|
||||
export const EDITION_MARKER_BIT_SIZE = 248
|
||||
export const MAX_CREATOR_LEN = 32 + 1 + 1
|
||||
export const MAX_METADATA_LEN =
|
||||
1 +
|
||||
32 +
|
||||
32 +
|
||||
MAX_NAME_LENGTH +
|
||||
MAX_SYMBOL_LENGTH +
|
||||
MAX_URI_LENGTH +
|
||||
MAX_CREATOR_LIMIT * MAX_CREATOR_LEN +
|
||||
2 +
|
||||
1 +
|
||||
1 +
|
||||
198
|
||||
|
||||
export enum MetadataKey {
|
||||
Uninitialized = 0,
|
||||
MetadataV1 = 4,
|
||||
EditionV1 = 1,
|
||||
MasterEditionV1 = 2,
|
||||
MasterEditionV2 = 6,
|
||||
EditionMarker = 7,
|
||||
}
|
||||
|
||||
export enum MetadataCategory {
|
||||
Audio = 'audio',
|
||||
Video = 'video',
|
||||
Image = 'image',
|
||||
VR = 'vr',
|
||||
HTML = 'html',
|
||||
}
|
||||
|
||||
export type MetadataFile = {
|
||||
uri: string
|
||||
type: string
|
||||
}
|
||||
|
||||
export type FileOrString = MetadataFile | string
|
||||
|
||||
export interface Auction {
|
||||
name: string
|
||||
auctionerName: string
|
||||
auctionerLink: string
|
||||
highestBid: number
|
||||
solAmt: number
|
||||
link: string
|
||||
image: string
|
||||
}
|
||||
|
||||
export interface Artist {
|
||||
address?: string
|
||||
name: string
|
||||
link: string
|
||||
image: string
|
||||
itemsAvailable?: number
|
||||
itemsSold?: number
|
||||
about?: string
|
||||
verified?: boolean
|
||||
|
||||
share?: number
|
||||
}
|
||||
|
||||
export enum ArtType {
|
||||
Master,
|
||||
Print,
|
||||
NFT,
|
||||
}
|
||||
export interface Art {
|
||||
url: string
|
||||
}
|
||||
|
||||
export enum MetaplexKey {
|
||||
Uninitialized = 0,
|
||||
OriginalAuthorityLookupV1 = 1,
|
||||
BidRedemptionTicketV1 = 2,
|
||||
StoreV1 = 3,
|
||||
WhitelistedCreatorV1 = 4,
|
||||
PayoutTicketV1 = 5,
|
||||
SafetyDepositValidationTicketV1 = 6,
|
||||
AuctionManagerV1 = 7,
|
||||
PrizeTrackingTicketV1 = 8,
|
||||
SafetyDepositConfigV1 = 9,
|
||||
AuctionManagerV2 = 10,
|
||||
BidRedemptionTicketV2 = 11,
|
||||
AuctionWinnerTokenTypeTrackerV1 = 12,
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import { PublicKey } from '@solana/web3.js'
|
||||
|
||||
export const findProgramAddress = async (
|
||||
seeds: (Buffer | Uint8Array)[],
|
||||
programId: PublicKey
|
||||
) => {
|
||||
const key =
|
||||
'pda-' +
|
||||
seeds.reduce((agg, item) => agg + item.toString('hex'), '') +
|
||||
programId.toString()
|
||||
const cached = localStorage.getItem(key)
|
||||
if (cached) {
|
||||
const value = JSON.parse(cached)
|
||||
|
||||
return [new PublicKey(value.key), parseInt(value.nonce)] as [
|
||||
PublicKey,
|
||||
number
|
||||
]
|
||||
}
|
||||
|
||||
const result = await PublicKey.findProgramAddress(seeds, programId)
|
||||
|
||||
return [result[0], result[1]] as [PublicKey, number]
|
||||
}
|
16
yarn.lock
16
yarn.lock
|
@ -2483,11 +2483,16 @@ bindings@^1.3.0:
|
|||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bn.js@5.1.3, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9, bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.2.0:
|
||||
bn.js@5.1.3, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9, bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.1, bn.js@^5.1.2:
|
||||
version "5.1.3"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b"
|
||||
integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==
|
||||
|
||||
bn.js@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002"
|
||||
integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==
|
||||
|
||||
boolbase@^1.0.0, boolbase@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
|
@ -2503,15 +2508,6 @@ borsh@^0.4.0:
|
|||
bs58 "^4.0.0"
|
||||
text-encoding-utf-8 "^1.0.2"
|
||||
|
||||
borsh@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.6.0.tgz#a7c9eeca6a31ca9e0607cb49f329cb659eb791e1"
|
||||
integrity sha512-sl5k89ViqsThXQpYa9XDtz1sBl3l1lI313cFUY1HKr+wvMILnb+58xpkqTNrYbelh99dY7K8usxoCusQmqix9Q==
|
||||
dependencies:
|
||||
bn.js "^5.2.0"
|
||||
bs58 "^4.0.0"
|
||||
text-encoding-utf-8 "^1.0.2"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
|
|
Loading…
Reference in New Issue