Skip to content

Commit c02fd63

Browse files
committed
move fungible operations
1 parent e8e02e9 commit c02fd63

21 files changed

Lines changed: 852 additions & 610 deletions

File tree

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
'use client';
2+
3+
import * as z from 'zod';
4+
import {
5+
BytesValue,
6+
TypedValue,
7+
ContractCallPayloadBuilder,
8+
ContractFunction,
9+
} from '@multiversx/sdk-core';
10+
import { useForm, useWatch } from 'react-hook-form';
11+
import { zodResolver } from '@hookform/resolvers/zod';
12+
import { Form } from '@/components/ui/form';
13+
import {
14+
esdtTokenProperties,
15+
sftNftTokenProperties,
16+
commonOpertationsGasLimit,
17+
builtInSC,
18+
TokenPropertyOrRole,
19+
} from '@/components/operations/constants';
20+
import { OperationsCheckboxGroup } from '@/components/operations/operations-checkbox-group';
21+
import { OperationsSubmitButton } from '@/components/operations/operations-submit-button';
22+
import { useEffect } from 'react';
23+
import { CommonOpertationContentProps } from '@/components/operations/operations-common-types';
24+
import { OperationsSelectField } from '@/components/operations/operations-select-field';
25+
import { useCreatorTokens } from '@/hooks/use-creator-tokens';
26+
import { getTokenIdKey } from '@/lib/get-token-id';
27+
import { useTransaction } from '@useelven/core';
28+
import { OperationInfoBox } from '@/components/operation-info-box';
29+
import { useTxStatus } from '@/hooks/use-tx-status';
30+
31+
const formSchema = z.object({
32+
tokenId: z.string().min(1, 'The field is required'),
33+
properties: z.array(z.string()),
34+
});
35+
36+
interface CreatorTokens extends Record<string, string> {
37+
identifier: string;
38+
collection: string;
39+
}
40+
41+
const propertiesMap: Record<
42+
CommonOpertationContentProps['tokenType'],
43+
TokenPropertyOrRole[]
44+
> = {
45+
fungible: esdtTokenProperties,
46+
'non-fungible': sftNftTokenProperties,
47+
'semi-fungible': sftNftTokenProperties,
48+
meta: sftNftTokenProperties,
49+
};
50+
51+
export const ChangeProperties = ({
52+
tokenType,
53+
}: {
54+
tokenType: CommonOpertationContentProps['tokenType'];
55+
}) => {
56+
const { triggerTx, error, txResult, transaction, pending } = useTransaction();
57+
58+
const { tokens } = useCreatorTokens<CreatorTokens>({
59+
tokenType,
60+
});
61+
62+
const form = useForm<z.infer<typeof formSchema>>({
63+
resolver: zodResolver(formSchema),
64+
defaultValues: {
65+
tokenId: '',
66+
properties: [],
67+
},
68+
});
69+
70+
useTxStatus({
71+
successHash: txResult?.hash,
72+
pendingHash: transaction?.getHash()?.toString(),
73+
error,
74+
pending,
75+
});
76+
77+
const watchTokenId = useWatch({ control: form.control, name: 'tokenId' });
78+
79+
const onSubmit = ({ tokenId, properties }: z.infer<typeof formSchema>) => {
80+
const args: TypedValue[] = [BytesValue.fromUTF8(tokenId.trim())];
81+
82+
for (const property of propertiesMap[tokenType]) {
83+
let propertyEnabled = false;
84+
85+
if (properties.includes(property.name)) {
86+
propertyEnabled = true;
87+
}
88+
89+
args.push(BytesValue.fromUTF8(property.name));
90+
args.push(BytesValue.fromUTF8(propertyEnabled.toString()));
91+
}
92+
93+
// TODO: replace ContractCallPayloadBuilder
94+
const data = new ContractCallPayloadBuilder()
95+
.setFunction(new ContractFunction('controlChanges'))
96+
.setArgs(args)
97+
.build();
98+
99+
triggerTx?.({
100+
address: builtInSC,
101+
gasLimit: commonOpertationsGasLimit,
102+
data,
103+
value: 0,
104+
});
105+
};
106+
107+
useEffect(() => {
108+
const tokenData = tokens?.find(
109+
(token) => token[getTokenIdKey(tokenType)] === watchTokenId
110+
);
111+
if (tokenData) {
112+
const properties = propertiesMap[tokenType].filter((property) => {
113+
const key = property.name;
114+
return tokenData[
115+
key === 'canTransferNFTCreateRole' ? 'canTransferNftCreateRole' : key
116+
];
117+
});
118+
form.setValue(
119+
'properties',
120+
properties.map((property) => property.name)
121+
);
122+
}
123+
// eslint-disable-next-line react-hooks/exhaustive-deps
124+
}, [tokenType, tokens, watchTokenId]);
125+
126+
return (
127+
<>
128+
<OperationInfoBox error={error} txHash={txResult?.hash} />
129+
<Form {...form}>
130+
<form
131+
id="change-properties-form"
132+
onSubmit={form.handleSubmit(onSubmit)}
133+
className="space-y-8"
134+
>
135+
<div className="flex-1 overflow-auto p-1">
136+
<OperationsSelectField
137+
name="tokenId"
138+
label="Token id"
139+
description="Please provide your token id"
140+
options={
141+
tokens
142+
? tokens
143+
?.sort((a, b) => {
144+
const aTokenId = a[getTokenIdKey(tokenType)];
145+
const bTokenId = b[getTokenIdKey(tokenType)];
146+
if (aTokenId < bTokenId) return -1;
147+
if (aTokenId > bTokenId) return 1;
148+
return 0;
149+
})
150+
.map((token) => ({
151+
value: token[getTokenIdKey(tokenType)],
152+
label: token[getTokenIdKey(tokenType)],
153+
}))
154+
: []
155+
}
156+
/>
157+
<OperationsCheckboxGroup
158+
items={propertiesMap[tokenType]}
159+
name="properties"
160+
label="Token properties"
161+
description="Set new properties set for the ESDT token."
162+
/>
163+
</div>
164+
<OperationsSubmitButton formId="change-properties-form" />
165+
</form>
166+
</Form>
167+
</>
168+
);
169+
};

