mirror of https://github.com/certusone/oyster.git
Full form hookups
This commit is contained in:
parent
499d5cb910
commit
3a521460e2
|
@ -16,9 +16,8 @@ import {
|
|||
TransactionInstruction,
|
||||
} from '@solana/web3.js';
|
||||
import crypto from 'crypto';
|
||||
import { getAssetCostToStore } from '../utils/assets';
|
||||
import { AR_SOL_HOLDER_ID } from '../utils/ids';
|
||||
const LAMPORT_MULTIPLIER = 10 ** 9;
|
||||
const WINSTON_MULTIPLIER = 10 ** 12;
|
||||
|
||||
interface IArweaveResult {
|
||||
error?: string;
|
||||
|
@ -179,43 +178,15 @@ export const prepPayForFilesTxn = async (
|
|||
}> => {
|
||||
const memo = programIds().memo;
|
||||
|
||||
const totalBytes = files.reduce((sum, f) => (sum += f.size), 0);
|
||||
|
||||
const txnFeeInWinstons = parseInt(
|
||||
await (await fetch('https://arweave.net/price/0')).text(),
|
||||
);
|
||||
const byteCostInWinstons = parseInt(
|
||||
await (
|
||||
await fetch('https://arweave.net/price/' + totalBytes.toString())
|
||||
).text(),
|
||||
);
|
||||
const totalArCost =
|
||||
(txnFeeInWinstons * files.length + byteCostInWinstons) / WINSTON_MULTIPLIER;
|
||||
|
||||
const conversionRates = JSON.parse(
|
||||
await (
|
||||
await fetch(
|
||||
'https://api.coingecko.com/api/v3/simple/price?ids=solana,arweave&vs_currencies=usd',
|
||||
)
|
||||
).text(),
|
||||
);
|
||||
|
||||
// To figure out how many lamports are required, multiply ar byte cost by this number
|
||||
const arMultiplier = conversionRates.arweave.usd / conversionRates.solana.usd;
|
||||
|
||||
const instructions: TransactionInstruction[] = [];
|
||||
const signers: Account[] = [];
|
||||
|
||||
// Add 10% padding for safety and slippage in price.
|
||||
const costToStoreInLamports =
|
||||
LAMPORT_MULTIPLIER * totalArCost * arMultiplier * 1.1;
|
||||
|
||||
if (wallet.publicKey)
|
||||
instructions.push(
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: wallet.publicKey,
|
||||
toPubkey: AR_SOL_HOLDER_ID,
|
||||
lamports: costToStoreInLamports,
|
||||
lamports: await getAssetCostToStore(files),
|
||||
}),
|
||||
);
|
||||
|
||||
|
|
|
@ -16,6 +16,9 @@ export const getAssetName = (
|
|||
else return getVerboseTokenName(ethTokens, `0x${parsedAssetAddress}`);
|
||||
};
|
||||
|
||||
export const LAMPORT_MULTIPLIER = 10 ** 9;
|
||||
const WINSTON_MULTIPLIER = 10 ** 12;
|
||||
|
||||
export const getAssetTokenSymbol = (
|
||||
parsedAssetAddress: string,
|
||||
assetChain: number,
|
||||
|
@ -52,3 +55,32 @@ export const chainToName = (chain?: ASSET_CHAIN) => {
|
|||
export const filterModalSolTokens = (tokens: TokenInfo[]) => {
|
||||
return tokens;
|
||||
};
|
||||
|
||||
export async function getAssetCostToStore(files: File[]) {
|
||||
const totalBytes = files.reduce((sum, f) => (sum += f.size), 0);
|
||||
console.log('Total bytes', totalBytes);
|
||||
const txnFeeInWinstons = parseInt(
|
||||
await (await fetch('https://arweave.net/price/0')).text(),
|
||||
);
|
||||
const byteCostInWinstons = parseInt(
|
||||
await (
|
||||
await fetch('https://arweave.net/price/' + totalBytes.toString())
|
||||
).text(),
|
||||
);
|
||||
const totalArCost =
|
||||
(txnFeeInWinstons * files.length + byteCostInWinstons) / WINSTON_MULTIPLIER;
|
||||
|
||||
const conversionRates = JSON.parse(
|
||||
await (
|
||||
await fetch(
|
||||
'https://api.coingecko.com/api/v3/simple/price?ids=solana,arweave&vs_currencies=usd',
|
||||
)
|
||||
).text(),
|
||||
);
|
||||
|
||||
// To figure out how many lamports are required, multiply ar byte cost by this number
|
||||
const arMultiplier = conversionRates.arweave.usd / conversionRates.solana.usd;
|
||||
|
||||
// Add 10% padding for safety and slippage in price.
|
||||
return LAMPORT_MULTIPLIER * totalArCost * arMultiplier * 1.1;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Steps,
|
||||
Row,
|
||||
|
@ -7,28 +7,34 @@ import {
|
|||
Col,
|
||||
Input,
|
||||
Statistic,
|
||||
Descriptions,
|
||||
Slider,
|
||||
} from 'antd';
|
||||
import { InboxOutlined } from '@ant-design/icons';
|
||||
import { ArtCard } from './../../components/ArtCard';
|
||||
import './styles.less';
|
||||
import { mintNFT } from '../../models';
|
||||
import { useConnection, useWallet } from '@oyster/common';
|
||||
import { getAssetCostToStore, LAMPORT_MULTIPLIER } from '../../utils/assets';
|
||||
|
||||
const { Step } = Steps;
|
||||
const { Dragger } = Upload;
|
||||
|
||||
enum Category {
|
||||
Audio = 'audio',
|
||||
Video = 'video',
|
||||
Image = 'image',
|
||||
}
|
||||
interface IProps {
|
||||
type: string;
|
||||
name: string;
|
||||
symbol: String;
|
||||
symbol: string;
|
||||
description: string;
|
||||
// preview image
|
||||
image: string;
|
||||
// stores link to item on meta
|
||||
external_url: string;
|
||||
externalUrl: string;
|
||||
royalty: number;
|
||||
files: File[];
|
||||
category: Category;
|
||||
}
|
||||
|
||||
export const ArtCreateView = () => {
|
||||
|
@ -36,19 +42,23 @@ export const ArtCreateView = () => {
|
|||
const { wallet, connected } = useWallet();
|
||||
const [step, setStep] = useState(0);
|
||||
const [attributes, setAttributes] = useState<IProps>({
|
||||
type: 'image',
|
||||
name: '',
|
||||
symbol: '',
|
||||
description: '',
|
||||
external_url: '',
|
||||
externalUrl: '',
|
||||
image: '',
|
||||
royalty: 0,
|
||||
files: [],
|
||||
category: Category.Image,
|
||||
});
|
||||
|
||||
// store files
|
||||
const mint = () => {
|
||||
mintNFT(connection, wallet, attributes.files, attributes);
|
||||
const metadata = {
|
||||
...(attributes as any),
|
||||
files: attributes.files.map(f => f.name),
|
||||
};
|
||||
mintNFT(connection, wallet, attributes.files, metadata);
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -71,10 +81,10 @@ export const ArtCreateView = () => {
|
|||
<Col xl={16}>
|
||||
{step === 0 && (
|
||||
<CategoryStep
|
||||
confirm={type => {
|
||||
confirm={(category: Category) => {
|
||||
setAttributes({
|
||||
...attributes,
|
||||
type,
|
||||
category,
|
||||
});
|
||||
setStep(1);
|
||||
}}
|
||||
|
@ -111,7 +121,7 @@ export const ArtCreateView = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const CategoryStep = (props: { confirm: (type: string) => void }) => {
|
||||
const CategoryStep = (props: { confirm: (category: Category) => void }) => {
|
||||
return (
|
||||
<>
|
||||
<Row className="call-to-action">
|
||||
|
@ -126,21 +136,21 @@ const CategoryStep = (props: { confirm: (type: string) => void }) => {
|
|||
<Button
|
||||
className="type-btn"
|
||||
size="large"
|
||||
onClick={() => props.confirm('image')}
|
||||
onClick={() => props.confirm(Category.Image)}
|
||||
>
|
||||
Image
|
||||
</Button>
|
||||
<Button
|
||||
className="type-btn"
|
||||
size="large"
|
||||
onClick={() => props.confirm('video')}
|
||||
onClick={() => props.confirm(Category.Video)}
|
||||
>
|
||||
Video
|
||||
</Button>
|
||||
<Button
|
||||
className="type-btn"
|
||||
size="large"
|
||||
onClick={() => props.confirm('audio')}
|
||||
onClick={() => props.confirm(Category.Audio)}
|
||||
>
|
||||
Audio
|
||||
</Button>
|
||||
|
@ -244,6 +254,21 @@ const InfoStep = (props: {
|
|||
}
|
||||
/>
|
||||
</label>
|
||||
<label className="action-field">
|
||||
<span className="field-title">Symbol</span>
|
||||
<Input
|
||||
className="input"
|
||||
placeholder="Max 10 characters"
|
||||
allowClear
|
||||
value={props.attributes.symbol}
|
||||
onChange={info =>
|
||||
props.setAttributes({
|
||||
...props.attributes,
|
||||
symbol: info.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
<label className="action-field">
|
||||
<span className="field-title">Description</span>
|
||||
<Input.TextArea
|
||||
|
@ -287,19 +312,29 @@ const RoyaltiesStep = (props: {
|
|||
<Row className="call-to-action">
|
||||
<h2>Set royalties for the creation</h2>
|
||||
<p>
|
||||
A royalty is a payment made by one the seller of this item to the
|
||||
creator. It is charged after every successful auction.
|
||||
A royalty is a payment made by the seller of this item to the creator.
|
||||
It is charged after every successful auction.
|
||||
</p>
|
||||
</Row>
|
||||
<Row className="content-action">
|
||||
<Col xl={12}>{file && <ArtCard file={file} />}</Col>
|
||||
<Col xl={12}>
|
||||
{file && (
|
||||
<ArtCard
|
||||
file={file}
|
||||
name={props.attributes.name}
|
||||
symbol={props.attributes.symbol}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
<Col className="section" xl={12}>
|
||||
<label className="action-field">
|
||||
<span className="field-title">Description</span>
|
||||
<Input
|
||||
className="input"
|
||||
placeholder="Max 50 characters"
|
||||
allowClear
|
||||
<span className="field-title">Royalty Percentage</span>
|
||||
<Slider
|
||||
min={0}
|
||||
max={100}
|
||||
onChange={(val: number) => {
|
||||
props.setAttributes({ ...props.attributes, royalty: val });
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</Col>
|
||||
|
@ -320,7 +355,20 @@ const RoyaltiesStep = (props: {
|
|||
|
||||
const LaunchStep = (props: { confirm: () => void; attributes: IProps }) => {
|
||||
const file = props.attributes.files[0];
|
||||
|
||||
const metadata = {
|
||||
...(props.attributes as any),
|
||||
files: props.attributes.files.map(f => f.name),
|
||||
};
|
||||
const [cost, setCost] = useState(0);
|
||||
useEffect(() => {
|
||||
getAssetCostToStore([
|
||||
...props.attributes.files,
|
||||
new File([JSON.stringify(metadata)], 'metadata.json'),
|
||||
]).then(lamports => {
|
||||
const sol = lamports / LAMPORT_MULTIPLIER;
|
||||
setCost(sol);
|
||||
});
|
||||
}, [file]);
|
||||
return (
|
||||
<>
|
||||
<Row className="call-to-action">
|
||||
|
@ -331,18 +379,26 @@ const LaunchStep = (props: { confirm: () => void; attributes: IProps }) => {
|
|||
</p>
|
||||
</Row>
|
||||
<Row className="content-action">
|
||||
<Col xl={12}>{file && <ArtCard file={file} />}</Col>
|
||||
<Col xl={12}>
|
||||
{file && (
|
||||
<ArtCard
|
||||
file={file}
|
||||
name={props.attributes.name}
|
||||
symbol={props.attributes.symbol}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
<Col className="section" xl={12}>
|
||||
<Statistic
|
||||
className="create-statistic"
|
||||
title="Royalty Percentage"
|
||||
value={20}
|
||||
value={props.attributes.royalty}
|
||||
suffix="%"
|
||||
/>
|
||||
<Statistic
|
||||
className="create-statistic"
|
||||
title="Cost to Create"
|
||||
value={20}
|
||||
value={cost}
|
||||
prefix="◎"
|
||||
/>
|
||||
</Col>
|
||||
|
|
Loading…
Reference in New Issue