mirror of https://github.com/certusone/oyster.git
Merge branch 'feature/m' into main
This commit is contained in:
commit
e316021029
|
@ -0,0 +1 @@
|
||||||
|
REACT_APP_CLIENT_ID=BKBTX-SmaEFGddZQrwqd65YFoImRQLca_Tj2IdmKyD2UbDpzrtN2WQ-NYLuej6gP0DfF3jSpEkI13wPt1uPedm0
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="226.000000pt" height="248.000000pt" viewBox="0 0 226.000000 248.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.16, written by Peter Selinger 2001-2019
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,248.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M482 2143 c-43 -21 -79 -64 -92 -111 -13 -48 -13 -246 0 -294 29
|
||||||
|
-103 92 -128 322 -128 l158 0 0 -588 0 -589 23 -43 c16 -30 37 -51 67 -67 39
|
||||||
|
-21 56 -23 182 -23 94 0 149 4 171 14 43 17 95 78 102 118 3 18 5 389 3 825
|
||||||
|
l-3 791 -31 39 c-59 74 -52 73 -484 73 -334 0 -389 -3 -418 -17z"/>
|
||||||
|
<path d="M1743 2144 c-57 -21 -126 -84 -154 -141 -35 -73 -32 -171 6 -241 54
|
||||||
|
-100 132 -146 245 -146 115 0 197 50 246 151 108 220 -110 459 -343 377z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 957 B |
|
@ -21,11 +21,14 @@ export const CurrentUserBadge = (props: { showBalance?: boolean, showAddress?: b
|
||||||
{
|
{
|
||||||
marginLeft: '0.5rem',
|
marginLeft: '0.5rem',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: props.iconSize
|
width: props.iconSize,
|
||||||
|
borderRadius: 50,
|
||||||
|
|
||||||
} :{
|
} :{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
width: props.iconSize,
|
width: props.iconSize,
|
||||||
paddingLeft: 0,
|
paddingLeft: 0,
|
||||||
|
borderRadius: 50,
|
||||||
};
|
};
|
||||||
|
|
||||||
const baseWalletKey: React.CSSProperties = { height: props.iconSize, cursor: 'pointer', userSelect: 'none' };
|
const baseWalletKey: React.CSSProperties = { height: props.iconSize, cursor: 'pointer', userSelect: 'none' };
|
||||||
|
@ -33,6 +36,21 @@ export const CurrentUserBadge = (props: { showBalance?: boolean, showAddress?: b
|
||||||
baseWalletKey
|
baseWalletKey
|
||||||
:{ ...baseWalletKey, paddingLeft: 0 };
|
:{ ...baseWalletKey, paddingLeft: 0 };
|
||||||
|
|
||||||
|
let name = props.showAddress ? shortenAddress(`${wallet.publicKey}`) : '';
|
||||||
|
const unknownWallet = wallet as any;
|
||||||
|
if(unknownWallet.name) {
|
||||||
|
name = unknownWallet.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
let image = <Identicon
|
||||||
|
address={wallet.publicKey?.toBase58()}
|
||||||
|
style={iconStyle}
|
||||||
|
/>;
|
||||||
|
|
||||||
|
if(unknownWallet.image) {
|
||||||
|
image = <img src={unknownWallet.image} style={iconStyle} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="wallet-wrapper">
|
<div className="wallet-wrapper">
|
||||||
{props.showBalance && <span>
|
{props.showBalance && <span>
|
||||||
|
@ -46,11 +64,8 @@ export const CurrentUserBadge = (props: { showBalance?: boolean, showAddress?: b
|
||||||
trigger="click"
|
trigger="click"
|
||||||
>
|
>
|
||||||
<div className="wallet-key" style={walletKeyStyle}>
|
<div className="wallet-key" style={walletKeyStyle}>
|
||||||
{props.showAddress && shortenAddress(`${wallet.publicKey}`)}
|
{name && (<span style={{ marginRight: '0.5rem' }}>{name}</span>)}
|
||||||
<Identicon
|
{image}
|
||||||
address={wallet.publicKey?.toBase58()}
|
|
||||||
style={iconStyle}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -21,16 +21,16 @@ export const Identicon = (props: {
|
||||||
if (address && ref.current) {
|
if (address && ref.current) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
ref.current.innerHTML = '';
|
ref.current.innerHTML = '';
|
||||||
ref.current.className = className || '';
|
ref.current.className = className || '';
|
||||||
ref.current.appendChild(
|
ref.current.appendChild(
|
||||||
Jazzicon(
|
Jazzicon(
|
||||||
style?.width || 16,
|
style?.width || 16,
|
||||||
parseInt(bs58.decode(address).toString('hex').slice(5, 15), 16),
|
parseInt(bs58.decode(address).toString('hex').slice(5, 15), 16),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
}catch (err) {
|
} catch (err) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,17 @@ import React from 'react';
|
||||||
import { Button, Select } from 'antd';
|
import { Button, Select } from 'antd';
|
||||||
import { useWallet } from '../../contexts/wallet';
|
import { useWallet } from '../../contexts/wallet';
|
||||||
import { ENDPOINTS, useConnectionConfig } from '../../contexts/connection';
|
import { ENDPOINTS, useConnectionConfig } from '../../contexts/connection';
|
||||||
|
import { shortenAddress } from '../../utils';
|
||||||
|
import {
|
||||||
|
CopyOutlined
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
export const Settings = ({
|
export const Settings = ({
|
||||||
additionalSettings,
|
additionalSettings,
|
||||||
}: {
|
}: {
|
||||||
additionalSettings?: JSX.Element;
|
additionalSettings?: JSX.Element;
|
||||||
}) => {
|
}) => {
|
||||||
const { connected, disconnect } = useWallet();
|
const { connected, disconnect, select, wallet } = useWallet();
|
||||||
const { endpoint, setEndpoint } = useConnectionConfig();
|
const { endpoint, setEndpoint } = useConnectionConfig();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -27,9 +31,23 @@ export const Settings = ({
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
{connected && (
|
{connected && (
|
||||||
<Button type="primary" onClick={disconnect}>
|
<>
|
||||||
Disconnect
|
<span>Wallet:</span>
|
||||||
</Button>
|
{wallet?.publicKey && (<Button
|
||||||
|
style={{ marginBottom: 5 }} onClick={() => navigator.clipboard.writeText(wallet.publicKey?.toBase58() || '')}>
|
||||||
|
<CopyOutlined />
|
||||||
|
{shortenAddress(wallet?.publicKey.toBase58())}
|
||||||
|
</Button>)}
|
||||||
|
|
||||||
|
<Button onClick={select}
|
||||||
|
style={{ marginBottom: 5 }}>
|
||||||
|
Change
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" onClick={disconnect}
|
||||||
|
style={{ marginBottom: 5 }}>
|
||||||
|
Disconnect
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{additionalSettings}
|
{additionalSettings}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -416,35 +416,35 @@ export function AccountsProvider({ children = null as any }) {
|
||||||
if (!connection || !publicKey) {
|
if (!connection || !publicKey) {
|
||||||
setTokenAccounts([]);
|
setTokenAccounts([]);
|
||||||
} else {
|
} else {
|
||||||
precacheUserTokenAccounts(connection, LEND_HOST_FEE_ADDRESS);
|
// precacheUserTokenAccounts(connection, LEND_HOST_FEE_ADDRESS);
|
||||||
|
|
||||||
precacheUserTokenAccounts(connection, publicKey).then(() => {
|
// precacheUserTokenAccounts(connection, publicKey).then(() => {
|
||||||
setTokenAccounts(selectUserAccounts());
|
// setTokenAccounts(selectUserAccounts());
|
||||||
});
|
// });
|
||||||
|
|
||||||
// This can return different types of accounts: token-account, mint, multisig
|
// This can return different types of accounts: token-account, mint, multisig
|
||||||
// TODO: web3.js expose ability to filter.
|
// TODO: web3.js expose ability to filter.
|
||||||
// this should use only filter syntax to only get accounts that are owned by user
|
// this should use only filter syntax to only get accounts that are owned by user
|
||||||
const tokenSubID = connection.onProgramAccountChange(
|
// const tokenSubID = connection.onProgramAccountChange(
|
||||||
programIds().token,
|
// programIds().token,
|
||||||
info => {
|
// info => {
|
||||||
// TODO: fix type in web3.js
|
// // TODO: fix type in web3.js
|
||||||
const id = (info.accountId as unknown) as string;
|
// const id = (info.accountId as unknown) as string;
|
||||||
// TODO: do we need a better way to identify layout (maybe a enum identifing type?)
|
// // TODO: do we need a better way to identify layout (maybe a enum identifing type?)
|
||||||
if (info.accountInfo.data.length === AccountLayout.span) {
|
// if (info.accountInfo.data.length === AccountLayout.span) {
|
||||||
const data = deserializeAccount(info.accountInfo.data);
|
// const data = deserializeAccount(info.accountInfo.data);
|
||||||
|
|
||||||
if (PRECACHED_OWNERS.has(data.owner.toBase58())) {
|
// if (PRECACHED_OWNERS.has(data.owner.toBase58())) {
|
||||||
cache.add(id, info.accountInfo, TokenAccountParser);
|
// cache.add(id, info.accountInfo, TokenAccountParser);
|
||||||
setTokenAccounts(selectUserAccounts());
|
// setTokenAccounts(selectUserAccounts());
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
'singleGossip',
|
// 'singleGossip',
|
||||||
);
|
// );
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
connection.removeProgramAccountChangeListener(tokenSubID);
|
// connection.removeProgramAccountChangeListener(tokenSubID);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [connection, connected, publicKey, selectUserAccounts]);
|
}, [connection, connected, publicKey, selectUserAccounts]);
|
||||||
|
|
|
@ -9,6 +9,8 @@ import { useLocalStorageState } from "../utils/utils";
|
||||||
import { LedgerProvider } from "@solana/wallet-ledger";
|
import { LedgerProvider } from "@solana/wallet-ledger";
|
||||||
import { SolongWalletAdapter } from "../wallet-adapters/solong";
|
import { SolongWalletAdapter } from "../wallet-adapters/solong";
|
||||||
import { PhantomWalletAdapter } from "../wallet-adapters/phantom";
|
import { PhantomWalletAdapter } from "../wallet-adapters/phantom";
|
||||||
|
import { TorusWalletAdapter } from "../wallet-adapters/torus";
|
||||||
|
import { useLocation } from "react-router";
|
||||||
|
|
||||||
const ASSETS_URL = 'https://raw.githubusercontent.com/solana-labs/oyster/main/assets/wallets/';
|
const ASSETS_URL = 'https://raw.githubusercontent.com/solana-labs/oyster/main/assets/wallets/';
|
||||||
export const WALLET_PROVIDERS = [
|
export const WALLET_PROVIDERS = [
|
||||||
|
@ -37,6 +39,12 @@ export const WALLET_PROVIDERS = [
|
||||||
icon: `https://www.phantom.app/img/logo.png`,
|
icon: `https://www.phantom.app/img/logo.png`,
|
||||||
adapter: PhantomWalletAdapter,
|
adapter: PhantomWalletAdapter,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Torus',
|
||||||
|
url: 'https://tor.us',
|
||||||
|
icon: `${ASSETS_URL}torus.svg`,
|
||||||
|
adapter: TorusWalletAdapter,
|
||||||
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const WalletContext = React.createContext<{
|
const WalletContext = React.createContext<{
|
||||||
|
@ -53,8 +61,8 @@ const WalletContext = React.createContext<{
|
||||||
|
|
||||||
export function WalletProvider({ children = null as any }) {
|
export function WalletProvider({ children = null as any }) {
|
||||||
const { endpoint } = useConnectionConfig();
|
const { endpoint } = useConnectionConfig();
|
||||||
|
const location = useLocation();
|
||||||
const [autoConnect, setAutoConnect] = useState(false);
|
const [autoConnect, setAutoConnect] = useState(location.pathname.indexOf('result=') >= 0 || false);
|
||||||
const [providerUrl, setProviderUrl] = useLocalStorageState("walletProvider");
|
const [providerUrl, setProviderUrl] = useLocalStorageState("walletProvider");
|
||||||
|
|
||||||
const provider = useMemo(() => WALLET_PROVIDERS.find(({ url }) => url === providerUrl), [providerUrl]);
|
const provider = useMemo(() => WALLET_PROVIDERS.find(({ url }) => url === providerUrl), [providerUrl]);
|
||||||
|
@ -90,6 +98,7 @@ export function WalletProvider({ children = null as any }) {
|
||||||
|
|
||||||
wallet.on("disconnect", () => {
|
wallet.on("disconnect", () => {
|
||||||
setConnected(false);
|
setConnected(false);
|
||||||
|
// setProviderUrl(null)
|
||||||
notify({
|
notify({
|
||||||
message: "Wallet update",
|
message: "Wallet update",
|
||||||
description: "Disconnected from wallet",
|
description: "Disconnected from wallet",
|
||||||
|
@ -99,6 +108,7 @@ export function WalletProvider({ children = null as any }) {
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
setConnected(false);
|
setConnected(false);
|
||||||
|
// setProviderUrl(null)
|
||||||
if (wallet) {
|
if (wallet) {
|
||||||
wallet.disconnect();
|
wallet.disconnect();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import EventEmitter from "eventemitter3"
|
||||||
|
import { Account, PublicKey, Transaction } from "@solana/web3.js"
|
||||||
|
import { WalletAdapter } from "@solana/wallet-base"
|
||||||
|
import OpenLogin from "@toruslabs/openlogin"
|
||||||
|
import { getED25519Key } from "@toruslabs/openlogin-ed25519"
|
||||||
|
|
||||||
|
const getSolanaPrivateKey = (openloginKey: string)=>{
|
||||||
|
const { sk } = getED25519Key(openloginKey)
|
||||||
|
return sk
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TorusWalletAdapter extends EventEmitter implements WalletAdapter {
|
||||||
|
_provider: OpenLogin | undefined;
|
||||||
|
endpoint: string;
|
||||||
|
providerUrl: string;
|
||||||
|
account: Account | undefined;
|
||||||
|
image: string = '';
|
||||||
|
name: string = '';
|
||||||
|
|
||||||
|
constructor(providerUrl: string, endpoint: string) {
|
||||||
|
super()
|
||||||
|
this.connect = this.connect.bind(this)
|
||||||
|
this.endpoint = endpoint;
|
||||||
|
this.providerUrl = providerUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
async signAllTransactions(transactions: Transaction[]): Promise<Transaction[]> {
|
||||||
|
if(this.account) {
|
||||||
|
let account = this.account;
|
||||||
|
transactions.forEach(t => t.partialSign(account));
|
||||||
|
}
|
||||||
|
|
||||||
|
return transactions
|
||||||
|
}
|
||||||
|
|
||||||
|
get publicKey() {
|
||||||
|
return this.account?.publicKey || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async signTransaction(transaction: Transaction) {
|
||||||
|
if(this.account) {
|
||||||
|
transaction.partialSign(this.account)
|
||||||
|
}
|
||||||
|
|
||||||
|
return transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
connect = async () => {
|
||||||
|
this._provider = new OpenLogin({
|
||||||
|
clientId: process.env.REACT_APP_CLIENT_ID || 'BKBTX-SmaEFGddZQrwqd65YFoImRQLca_Tj2IdmKyD2UbDpzrtN2WQ-NYLuej6gP0DfF3jSpEkI13wPt1uPedm0',
|
||||||
|
network: "mainnet", // mainnet, testnet, development
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this._provider.init();
|
||||||
|
} catch (ex) {
|
||||||
|
console.error('init failed', ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(this._provider?.state.store);
|
||||||
|
|
||||||
|
if (this._provider.privKey) {
|
||||||
|
const privateKey = this._provider.privKey;
|
||||||
|
const secretKey = getSolanaPrivateKey(privateKey);
|
||||||
|
this.account = new Account(secretKey);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const { privKey } = await this._provider.login();
|
||||||
|
const secretKey = getSolanaPrivateKey(privKey);
|
||||||
|
this.account = new Account(secretKey);
|
||||||
|
} catch(ex) {
|
||||||
|
console.error('login failed', ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = this._provider?.state.store.get('name');;
|
||||||
|
this.image = this._provider?.state.store.get('profileImage');;
|
||||||
|
|
||||||
|
this.emit("connect");
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect = async () => {
|
||||||
|
console.log("Disconnecting...")
|
||||||
|
if (this._provider) {
|
||||||
|
await this._provider.logout();
|
||||||
|
await this._provider._cleanup();
|
||||||
|
this._provider = undefined;
|
||||||
|
this.emit("disconnect");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,9 @@
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@testing-library/jest-dom": "^4.2.4",
|
||||||
"@testing-library/react": "^9.5.0",
|
"@testing-library/react": "^9.5.0",
|
||||||
"@testing-library/user-event": "^7.2.1",
|
"@testing-library/user-event": "^7.2.1",
|
||||||
|
"@toruslabs/openlogin": "^0.7.0",
|
||||||
|
"@toruslabs/openlogin-ed25519": "^0.7.0",
|
||||||
|
"@toruslabs/openlogin-utils": "^0.7.0",
|
||||||
"@toruslabs/torus-embed": "^1.9.10",
|
"@toruslabs/torus-embed": "^1.9.10",
|
||||||
"@types/animejs": "^3.1.3",
|
"@types/animejs": "^3.1.3",
|
||||||
"@types/chart.js": "^2.9.29",
|
"@types/chart.js": "^2.9.29",
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const createAuction = () => {
|
||||||
|
// TODO:
|
||||||
|
};
|
|
@ -1 +1,3 @@
|
||||||
export const nop = () => {};
|
export * from './nft';
|
||||||
|
export * from './vault';
|
||||||
|
export * from './auction';
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const createVault = () => {
|
||||||
|
// TODO:
|
||||||
|
};
|
|
@ -10,7 +10,7 @@ const UserActions = () => {
|
||||||
<Link to={`/art/create`}>
|
<Link to={`/art/create`}>
|
||||||
<Button className="app-btn">Create</Button>
|
<Button className="app-btn">Create</Button>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={`/auction/create`}>
|
<Link to={`/auction/create/0`}>
|
||||||
<Button type="primary">Sell</Button>
|
<Button type="primary">Sell</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</>;
|
</>;
|
||||||
|
|
|
@ -1,21 +1,12 @@
|
||||||
import React, { useLayoutEffect, useState } from 'react';
|
import React, { useLayoutEffect, useState } from 'react';
|
||||||
import { Card, Avatar } from 'antd';
|
import { Card, Avatar, CardProps } from 'antd';
|
||||||
import { MetadataCategory } from '@oyster/common';
|
import { MetadataCategory } from '@oyster/common';
|
||||||
import { ArtContent } from './../ArtContent';
|
import { ArtContent } from './../ArtContent';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const { Meta } = Card;
|
const { Meta } = Card;
|
||||||
|
|
||||||
export const ArtCard = ({
|
export interface ArtCardProps extends CardProps {
|
||||||
image,
|
|
||||||
category,
|
|
||||||
name,
|
|
||||||
symbol,
|
|
||||||
description,
|
|
||||||
artist,
|
|
||||||
preview,
|
|
||||||
small,
|
|
||||||
}: {
|
|
||||||
image?: string;
|
image?: string;
|
||||||
category?: MetadataCategory
|
category?: MetadataCategory
|
||||||
name?: string;
|
name?: string;
|
||||||
|
@ -23,13 +14,17 @@ export const ArtCard = ({
|
||||||
description?: string;
|
description?: string;
|
||||||
artist?: string;
|
artist?: string;
|
||||||
preview?: boolean;
|
preview?: boolean;
|
||||||
small?: boolean
|
small?: boolean;
|
||||||
}) => {
|
}
|
||||||
|
|
||||||
|
export const ArtCard = (props: ArtCardProps) => {
|
||||||
|
const { className, small, category, image, name, preview, artist, ...rest } = props;
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
hoverable={true}
|
hoverable={true}
|
||||||
className={`art-card ${small ? 'small' : ''}`}
|
className={`art-card ${small ? 'small' : ''} ${className}`}
|
||||||
cover={<ArtContent category={category} content={image} />}
|
cover={<ArtContent category={category} content={image} preview={preview} />}
|
||||||
|
{...rest}
|
||||||
>
|
>
|
||||||
<Meta
|
<Meta
|
||||||
title={`${name}`}
|
title={`${name}`}
|
||||||
|
|
|
@ -2,11 +2,27 @@ import React, { useMemo } from 'react';
|
||||||
import { Image } from 'antd';
|
import { Image } from 'antd';
|
||||||
import { MetadataCategory } from '@oyster/common'
|
import { MetadataCategory } from '@oyster/common'
|
||||||
|
|
||||||
export const ArtContent = ({ content, category, className }: { category?: MetadataCategory, content?: string, className?: string }) => {
|
export const ArtContent = ({
|
||||||
|
content,
|
||||||
|
category,
|
||||||
|
className,
|
||||||
|
preview
|
||||||
|
}: {
|
||||||
|
category?: MetadataCategory,
|
||||||
|
content?: string,
|
||||||
|
className?: string,
|
||||||
|
preview?: boolean,
|
||||||
|
}) => {
|
||||||
return category === 'video' ?
|
return category === 'video' ?
|
||||||
<video src={content} className={className} playsInline={true} autoPlay={true} controlsList="nodownload" loop={true} /> :
|
<video src={content}
|
||||||
|
className={className}
|
||||||
|
playsInline={true}
|
||||||
|
autoPlay={true}
|
||||||
|
controlsList="nodownload"
|
||||||
|
loop={true} /> :
|
||||||
<Image
|
<Image
|
||||||
src={content}
|
src={content}
|
||||||
|
preview={preview}
|
||||||
wrapperClassName={className}
|
wrapperClassName={className}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export * from './useArt';
|
export * from './useArt';
|
||||||
export * from './useAuctions';
|
export * from './useAuctions';
|
||||||
|
export * from './useUserArts';
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { TokenAccount, useUserAccounts } from '@oyster/common';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { useMeta } from './../contexts';
|
||||||
|
|
||||||
|
export const useUserArts = () => {
|
||||||
|
const { metadata } = useMeta();
|
||||||
|
const { userAccounts } = useUserAccounts();
|
||||||
|
const accountByMint = userAccounts.reduce((prev, acc) => {
|
||||||
|
prev.set(acc.info.mint.toBase58(), acc);
|
||||||
|
return prev;
|
||||||
|
}, new Map<string, TokenAccount>());
|
||||||
|
|
||||||
|
const ownedMetadata = metadata.filter(m => accountByMint.has(m.info.mint.toBase58()));
|
||||||
|
|
||||||
|
return ownedMetadata;
|
||||||
|
}
|
|
@ -1,2 +1 @@
|
||||||
export * from './dex';
|
export * from './dex';
|
||||||
export * from './nft';
|
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
import { PublicKey } from '@solana/web3.js';
|
||||||
|
|
||||||
|
export * from './initAuctionManager';
|
||||||
|
export * from './redeemBid';
|
||||||
|
export * from './redeemLimitedEditionBid';
|
||||||
|
export * from './redeemMasterEditionBid';
|
||||||
|
export * from './redeemOpenEditionBid';
|
||||||
|
export * from './startAuction';
|
||||||
|
export * from './validateSafetyDepositBox';
|
||||||
|
|
||||||
|
export class AuctionManager {
|
||||||
|
key?: number;
|
||||||
|
authority?: PublicKey;
|
||||||
|
auction?: PublicKey;
|
||||||
|
vault?: PublicKey;
|
||||||
|
auctionProgram?: PublicKey;
|
||||||
|
tokenVaultProgram?: PublicKey;
|
||||||
|
tokenMetadataProgram?: PublicKey;
|
||||||
|
tokenProgram?: PublicKey;
|
||||||
|
state?: AuctionManagerState;
|
||||||
|
settings?: AuctionManagerSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AuctionManagerSettings {
|
||||||
|
openEditionWinnerConstraint?: WinningConstraint;
|
||||||
|
openEditionNonWinningConstraint?: NonWinningConstraint;
|
||||||
|
winningConfigs?: WinningConfig[];
|
||||||
|
openEditionConfig?: number;
|
||||||
|
openEditionFixedPrice?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WinningConstraint {
|
||||||
|
NoOpenEdition,
|
||||||
|
OpenEditionGiven,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum NonWinningConstraint {
|
||||||
|
NoOpenEdition,
|
||||||
|
GivenForFixedPrice,
|
||||||
|
GivenForBidPrice,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum EditionType {
|
||||||
|
// Not an edition
|
||||||
|
NA,
|
||||||
|
/// Means you are auctioning off the master edition record
|
||||||
|
MasterEdition,
|
||||||
|
/// Means you are using the master edition to print off new editions during the auction (limited or open edition)
|
||||||
|
LimitedEdition,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WinningConfig {
|
||||||
|
safetyDepositBoxIndex?: number;
|
||||||
|
amount?: number;
|
||||||
|
hasAuthority?: boolean;
|
||||||
|
editionType?: EditionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WinningConfigState {
|
||||||
|
/// Used for cases of minting Limited Editions and keeping track of how many have been made so far.
|
||||||
|
amountMinted?: number;
|
||||||
|
/// Each safety deposit box needs to be validated via endpoint before auction manager will agree to let auction begin.
|
||||||
|
validated?: boolean;
|
||||||
|
/// Ticked to true when a prize is claimed
|
||||||
|
claimed?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AuctionManagerState {
|
||||||
|
status?: AuctionManagerStatus;
|
||||||
|
/// When all configs are validated the auction is started and auction manager moves to Running
|
||||||
|
winningConfigsValidated?: number;
|
||||||
|
|
||||||
|
/// Each master edition used as a template has to grant it's authority to the auction manager.
|
||||||
|
/// This counter is incremented by one each time this is done. At the end of the auction; this is decremented
|
||||||
|
/// each time authority is delegated back to the owner or the new owner and when it hits 0 another condition
|
||||||
|
/// is met for going to Finished state.
|
||||||
|
masterEditionsWithAuthoritiesRemainingToReturn?: number;
|
||||||
|
|
||||||
|
winningConfigStates?: WinningConfigState[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AuctionManagerStatus {
|
||||||
|
Initialized,
|
||||||
|
Validated,
|
||||||
|
Running,
|
||||||
|
Disbursing,
|
||||||
|
Finished,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BidRedemptionTicket {
|
||||||
|
openEditionRedeemed?: boolean;
|
||||||
|
bidRedeemed?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SCHEMA = new Map<any, any>([
|
||||||
|
[
|
||||||
|
AuctionManager,
|
||||||
|
{
|
||||||
|
kind: 'struct',
|
||||||
|
fields: [
|
||||||
|
['key', 'u8'],
|
||||||
|
['authority', 'u32'],
|
||||||
|
['auction', 'u32'],
|
||||||
|
['vault', 'u32'],
|
||||||
|
['auctionProgram', 'u32'],
|
||||||
|
['tokenVaultProgram', 'u32'],
|
||||||
|
['tokenMetadataProgram', 'u32'],
|
||||||
|
['tokenProgram', 'u32'],
|
||||||
|
['state', AuctionManagerState],
|
||||||
|
['settings', AuctionManagerSettings],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AuctionManagerSettings,
|
||||||
|
{
|
||||||
|
kind: 'struct',
|
||||||
|
fields: [
|
||||||
|
['openEditionWinnerConstraint', { kind: 'enum' }], // TODO:
|
||||||
|
['openEditionNonWinningConstraint', { kind: 'enum' }], // TODO:
|
||||||
|
['winningConfigs', [WinningConfig]], // TODO: check
|
||||||
|
['openEditionConfig', { kind: 'option', type: 'u8' }],
|
||||||
|
['openEditionFixedPrice', { kind: 'option', type: 'u8' }],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
WinningConfig,
|
||||||
|
{
|
||||||
|
kind: 'struct',
|
||||||
|
fields: [
|
||||||
|
['safetyDepositBoxIndex', 'u8'],
|
||||||
|
['amount', 'u8'],
|
||||||
|
['hasAuthority', 'u8'], // bool
|
||||||
|
['editionType', { kind: 'enum' }], // TODO:
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
WinningConfigState,
|
||||||
|
{
|
||||||
|
kind: 'struct',
|
||||||
|
fields: [
|
||||||
|
['amountMinted', 'u8'],
|
||||||
|
['validated', 'u8'], // bool
|
||||||
|
['claimed', 'u8'], // bool
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AuctionManagerState,
|
||||||
|
{
|
||||||
|
kind: 'struct',
|
||||||
|
fields: [
|
||||||
|
// TODO: fix enum
|
||||||
|
['status', { kind: 'enum' }],
|
||||||
|
['winningConfigsValidated', 'u8'],
|
||||||
|
['masterEditionsWithAuthoritiesRemainingToReturn', 'u8'],
|
||||||
|
['winningConfigStates', [WinningConfigState]],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
BidRedemptionTicket,
|
||||||
|
{
|
||||||
|
kind: 'struct',
|
||||||
|
fields: [
|
||||||
|
['openEditionRedeemed', 'u8'], // bool
|
||||||
|
['bidRedeemed', 'u8'], // bool
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]);
|
|
@ -0,0 +1 @@
|
||||||
|
export const initAuctionManager = () => {};
|
|
@ -0,0 +1 @@
|
||||||
|
export const redeemBid = () => {};
|
|
@ -0,0 +1 @@
|
||||||
|
export const redeemLimitedEditionBid = () => {};
|
|
@ -0,0 +1 @@
|
||||||
|
export const redeemMasterEditionBid = () => {};
|
|
@ -0,0 +1 @@
|
||||||
|
export const redeemOpenEditionBid = () => {};
|
|
@ -0,0 +1 @@
|
||||||
|
export const startAuction = () => {};
|
|
@ -0,0 +1 @@
|
||||||
|
export const validateSafetyDepositBox = () => {};
|
|
@ -25,11 +25,6 @@ export function Routes() {
|
||||||
<MetaProvider>
|
<MetaProvider>
|
||||||
<AppLayout>
|
<AppLayout>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route
|
|
||||||
exact
|
|
||||||
path="/"
|
|
||||||
component={() => <HomeView />}
|
|
||||||
/>
|
|
||||||
<Route
|
<Route
|
||||||
exact
|
exact
|
||||||
path="/art/create/:step_param?"
|
path="/art/create/:step_param?"
|
||||||
|
@ -65,6 +60,10 @@ export function Routes() {
|
||||||
path="/auction/:id"
|
path="/auction/:id"
|
||||||
component={() => <AuctionView />}
|
component={() => <AuctionView />}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
path="/"
|
||||||
|
component={() => <HomeView />}
|
||||||
|
/>
|
||||||
</Switch>
|
</Switch>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
</MetaProvider>
|
</MetaProvider>
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
import { PublicKey } from '@solana/web3.js';
|
import { PublicKey } from '@solana/web3.js';
|
||||||
|
|
||||||
export const WORMHOLE_PROGRAM_ID = new PublicKey(
|
export const AUCTION_PROGRAM_ID = new PublicKey(
|
||||||
'WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC',
|
'aucPpK6yb5MpEHDwyp3Kg79McUoKVqTMeum7iJ9syeH',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const VAULT_PROGRAM_ID = new PublicKey(
|
||||||
|
'vauLTA73sFPqA8whreUbBsbn3SLJH2vhrW9fP5dmfdC',
|
||||||
|
);
|
||||||
|
|
||||||
|
export const METAPLEX_PROGRAM_ID = new PublicKey(
|
||||||
|
'HvwC9QSAzvGXhhVrgPmauVwFWcYZhne3hVot9EbHuFTm',
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: generate key ---
|
||||||
export const AR_SOL_HOLDER_ID = new PublicKey(
|
export const AR_SOL_HOLDER_ID = new PublicKey(
|
||||||
'HvwC9QSAzvGXhhVrgPmauVwFWcYZhne3hVot9EbHuFTm',
|
'HvwC9QSAzvGXhhVrgPmauVwFWcYZhne3hVot9EbHuFTm',
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { ArtCard } from './../../components/ArtCard';
|
||||||
import { UserSearch, UserValue } from './../../components/UserSearch';
|
import { UserSearch, UserValue } from './../../components/UserSearch';
|
||||||
import { Confetti } from './../../components/Confetti';
|
import { Confetti } from './../../components/Confetti';
|
||||||
import './../styles.less';
|
import './../styles.less';
|
||||||
import { mintNFT } from '../../models';
|
import { mintNFT } from '../../actions';
|
||||||
import {
|
import {
|
||||||
MAX_METADATA_LEN,
|
MAX_METADATA_LEN,
|
||||||
MAX_OWNER_LEN,
|
MAX_OWNER_LEN,
|
||||||
|
@ -357,13 +357,13 @@ const InfoStep = (props: {
|
||||||
setAttributes: (attr: IMetadataExtension) => void;
|
setAttributes: (attr: IMetadataExtension) => void;
|
||||||
confirm: () => void;
|
confirm: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [creators, setCreators] = useState<Array<UserValue>>([]);
|
const [creators, setCreators] = useState<Array<UserValue>>([])
|
||||||
const [royalties, setRoyalties] = useState<Array<Royalty>>([])
|
const [royalties, setRoyalties] = useState<Array<Royalty>>([])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRoyalties(creators.map(creator => ({
|
setRoyalties(creators.map(creator => ({
|
||||||
creator_key: creator.key,
|
creator_key: creator.key,
|
||||||
amount: 100 / creators.length,
|
amount: Math.trunc(100 / creators.length),
|
||||||
})))
|
})))
|
||||||
}, [creators])
|
}, [creators])
|
||||||
|
|
||||||
|
@ -463,6 +463,10 @@ const InfoStep = (props: {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const shuffle = (array: Array<any>) => {
|
||||||
|
array.sort(() => Math.random() - 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
const RoyaltiesSplitter = (props: {
|
const RoyaltiesSplitter = (props: {
|
||||||
creators: Array<UserValue>,
|
creators: Array<UserValue>,
|
||||||
royalties: Array<Royalty>,
|
royalties: Array<Royalty>,
|
||||||
|
@ -476,15 +480,31 @@ const RoyaltiesSplitter = (props: {
|
||||||
|
|
||||||
const amt = royalty.amount
|
const amt = royalty.amount
|
||||||
const handleSlide = (newAmt: number) => {
|
const handleSlide = (newAmt: number) => {
|
||||||
const diff = newAmt - amt
|
const othersRoyalties = props.royalties.filter(_royalty => _royalty.creator_key != royalty.creator_key)
|
||||||
let n = props.royalties.length - 1
|
if (othersRoyalties.length < 1) return
|
||||||
|
shuffle(othersRoyalties)
|
||||||
|
const others_n = props.royalties.length - 1
|
||||||
|
const sign = Math.sign(newAmt - amt)
|
||||||
|
let remaining = Math.abs(newAmt - amt)
|
||||||
|
let count = 0
|
||||||
|
while (remaining > 0 && count < 100) {
|
||||||
|
const idx = count % others_n
|
||||||
|
const _royalty = othersRoyalties[idx]
|
||||||
|
if (
|
||||||
|
(0 < _royalty.amount && _royalty.amount < 100) // Normal
|
||||||
|
|| (_royalty.amount == 0 && sign < 0) // Low limit
|
||||||
|
|| (_royalty.amount == 100 && sign > 0) // High limit
|
||||||
|
) {
|
||||||
|
_royalty.amount -= sign
|
||||||
|
remaining -= 1
|
||||||
|
}
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
|
||||||
props.setRoyalties(props.royalties.map(_royalty => {
|
props.setRoyalties(props.royalties.map(_royalty => {
|
||||||
let computed_amount = _royalty.amount - diff / n
|
const computed_amount = othersRoyalties.find(newRoyalty =>
|
||||||
if (computed_amount <= 0) {
|
newRoyalty.creator_key == _royalty.creator_key
|
||||||
computed_amount = 0
|
)?.amount
|
||||||
// n -= 1
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
..._royalty,
|
..._royalty,
|
||||||
amount: _royalty.creator_key == royalty.creator_key ? newAmt : computed_amount,
|
amount: _royalty.creator_key == royalty.creator_key ? newAmt : computed_amount,
|
||||||
|
@ -494,7 +514,7 @@ const RoyaltiesSplitter = (props: {
|
||||||
return (
|
return (
|
||||||
<Row key={idx} style={{ margin: '5px auto' }}>
|
<Row key={idx} style={{ margin: '5px auto' }}>
|
||||||
<Col span={11} className="slider-elem">{creator.label}</Col>
|
<Col span={11} className="slider-elem">{creator.label}</Col>
|
||||||
<Col span={8} className="slider-elem">{amt.toFixed(0)}%</Col>
|
<Col span={8} className="slider-elem">{amt}%</Col>
|
||||||
<Col span={4}><Slider value={amt} onChange={handleSlide} /></Col>
|
<Col span={4}><Slider value={amt} onChange={handleSlide} /></Col>
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,20 +1,12 @@
|
||||||
import { ParsedAccount, TokenAccount, useUserAccounts } from '@oyster/common';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ArtCard } from '../../components/ArtCard';
|
import { ArtCard } from '../../components/ArtCard';
|
||||||
import { useMeta } from '../../contexts';
|
|
||||||
import { Row, Col } from 'antd';
|
import { Row, Col } from 'antd';
|
||||||
import Masonry from 'react-masonry-css'
|
import Masonry from 'react-masonry-css'
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { useUserArts } from '../../hooks';
|
||||||
|
|
||||||
export const ArtworksView = () => {
|
export const ArtworksView = () => {
|
||||||
const { metadata } = useMeta();
|
const ownedMetadata = useUserArts();
|
||||||
const { userAccounts } = useUserAccounts();
|
|
||||||
const accountByMint = userAccounts.reduce((prev, acc) => {
|
|
||||||
prev.set(acc.info.mint.toBase58(), acc);
|
|
||||||
return prev;
|
|
||||||
}, new Map<string, TokenAccount>());
|
|
||||||
|
|
||||||
const ownedMetadata = metadata.filter(m => accountByMint.has(m.info.mint.toBase58()));
|
|
||||||
const breakpointColumnsObj = {
|
const breakpointColumnsObj = {
|
||||||
default: 4,
|
default: 4,
|
||||||
1100: 3,
|
1100: 3,
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
Row,
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
ButtonProps,
|
||||||
|
} from 'antd';
|
||||||
|
import { ArtCard } from './../../components/ArtCard';
|
||||||
|
import './../styles.less';
|
||||||
|
import {
|
||||||
|
Metadata,
|
||||||
|
ParsedAccount,
|
||||||
|
} from '@oyster/common';
|
||||||
|
import { useUserArts } from '../../hooks';
|
||||||
|
import Masonry from 'react-masonry-css';
|
||||||
|
|
||||||
|
export interface ArtSelectorProps extends ButtonProps {
|
||||||
|
selected: ParsedAccount<Metadata>[];
|
||||||
|
setSelected: (selected: ParsedAccount<Metadata>[]) => void;
|
||||||
|
allowMultiple: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ArtSelector = (props: ArtSelectorProps) => {
|
||||||
|
const { selected, setSelected, allowMultiple, ...rest } = props;
|
||||||
|
const items = useUserArts();
|
||||||
|
const [selectedItems, setSelectedItems] = useState<Set<string>>(new Set(props.selected.map(item => item.pubkey.toBase58())));
|
||||||
|
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
const showModal = () => {
|
||||||
|
setVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
props.setSelected(items.filter(item => selectedItems.has(item.pubkey.toBase58())));
|
||||||
|
}, [selectedItems]);
|
||||||
|
|
||||||
|
const breakpointColumnsObj = {
|
||||||
|
default: 4,
|
||||||
|
1100: 3,
|
||||||
|
700: 2,
|
||||||
|
500: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Button {...rest} onClick={() => showModal} />
|
||||||
|
<Modal visible={visible}>
|
||||||
|
<Row className="call-to-action" style={{ marginBottom: 0 }}>
|
||||||
|
<h2>Select the NFT you want to sell</h2>
|
||||||
|
<p style={{ fontSize: '1.2rem' }}>
|
||||||
|
Select the NFT that you want to sell copy/copies of.
|
||||||
|
</p>
|
||||||
|
</Row>
|
||||||
|
<Row className="content-action">
|
||||||
|
<Masonry
|
||||||
|
breakpointCols={breakpointColumnsObj}
|
||||||
|
className="my-masonry-grid"
|
||||||
|
columnClassName="my-masonry-grid_column"
|
||||||
|
>
|
||||||
|
{items.map(m => {
|
||||||
|
const id = m.pubkey.toBase58();
|
||||||
|
const isSelected = selectedItems.has(id);
|
||||||
|
|
||||||
|
const onSelect = () => {
|
||||||
|
let list = [...selectedItems.keys()];
|
||||||
|
if (props.allowMultiple) {
|
||||||
|
list = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
isSelected ?
|
||||||
|
setSelectedItems(new Set(list.filter(item => item !== id))) :
|
||||||
|
setSelectedItems(new Set([...list, id]));
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ArtCard key={id}
|
||||||
|
image={m.info.extended?.image}
|
||||||
|
category={m.info.extended?.category}
|
||||||
|
name={m.info?.name}
|
||||||
|
symbol={m.info.symbol}
|
||||||
|
preview={false}
|
||||||
|
onClick={onSelect}
|
||||||
|
className={isSelected ? 'selected-card' : 'not-selected-card'}
|
||||||
|
/>;
|
||||||
|
})}
|
||||||
|
</Masonry>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="large"
|
||||||
|
onClick={() => {
|
||||||
|
// TODO;
|
||||||
|
}}
|
||||||
|
className="action-btn"
|
||||||
|
>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
</Row>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -61,7 +61,7 @@
|
||||||
|
|
||||||
.type-btn {
|
.type-btn {
|
||||||
height: 80px;
|
height: 80px;
|
||||||
width: 280px;
|
width: 450px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: #1d1d1d;
|
background-color: #1d1d1d;
|
||||||
border-width: 0px;
|
border-width: 0px;
|
||||||
|
@ -166,6 +166,7 @@
|
||||||
.action-field {
|
.action-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
.field-title {
|
.field-title {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
@ -185,9 +186,34 @@
|
||||||
margin: 12px 0px;
|
margin: 12px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field-info {
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.field-date {
|
||||||
|
background: #282828;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
border-width: 0px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-card {
|
||||||
|
border-width: 3px;
|
||||||
|
border-color: #5870EE !important;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-selected-card {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.royalties-input {
|
.royalties-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
|
@ -266,3 +292,15 @@
|
||||||
height: 24px;
|
height: 24px;
|
||||||
margin-top: -7px;
|
margin-top: -7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.radio-field {
|
||||||
|
display: block;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.radio-subtitle {
|
||||||
|
color: dimgray;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
56
yarn.lock
56
yarn.lock
|
@ -3345,7 +3345,7 @@
|
||||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
|
||||||
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
|
||||||
|
|
||||||
"@toruslabs/eccrypto@^1.1.5":
|
"@toruslabs/eccrypto@^1.1.5", "@toruslabs/eccrypto@^1.1.6":
|
||||||
version "1.1.6"
|
version "1.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/@toruslabs/eccrypto/-/eccrypto-1.1.6.tgz#ce877cf00d6f9cf7ab3daa6ac4d6d540110b813b"
|
resolved "https://registry.yarnpkg.com/@toruslabs/eccrypto/-/eccrypto-1.1.6.tgz#ce877cf00d6f9cf7ab3daa6ac4d6d540110b813b"
|
||||||
integrity sha512-L3TAsdEARouyzTbSKE0PqcYXmHQiFh95FB2YnsRbWERCAF0VWg3kY/YA//M/HBZqCJoRwa5WRA61lWbM7zAX5Q==
|
integrity sha512-L3TAsdEARouyzTbSKE0PqcYXmHQiFh95FB2YnsRbWERCAF0VWg3kY/YA//M/HBZqCJoRwa5WRA61lWbM7zAX5Q==
|
||||||
|
@ -3372,6 +3372,44 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
deepmerge "^4.2.2"
|
deepmerge "^4.2.2"
|
||||||
|
|
||||||
|
"@toruslabs/openlogin-ed25519@^0.7.0":
|
||||||
|
version "0.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@toruslabs/openlogin-ed25519/-/openlogin-ed25519-0.7.0.tgz#836bb22b2daf0034e73d3666ad0c98227d342f36"
|
||||||
|
integrity sha512-+JjCadlbBI1sBie+Z/CS5EHVjjCThxq0imj0VNEcNDJUqdF91uJJghyWx46GllSHoJhWh7BsZUpVU0WZZMBPmw==
|
||||||
|
dependencies:
|
||||||
|
"@toruslabs/tweetnacl-js" "^1.0.3"
|
||||||
|
|
||||||
|
"@toruslabs/openlogin-jrpc@^0.7.0":
|
||||||
|
version "0.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@toruslabs/openlogin-jrpc/-/openlogin-jrpc-0.7.0.tgz#72fee3a94e9488ae133b0595bdf4d272ca01010d"
|
||||||
|
integrity sha512-BO6QnMQoZDp6O/JPVRj216es2m+10JcvtvudNhtfeCBPEXw/uc/OBRBBM0CC002zAHDgivSlPCc+8KOvNRXl1w==
|
||||||
|
dependencies:
|
||||||
|
"@toruslabs/openlogin-utils" "^0.7.0"
|
||||||
|
end-of-stream "^1.4.4"
|
||||||
|
fast-safe-stringify "^2.0.7"
|
||||||
|
once "^1.4.0"
|
||||||
|
pump "^3.0.0"
|
||||||
|
|
||||||
|
"@toruslabs/openlogin-utils@^0.7.0":
|
||||||
|
version "0.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@toruslabs/openlogin-utils/-/openlogin-utils-0.7.0.tgz#d612aacd0daaaf48734c7512335777569113902b"
|
||||||
|
integrity sha512-6caBBLGKjcDulU6oD0p7obtg0dTQlCLTAkDStkwSK9VObMOA/eR92jn3ivjE+r+epiU0LH7kFLAE8/ZFbtpNCw==
|
||||||
|
dependencies:
|
||||||
|
base64url "^3.0.1"
|
||||||
|
keccak "^3.0.1"
|
||||||
|
randombytes "^2.1.0"
|
||||||
|
|
||||||
|
"@toruslabs/openlogin@^0.7.0":
|
||||||
|
version "0.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@toruslabs/openlogin/-/openlogin-0.7.0.tgz#0169a38b0ab634f13354ded010565ff8ba179db1"
|
||||||
|
integrity sha512-p3s1i3VaHOLBdSjgYtUhW59FdYuAp3FkTxoQHmVr2Yr8NoIhAtRBd9JWKuHm5kGtjmb6+eI7pQx1CVoIph5yWw==
|
||||||
|
dependencies:
|
||||||
|
"@toruslabs/eccrypto" "^1.1.6"
|
||||||
|
"@toruslabs/openlogin-jrpc" "^0.7.0"
|
||||||
|
"@toruslabs/openlogin-utils" "^0.7.0"
|
||||||
|
lodash.merge "^4.6.2"
|
||||||
|
pump "^3.0.0"
|
||||||
|
|
||||||
"@toruslabs/torus-embed@^1.8.0", "@toruslabs/torus-embed@^1.9.10":
|
"@toruslabs/torus-embed@^1.8.0", "@toruslabs/torus-embed@^1.9.10":
|
||||||
version "1.9.14"
|
version "1.9.14"
|
||||||
resolved "https://registry.yarnpkg.com/@toruslabs/torus-embed/-/torus-embed-1.9.14.tgz#6d487a735ec6e570d62a044069581dc372ab193e"
|
resolved "https://registry.yarnpkg.com/@toruslabs/torus-embed/-/torus-embed-1.9.14.tgz#6d487a735ec6e570d62a044069581dc372ab193e"
|
||||||
|
@ -3409,6 +3447,11 @@
|
||||||
memory-cache "^0.2.0"
|
memory-cache "^0.2.0"
|
||||||
web3-utils "^1.3.3"
|
web3-utils "^1.3.3"
|
||||||
|
|
||||||
|
"@toruslabs/tweetnacl-js@^1.0.3":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@toruslabs/tweetnacl-js/-/tweetnacl-js-1.0.3.tgz#52abbcd2a6b77959ef6a98afedce77764d87226d"
|
||||||
|
integrity sha512-WQJYMTR/bkqvpk3DWOqRt5e24RhwJp9PXUoSj4zSthd3+fDhKYCI56YVMPNDKah1fCffOe9F3m8iZ5SgDZ+Csw==
|
||||||
|
|
||||||
"@typechain/ethers-v4@^1.0.0":
|
"@typechain/ethers-v4@^1.0.0":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@typechain/ethers-v4/-/ethers-v4-1.0.1.tgz#268b619c2660bf7689cda86e30798bd30753ae94"
|
resolved "https://registry.yarnpkg.com/@typechain/ethers-v4/-/ethers-v4-1.0.1.tgz#268b619c2660bf7689cda86e30798bd30753ae94"
|
||||||
|
@ -5977,6 +6020,11 @@ base64-js@^1.0.2, base64-js@^1.3.0, base64-js@^1.3.1:
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||||
|
|
||||||
|
base64url@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d"
|
||||||
|
integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==
|
||||||
|
|
||||||
base@^0.11.1:
|
base@^0.11.1:
|
||||||
version "0.11.2"
|
version "0.11.2"
|
||||||
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
||||||
|
@ -8835,7 +8883,7 @@ encoding@^0.1.11:
|
||||||
dependencies:
|
dependencies:
|
||||||
iconv-lite "^0.6.2"
|
iconv-lite "^0.6.2"
|
||||||
|
|
||||||
end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.0, end-of-stream@^1.4.1:
|
end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.0, end-of-stream@^1.4.1, end-of-stream@^1.4.4:
|
||||||
version "1.4.4"
|
version "1.4.4"
|
||||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||||
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||||
|
@ -10018,7 +10066,7 @@ fast-memoize@^2.5.2:
|
||||||
resolved "https://registry.yarnpkg.com/fast-memoize/-/fast-memoize-2.5.2.tgz#79e3bb6a4ec867ea40ba0e7146816f6cdce9b57e"
|
resolved "https://registry.yarnpkg.com/fast-memoize/-/fast-memoize-2.5.2.tgz#79e3bb6a4ec867ea40ba0e7146816f6cdce9b57e"
|
||||||
integrity sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==
|
integrity sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==
|
||||||
|
|
||||||
fast-safe-stringify@^2.0.6:
|
fast-safe-stringify@^2.0.6, fast-safe-stringify@^2.0.7:
|
||||||
version "2.0.7"
|
version "2.0.7"
|
||||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
|
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
|
||||||
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
|
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
|
||||||
|
@ -13524,7 +13572,7 @@ lodash.memoize@4.x, lodash.memoize@^4.1.2:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||||
|
|
||||||
lodash.merge@^4.4.0:
|
lodash.merge@^4.4.0, lodash.merge@^4.6.2:
|
||||||
version "4.6.2"
|
version "4.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
|
||||||
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
|
||||||
|
|
Loading…
Reference in New Issue