app/(operations)/common/toggle-special-roles.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ const rolesMap: Record<
4646
export const ToggleSpecialRoles = ({
4747
tokenType,
4848
}: {
49-
tokenType: CommonOpertationContentProps['tokenType'];
49+
tokenType: CommonOpertationContentProps['tokenType']; // TODO: update types, replace this one
5050
}) => {
5151
const { triggerTx, error, txResult, transaction, pending } = useTransaction();
5252

@@ -124,6 +124,7 @@ export const ToggleSpecialRoles = ({
124124
}
125125
}
126126

127+
// TODO: replace ContractCallPayloadBuilder
127128
const data = new ContractCallPayloadBuilder()
128129
.setFunction(
129130
new ContractFunction(
@@ -139,8 +140,6 @@ export const ToggleSpecialRoles = ({
139140
data,
140141
value: 0,
141142
});
142-
143-
form.reset();
144143
};
145144

146145
const rolesDescription = () => {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
'use client';
2+
3+
import * as z from 'zod';
4+
import {
5+
BytesValue,
6+
TypedValue,
7+
ContractCallPayloadBuilder,
8+
ContractFunction,
9+
Address,
10+
AddressValue,
11+
} from '@multiversx/sdk-core';
12+
import { useForm } from 'react-hook-form';
13+
14+
import { zodResolver } from '@hookform/resolvers/zod';
15+
import { Form } from '@/components/ui/form';
16+
import {
17+
builtInSC,
18+
commonOpertationsGasLimit,
19+
} from '@/components/operations/constants';
20+
import { OperationsInputField } from '@/components/operations/operations-input-field';
21+
import { OperationsSubmitButton } from '@/components/operations/operations-submit-button';
22+
import { CommonOpertationContentProps } from '@/components/operations/operations-common-types';
23+
import { OperationsTokenIdInput } from '@/components/operations/operations-tokenid-input';
24+
import { useTxStatus } from '@/hooks/use-tx-status';
25+
import { useTransaction } from '@useelven/core';
26+
import { OperationInfoBox } from '@/components/operation-info-box';
27+
28+
const formSchema = z.object({
29+
tokenId: z.string().min(1, 'The field is required'),
30+
address: z.string().min(1, 'The field is required'),
31+
});
32+
33+
export const TransferOwnership = ({
34+
tokenType,
35+
}: {
36+
tokenType: CommonOpertationContentProps['tokenType']; // TODO: update types, replace this one
37+
}) => {
38+
const { triggerTx, error, txResult, transaction, pending } = useTransaction();
39+
40+
const form = useForm<z.infer<typeof formSchema>>({
41+
resolver: zodResolver(formSchema),
42+
defaultValues: {
43+
tokenId: '',
44+
address: '',
45+
},
46+
});
47+
48+
useTxStatus({
49+
successHash: txResult?.hash,
50+
pendingHash: transaction?.getHash()?.toString(),
51+
error,
52+
pending,
53+
});
54+
55+
const onSubmit = ({ tokenId, address }: z.infer<typeof formSchema>) => {
56+
const args: TypedValue[] = [
57+
BytesValue.fromUTF8(tokenId.trim()),
58+
new AddressValue(new Address(address.trim())),
59+
];
60+
61+
// TODO: replace ContractCallPayloadBuilder
62+
const data = new ContractCallPayloadBuilder()
63+
.setFunction(new ContractFunction('transferOwnership'))
64+
.setArgs(args)
65+
.build();
66+
67+
triggerTx?.({
68+
address: builtInSC,
69+
gasLimit: commonOpertationsGasLimit,
70+
data,
71+
value: 0,
72+
});
73+
74+
form.reset();
75+
};
76+
77+
return (
78+
<>
79+
<OperationInfoBox error={error} txHash={txResult?.hash} />
80+
<Form {...form}>
81+
<form
82+
id="transfer-ownership-form"
83+
onSubmit={form.handleSubmit(onSubmit)}
84+
className="space-y-8"
85+
>
86+
<div className="flex-1 overflow-auto p-1">
87+
<OperationsTokenIdInput tokenType={tokenType} />
88+
<OperationsInputField
89+
name="address"
90+
label="Address"
91+
placeholder="Example: erd1..."
92+
description="Please provide the address of a new owner"
93+
/>
94+
</div>
95+
<OperationsSubmitButton formId="transfer-ownership-form" />
96+
</form>
97+
</Form>
98+
</>
99+
);
100+
};
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { NextPage } from 'next';
2+
import { ChangeProperties } from '../../common/change-properties';
3+
import { Separator } from '@/components/ui/separator';
4+
5+
const ChangePropertiesPage: NextPage = () => {
6+
return (
7+
<div>
8+
<div className="mb-6 flex flex-col">
9+
<h1 className="mb-3 scroll-m-20 text-2xl font-semibold leading-none tracking-tight">
10+
Change properties of a fungible ESDT
11+
</h1>
12+
<p className="text-sm text-muted-foreground">
13+
The manager of an ESDT token may individually change any of the
14+
properties of the token, or multiple properties at once. The token
15+
should have the canUpgrade property set to true.
16+
</p>
17+
</div>
18+
<ChangeProperties tokenType="fungible" />
19+
<Separator className="my-12" />
20+
<div className="text-xs">
21+
<p className="mb-3">
22+
In the rapidly evolving landscape of blockchain technology, the
23+
ability for a manager of an eStandard Digital Token (ESDT) to modify
24+
the properties of the token represents a significant advantage. This
25+
flexibility allows the manager to adjust any single property or
26+
simultaneously change multiple properties, tailoring the token to meet
27+
shifting market demands and regulatory requirements. To enable such
28+
modifications, the token must have the canUpgrade property activated.
29+
This feature is crucial for ensuring that ESDT tokens remain
30+
competitive and compliant, as it allows for ongoing adjustments and
31+
enhancements that can improve functionality, security, and overall
32+
performance.
33+
</p>
34+
<p className="mb-3">
35+
The capacity to upgrade token properties is not only vital for
36+
adapting to changes in the digital currency ecosystem but also
37+
enhances the token&apos;s utility and market positioning. By allowing
38+
token managers to implement changes efficiently, the MultiversX
39+
network supports a dynamic and responsive management approach. This
40+
adaptability is essential for maintaining relevance in the face of
41+
technological advancements and changing user needs. It also ensures
42+
that stakeholders can leverage the most current features while
43+
adhering to the highest standards of security and compliance,
44+
fostering trust and stability within the blockchain environment.
45+
</p>
46+
</div>
47+
</div>
48+
);
49+
};
50+
51+
export default ChangePropertiesPage;

0 commit comments

Comments
 (0)