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/spirekey sdk market place #2415

Merged
merged 12 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
2 changes: 1 addition & 1 deletion packages/apps/marmalade-marketplace/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ NEXT_PUBLIC_API_URL=http://localhost:3000
NEXT_PUBLIC_WALLET_URL=https://spirekey.kadena.io/

NEXT_PUBLIC_CHAINWEB_API_HOST=https://api.testnet.chainweb.com
NEXT_PUBLIC_NETWORK_NAME=testnet04
NEXT_PUBLIC_NETWORKID=testnet04
NEXT_PUBLIC_CHAIN_IDS="0,1"

# Optional parameter to specify the start block for the event indexer.
Expand Down
2 changes: 2 additions & 0 deletions packages/apps/marmalade-marketplace/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,5 @@ yarn-error.log*

# firebase files
.firebaserc
.vercel
.env*.local
3 changes: 2 additions & 1 deletion packages/apps/marmalade-marketplace/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"dev": "pnpm generate && next dev",
"generate": "npx graphql-codegen",
"start": "next start",
"start:cron": "ts-node scripts/cron.ts",
"start:cron": "TS_NODE_PROJECT='./tsconfig.cron.json' ts-node scripts/cron.ts",
"test": "vitest run",
"test:coverage": "vitest run --coverage"
},
Expand All @@ -21,6 +21,7 @@
"@kadena/kode-icons": "workspace:*",
"@kadena/kode-ui": "workspace:*",
"@kadena/pactjs": "workspace:*",
"@kadena/spirekey-sdk": "0.0.1-dev-8",
"@pinata/sdk": "^2.1.0",
"firebase": "^10.8.0",
"formidable": "^3.5.1",
Expand Down
263 changes: 128 additions & 135 deletions packages/apps/marmalade-marketplace/src/components/CreateToken/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ import GenerateURIForm from '@/components/GenerateURIForm';
import CrudCard from '@/components/CrudCard';

// Import client
import { ChainId } from '@kadena/client';
import { ChainId, ICommand, IUnsignedCommand } from '@kadena/client';
import { createTokenId, createToken, ICreateTokenPolicyConfig } from '@kadena/client-utils/marmalade';
import { useAccount } from '@/hooks/account';
import { getPolicies, formatGuardInput, formatRoyaltyInput, createPrecision, formatAccount, generateSpireKeyGasCapability } from '@/utils/helper';
import { createSignWithSpireKey } from '@/utils/signWithSpireKey';
import SendTransaction from '@/components/SendTransaction';
import { createSignWithSpireKeySDK } from '@/utils/signWithSpireKey';
import { useTransaction } from '@/hooks/transaction';

function CreateTokenComponent() {
const router = useRouter();
const { account, webauthnAccount } = useAccount();
const { transaction, send, preview, poll } = useTransaction();
const { setTransaction } = useTransaction();

const excluded = "[EXCLUDED]";
let tokenId = '';

const [walletKey, setWalletKey] = useState<string>('');
const [file, setFile] = useState<File | null>(null);
Expand Down Expand Up @@ -94,11 +94,16 @@ function CreateTokenComponent() {
}
}, [webauthnAccount]);

const onTransactionSigned = (transaction: IUnsignedCommand | ICommand) => {
setTransaction(transaction);
router.push(`/transaction?returnUrl=/mint?tokenId=${tokenId}`);
}

const config = {
host: env.URL,
networkId: env.NETWORKID,
chainId: tokenInput.chainId as ChainId,
sign: createSignWithSpireKey(router, { host: env.WALLET_URL ?? '' }),
sign: createSignWithSpireKeySDK([account], onTransactionSigned),
};

const handleTokenInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
Expand Down Expand Up @@ -162,8 +167,7 @@ function CreateTokenComponent() {
}
};

