Skip to content

Commit

Permalink
add change nft attributes functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
juliancwirko committed Oct 25, 2023
1 parent 8139fc1 commit bb1b904
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 11 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### [0.4.0](https://github.com/xdevguild/buildo.dev/releases/tag/v0.4.0) (2023-10-25)
- add change NFT attributes functionality

### [0.3.0](https://github.com/xdevguild/buildo.dev/releases/tag/v0.3.0) (2023-10-23)
- add herotag functionality
- add check address (also by herotag) utility
Expand Down
8 changes: 8 additions & 0 deletions components/home-cards.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,14 @@ export const HomeCards = () => {
setDialogState('nonFungibleEsdt', 'changeProperties');
},
},
{
title: 'Change attributes',
description:
'An user that has the ESDTRoleNFTUpdateAttributes role set for a given ESDT, can change the attributes of a given NFT',
onClick: () => {
setDialogState('nonFungibleEsdt', 'changeAttributes');
},
},
{
title: 'Send',
description: 'Send NFT',
Expand Down
5 changes: 4 additions & 1 deletion components/operations/meta-tokens/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@ export const Create = ({ triggerTx, close }: OperationContentProps) => {

triggerTx?.({
address,
gasLimit: nftSftCreateOpertationsGasLimit + data.length() * 1500 + 50000,
gasLimit:
nftSftCreateOpertationsGasLimit +
data.length() * 1500 +
attributes.length * 50000,
data,
value: 0,
});
Expand Down
157 changes: 157 additions & 0 deletions components/operations/non-fungible-tokens/change-attributes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import * as z from 'zod';
import {
BigUIntValue,
BytesValue,
TypedValue,
ContractCallPayloadBuilder,
ContractFunction,
} from '@multiversx/sdk-core';
import Bignumber from 'bignumber.js';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { Form } from '@/components/ui/form';
import {
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { OperationsInputField } from '@/components/operations/operations-input-field';
import { OperationsSubmitButton } from '../operations-submit-button';
import { useContext } from 'react';
import { OperationsStateDialogContext } from '@/components/operations/operations-status-dialog';
import { OperationContentProps } from '@/components/operations/operations-common-types';
import { useAccount, useConfig } from '@useelven/core';
import axios from 'axios';
import { specialOpertationsGasLimit } from '../constants';

const formSchema = z.object({
tokenId: z.string().min(1, 'The field is required'),
attributes: z
.string()
.min(
1,
'The field is required. You will need at least metadata.json attribute.'
),
});

export const ChangeAttributes = ({
triggerTx,
close,
}: OperationContentProps) => {
const { address } = useAccount();
const { apiAddress } = useConfig();

const { setOpen: setTxStatusDialogOpen } = useContext(
OperationsStateDialogContext
);

const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
tokenId: '',
attributes: '',
},
});

const onSubmit = async ({
tokenId,
attributes,
}: z.infer<typeof formSchema>) => {
try {
// TODO: replace with useElven useApiCall when ready to handle such cases
const tokenOnNetwork = await axios.get<{ nonce: number; ticker: string }>(
`${apiAddress}/nfts/${tokenId.trim()}`,
{
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
}
);

const nonce = tokenOnNetwork?.data?.nonce;
const collectionTicker = tokenOnNetwork?.data?.ticker;

// TODO: show the error in the transaction status modal
if (!nonce || !collectionTicker) {
console.error(
"Can't read the nonce or/and collection ticker of the token, using MultiversX API!"
);
return;
}

const args: TypedValue[] = [
BytesValue.fromUTF8(collectionTicker.trim()),
new BigUIntValue(new Bignumber(nonce)),
BytesValue.fromUTF8(attributes.trim()),
];

const data = new ContractCallPayloadBuilder()
.setFunction(new ContractFunction('ESDTNFTUpdateAttributes'))
.setArgs(args)
.build();

triggerTx?.({
address,
gasLimit:
specialOpertationsGasLimit +
data.length() * 1500 +
attributes?.length * 50000,
data,
value: 0,
});

setTxStatusDialogOpen(true);
form.reset();
close();
} catch (e) {
console.error(
"Can't read the nonce or/and collection ticker of the token, using MultiversX API!",
e
);
}
};

return (
<>
<DialogHeader className="p-8 pb-0">
<DialogTitle>Change attributes</DialogTitle>
<DialogDescription>
An user that has the ESDTRoleNFTUpdateAttributes role set for a given
ESDT, can change the attributes of a given NFT.
ESDTNFTUpdateAttributes will remove the old attributes and add the new
ones. Therefore, if you want to keep the old attributes you will have
to pass them along with the new ones.
</DialogDescription>
</DialogHeader>
<div className="overflow-y-auto py-0 px-8">
<Form {...form}>
<form
id="nft-change-attributes-form"
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-8"
>
<div className="flex-1 overflow-auto p-1">
<OperationsInputField
name="tokenId"
label="Token id"
placeholder="Example: MyToken-23432-01"
description="Please provide your token id"
/>
<OperationsInputField
name="attributes"
label="Attributes"
placeholder="Example: metadata:{ipfsCID_here}/metadata.json;tags:tag1,tag2,tag3"
description="Provide attributes. In most cases you'll need the metadata attribute which points to JSON file on IPFS. Separate with ';'"
/>
</div>
</form>
</Form>
</div>
<DialogFooter className="py-4 px-8">
<OperationsSubmitButton formId="nft-change-attributes-form" />
</DialogFooter>
</>
);
};
5 changes: 2 additions & 3 deletions components/operations/non-fungible-tokens/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,7 @@ export const Create = ({ triggerTx, close }: OperationContentProps) => {
gasLimit:
nftSftCreateOpertationsGasLimit +
data.length() * 1500 +
(attributes?.length || 0) +
(hash?.length || 0) * 50000,
(attributes?.length || 0 + (hash?.length || 0)) * 50000,
data,
value: 0,
});
Expand Down Expand Up @@ -154,7 +153,7 @@ export const Create = ({ triggerTx, close }: OperationContentProps) => {
name="attributes"
label="Attributes"
placeholder="Example: metadata:{ipfsCID_here}/metadata.json;tags:tag1,tag2,tag3"
description="Provide attributes. In most cases yoy'll need the metadata attribute which points to JSON file on IPFS. Separate with ';'"
description="Provide attributes. In most cases you'll need the metadata attribute which points to JSON file on IPFS. Separate with ';'"
/>
<OperationsInputField
name="hash"
Expand Down
9 changes: 7 additions & 2 deletions components/operations/operations-content-map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import { AccountStorage } from '@/components/operations/general/account-storage'
import { DecodeTransaction } from '@/components/operations/utils-operations/decode-transaction';
import { CheckTokenData } from '@/components/operations/utils-operations/check-token-data';
import { CheckAddressData } from '@/components/operations/utils-operations/check-address-data';
import { Herotag } from './general/herotag';
import { Herotag } from '@/components/operations/general/herotag';
import { ChangeAttributes } from '@/components/operations/non-fungible-tokens/change-attributes';

export type OperationsContentMap = Record<
string,
Expand Down Expand Up @@ -183,6 +184,10 @@ export const getOperationsContentsMap = ({
),
additionalInfo: 'You have changed the properties of a non-fungible ESDT.',
},
changeAttributes: {
component: <ChangeAttributes triggerTx={triggerTx} close={closeDialog} />,
additionalInfo: 'You have changed the attributes of a non-fungible ESDT.',
},
send: {
component: <NftSend transfer={transfer} close={closeDialog} />,
tokenTransfer: true,
Expand Down Expand Up @@ -304,7 +309,7 @@ export const getOperationsContentsMap = ({
send: {
component: <MetaSend transfer={transfer} close={closeDialog} />,
tokenTransfer: true,
additionalInfo: 'You have sent the Meta ESDT amount.',
additionalInfo: 'You have sent the meta ESDT amount.',
},
},
utilities: {
Expand Down
5 changes: 2 additions & 3 deletions components/operations/semi-fungible-tokens/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ export const Create = ({ triggerTx, close }: OperationContentProps) => {
gasLimit:
nftSftCreateOpertationsGasLimit +
data.length() * 1500 +
(attributes?.length || 0) +
(hash?.length || 0) * 50000,
(attributes?.length || 0 + (hash?.length || 0)) * 50000,
data,
value: 0,
});
Expand Down Expand Up @@ -168,7 +167,7 @@ export const Create = ({ triggerTx, close }: OperationContentProps) => {
name="attributes"
label="Attributes"
placeholder="Example: metadata:{ipfsCID_here}/metadata.json;tags:tag1,tag2,tag3"
description="Provide attributes. In most cases yoy'll need the metadata attribute which points to JSON file on IPFS. Separate with ';'"
description="Provide attributes. In most cases you'll need the metadata attribute which points to JSON file on IPFS. Separate with ';'"
/>
<OperationsInputField
name="hash"
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "buildo.dev",
"version": "0.3.0",
"version": "0.4.0",
"author": "Julian Ćwirko <julian.io>",
"license": "MIT",
"homepage": "https://www.buildo.dev",
Expand Down Expand Up @@ -36,7 +36,7 @@
"buffer": "6.0.3",
"class-variance-authority": "0.7.0",
"clsx": "2.0.0",
"keccak": "^3.0.4",
"keccak": "3.0.4",
"lucide-react": "0.288.0",
"next": "13.5.6",
"next-themes": "0.2.1",
Expand Down

1 comment on commit bb1b904

@vercel
Copy link

@vercel vercel bot commented on bb1b904 Oct 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.