const handleSubmit = async (event: FormEvent) => {
event.preventDefault();
const handleSubmit = async () => {
setUploading(true);
try {
if (!account) throw new Error("Connect Spirekey account");
Expand All @@ -182,6 +186,7 @@ function CreateTokenComponent() {
const processTokenCreation = async (inputs: any) => {
try {
const tokenIdCreated = await createTokenId({ ...inputs, networkId: config.networkId, host: config.host });
tokenId = tokenIdCreated;

await createToken(
{
Expand Down Expand Up @@ -233,7 +238,6 @@ function CreateTokenComponent() {

const uploadMetadata = async (metadata: any) => {
try {
setUploading(true);
const formData = new FormData();
const metadataContent = JSON.stringify(metadata);
const metadataBlob = new Blob([metadataContent], { type: 'application/json' });
Expand All @@ -244,30 +248,25 @@ function CreateTokenComponent() {
body: formData,
});
const ipfsHash = await res.text();
setUploading(false);
return `ipfs://${ipfsHash}`;
} catch (e) {
console.error(e);
setUploading(false);
alert('Trouble uploading file');
}
};

const uploadFile = async (fileToUpload: any) => {
try {
setUploading(true);
const formData = new FormData();
formData.append('file', fileToUpload, fileToUpload.name);
const res = await fetch('/api/files', {
method: 'POST',
body: formData,
});
const ipfsHash = await res.text();
setUploading(false);
return `ipfs://${ipfsHash}`;
} catch (e) {
console.error(e);
setUploading(false);
alert('Trouble uploading file');
}
};
Expand All @@ -281,7 +280,7 @@ function CreateTokenComponent() {
...input,
chainId: input.chainId as ChainId,
precision: createPrecision(input.precision),
creator: formatAccount(account?.accountName || '', account?.credentials[0].publicKey || ''),
creator: formatAccount(account?.accountName || '', account?.devices[0].guard.keys[0] || ''),
};
};

Expand All @@ -298,138 +297,132 @@ function CreateTokenComponent() {
};

return (
<>
{!transaction ? (
<div>
<Stack flex={1} flexDirection="column" className={styles.container}>
<CrudCard
headingSize="h3"
titleIcon={<MonoAutoFixHigh />}
title="Create Token"
description={[
"Create a new token",
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi",
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore",
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia"
]}
>
<div>
<GenerateURIForm
handleTokenInputChange={handleTokenInputChange}
tokenInput={tokenInput}
setError={setError}
file={file}
setFile={setFile}
imagePreview={imagePreview}
setImagePreview={setImagePreview}
base64Image={base64Image}
setBase64Image={setBase64Image}
/>
<div className={styles.formContainer}>
<TextField
label="Creation Guard"
name="CreationGuard"
value={walletKey}
disabled
/>
<NumberField
label="Precision"
value={tokenInput.precision}
onValueChange={handlePrecisionChange}
/>
<Select label="Chain" name="chainId" selectedKey={tokenInput.chainId} isDisabled>
{Array.from({ length: 20 }, (_, i) => i.toString()).map(option => (
<SelectItem key={option} textValue={option}>{option}</SelectItem>
))}
</Select>
</div>
</div>
</CrudCard>
<CrudCard
title="Metadata"
description={["Select the metadata input that will be stored as the uri"]}
>
<div>
<Stack flex={1} flexDirection="column" className={styles.container}>
<CrudCard
headingSize="h3"
titleIcon={<MonoAutoFixHigh />}
title="Create Token"
description={[
"Create a new token",
"Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi",
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore",
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia"
]}
>
<div>
<GenerateURIForm
handleTokenInputChange={handleTokenInputChange}
tokenInput={tokenInput}
setError={setError}
file={file}
setFile={setFile}
imagePreview={imagePreview}
setImagePreview={setImagePreview}
base64Image={base64Image}
setBase64Image={setBase64Image}
/>
<div className={styles.formContainer}>
<TextField
label="Name"
name="metadataName"
value={tokenInput.metadataName as string}
onChange={handleTokenInputChange}
label="Creation Guard"
name="CreationGuard"
value={walletKey}
disabled
/>
<TextareaField
label="Description"
name="metadataDescription"
value={tokenInput.metadataDescription as string}
onChange={handleTokenInputChange}
<NumberField
label="Precision"
value={tokenInput.precision}
onValueChange={handlePrecisionChange}
/>
<Select label="Chain" name="chainId" selectedKey={tokenInput.chainId} isDisabled>
{Array.from({ length: 20 }, (_, i) => i.toString()).map(option => (
<SelectItem key={option} textValue={option}>{option}</SelectItem>
))}
</Select>
</div>
{/* <TextField
label="Author"
name="metadataAuthors"
value={tokenInput.metadataAuthors as string}
onChange={handleTokenInputChange}
info="(optional)"
/>
</div>
</CrudCard>
<CrudCard
title="Metadata"
description={["Select the metadata input that will be stored as the uri"]}
>
<div className={styles.formContainer}>
<TextField
label="Collection Name"
name="metadataCollectionName"
value={tokenInput.metadataCollectionName as string}
label="Name"
name="metadataName"
value={tokenInput.metadataName as string}
onChange={handleTokenInputChange}
info="(optional)"
/>
<TextField
label="Collection Family"
name="metadataCollectionFamily"
value={tokenInput.metadataCollectionFamily as string}
<TextareaField
label="Description"
name="metadataDescription"
value={tokenInput.metadataDescription as string}
onChange={handleTokenInputChange}
info="(optional)"
/> */}
</CrudCard>
<CrudCard
title="Policies"
description={["Select the metadata input that will be stored as the uri"]}
>
<PolicyForm handleCheckboxChange={handleCheckboxChange} />
</CrudCard>
{(policyConfig.guarded) && <GuardForm guardInput={guardInput} handleGuardInputChange={handleGuardInputChange} handleGuardExcludeChange={handleGuardExcludeChange} excluded={excluded} />}
{policyConfig.hasRoyalty && <RoyaltyForm royaltyInput={royaltyInput} handleRoyaltyInputChange={handleRoyaltyInputChange} /> }
{policyConfig.collection && <CollectionForm collectionInput={collectionInput} handleCollectionInputChange={handleCollectionInputChange} /> }
{policyConfig.nonFungible && (<CrudCard
title="Non-fungible"
description={[
"Enforces that token is non-fungible by setting max-supply to 1 and precision to 0"
]}
>
No data required
</CrudCard>)}
{policyConfig.nonUpdatableURI && (<CrudCard
title="Non-upgradable URI"
description={[
"Enforces that token's URI is not updatable. If not selected, a URI guard is required"
]}
/>
</div>
{/* <TextField
label="Author"
name="metadataAuthors"
value={tokenInput.metadataAuthors as string}
onChange={handleTokenInputChange}
info="(optional)"
/>
<TextField
label="Collection Name"
name="metadataCollectionName"
value={tokenInput.metadataCollectionName as string}
onChange={handleTokenInputChange}
info="(optional)"
/>
<TextField
label="Collection Family"
name="metadataCollectionFamily"
value={tokenInput.metadataCollectionFamily as string}
onChange={handleTokenInputChange}
info="(optional)"
/> */}
</CrudCard>
<CrudCard
title="Policies"
description={["Select the metadata input that will be stored as the uri"]}
>
No data required
</CrudCard>)}

{error && (
<div className={styles.errorBox}>
<p>Error: {error}</p>
</div>
)}
</Stack>
<div className={styles.buttonRow}>
<Button variant="outlined" onPress={onCancelPress}>
Cancel
</Button>
<Button isDisabled={uploading} loadingLabel="Creating Token..." isLoading={uploading} type="submit" onClick={handleSubmit}>
Create Token
</Button>
</div>
<PolicyForm handleCheckboxChange={handleCheckboxChange} />
</CrudCard>
{(policyConfig.guarded) && <GuardForm guardInput={guardInput} handleGuardInputChange={handleGuardInputChange} handleGuardExcludeChange={handleGuardExcludeChange} excluded={excluded} />}
{policyConfig.hasRoyalty && <RoyaltyForm royaltyInput={royaltyInput} handleRoyaltyInputChange={handleRoyaltyInputChange} /> }
{policyConfig.collection && <CollectionForm collectionInput={collectionInput} handleCollectionInputChange={handleCollectionInputChange} /> }
{policyConfig.nonFungible && (<CrudCard
title="Non-fungible"
description={[
"Enforces that token is non-fungible by setting max-supply to 1 and precision to 0"
]}
>
No data required
</CrudCard>)}
{policyConfig.nonUpdatableURI && (<CrudCard
title="Non-upgradable URI"
description={[
"Enforces that token's URI is not updatable. If not selected, a URI guard is required"
]}
>
No data required
</CrudCard>)}

{error && (
<div className={styles.errorBox}>
<p>Error: {error}</p>
</div>
)}
</Stack>
<div className={styles.buttonRow}>
<Button variant="outlined" onPress={onCancelPress}>
Cancel
</Button>
<Button isDisabled={uploading} loadingLabel="Creating Token..." isLoading={uploading} onPress={handleSubmit}>
Create Token
</Button>
</div>
) : (
<SendTransaction send={send} preview={preview} poll={poll} transaction={transaction} />
)}
</>
</div>
);
}

Expand Down
Loading
Loading