bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 41쪽 중 16쪽
net Contract and sets it in the state.
By following these steps, we'll configure the Portkey provider to connect users' wallets to your app and interact with the multi-token smart contract including NFT related functionalities. This setup will enable our frontend components to perform actions like
create NFTs
,
validate NFTs
, and
transfer NFTs
.
Configure Connect Wallet Function
Step 1: Locate the File
Go to the
src/components/layout/header/index.tsx
file.
Step 2: Write the Connect Wallet Function
The
header/index.tsx
file is the header of our NFT dApp. It allows users to connect their Portkey wallet with the NFT dApp.
Before users can interact with the smart contract, we need to write the
Connect Wallet
function.
Find the comment
// Step C - Connect Portkey Wallet
.
Replace the existing connect function with this code snippet:
header/index.tsx
const
connect
=
async
(
walletProvider
?
:
IPortkeyProvider
)
=>
{
// Step C - Connect Portkey Wallet
const
accounts
=
await
(
walletProvider
?
walletProvider
:
provider
)
?.
request
(
{
method
:
MethodsBase
.
REQUEST_ACCOUNTS
,
}
)
;
const
account
=
accounts
?.
AELF
&&
accounts
?.
AELF
[
0
]
;
if
(
account
)
{
setCurrentWalletAddress
(
account
.
replace
(
/
^
ELF_
/
,
""
)
.
replace
(
/
_AELF
$
/
,
""
)
)
;
setIsConnected
(
true
)
;
}
!
walletProvider
&&
toast
.
success
(
"Successfully connected"
)
;
}
;
Explanation:
connect
Function
: This function connects the user's Portkey wallet with the dApp.
Fetch Accounts
: It fetches the wallet accounts using the provider.
Log Accounts
: Logs the accounts to the console for debugging.
Set Wallet Address
: Sets the current wallet address state variable with the fetched account.
Update Connection Status
: Updates the state to indicate that the wallet is connected.
User Notification
: Displays an alert to notify the user that their wallet is successfully connected.
In this code, we fetch the Portkey wallet account using the provider and update the wallet address state variable. An alert notifies the user that their wallet is successfully connected.
With the Connect Wallet function defined, we're ready to write the remaining functions in the next steps.
Configure Create NFT Form Code
Step 1: Locate the File
Go to the
src/pages/create-nft/index.tsx
file. This file is the
Create NFTs
page where users can enter details like the
tokenName
,
symbol
,
totalSupply
and
decimals
.
Step 2: Prepare Form to Create NFTs
Find the comment
// Step D - Configure NFT Form
.
Replace the form variable with this code snippet:
create-nft/index.tsx
// Step D - Configure NFT Form
const
form
=
useForm
<
z
.
infer
<
typeof
formSchema
>>
(
{
resolver
:
zodResolver
(
formSchema
)
,
defaultValues
:
{
tokenName
:
""
,
symbol
:
""
,
totalSupply
:
""
,
decimals
:
""
,
}
,
}
)
;
Here's what the function does:
Initializes a new form variable with default values needed to create a nft.
Fields include:
tokenName
,
symbol
,
totalSupply
, and
decimals
.
Now the form is ready for users to fill in the necessary details for their NFT function interaction.
Create NFT Collection
Let's write the functions to
Create New NFT Collection
on the aelf mainchain and the dAppChain.
Step 1: Write the function to
Create New NFT Collection
on the MainChain
The
create-nft/index.tsx
file includes the code to create NFTs. It allows users to create new NFTs.
Find the comment
// step 1 - Create New NFT Collection on the mainchain
.
Replace the existing
createNftCollectionOnMainChain
function with this code snippet:
create-nft/index.tsx
// step - 1 Create New NFT Collection on the mainchain
const
createNftCollectionOnMainChain
=
async
(
values
:
{
tokenName
:
string
;
symbol
:
string
;
totalSupply
:
string
;
decimals
:
string
;
}
)
=>
{
let
createLoadingId
;
try
{
createLoadingId
=
toast
.
loading
(
"Creating NFT Collection.."
)
;
// Create an object with the necessary information for the new NFT collection.
const
createNtfInput
:
INftInput
=
{
tokenName
:
values
.
tokenName
,
// Name of the nft Collection
symbol
:
values
.
symbol
,
// Symbol of the token (You have to get it from your PortKey wallet on NFT seed from NFT section)
totalSupply
:
values
.
totalSupply
,
// Total supply of the token
decimals
:
values
.
decimals
,
// Decimals of the token
issuer
:
currentWalletAddress
,
// Address of the token issuer
isBurnable
:
true
,
// Indicates if the token can be burned
issueChainId
:
sidechain_from_chain_id
,
// ID of the issuing chain
owner
:
currentWalletAddress
,
// Owner's wallet address
}
;
// Call the smart contract method to create the new NFT collection on the main chain.
const
result
=
await
mainChainSmartContract
?.
callSendMethod
(
"Create"
,
currentWalletAddress
,
createNtfInput
)
;
// Log the result of the creation for debugging purposes.
console
.
log
(
"========= result of createNewNft ========="
,
result
)
;
toast
.
update
(
createLoadingId
,
{
render
:
"NFT Collection Created Successfully On MainChain"
,
type
:
"success"
,
isLoading
:
false
,
}
)
;
removeNotification
(
createLoadingId
)
;
// Return the input data for further use.
return
createNtfInput
;
}
catch
(
error
:
any
)
{
// If there's an error, log it and alert the user.
console
.
error
(
error
.
message
,
"=====error"
)
;
toast
.
update
(
createLoadingId
,
{
render
:
error
.
message
,
type
:
"error"
,
isLoading
:
false
,
}
)
;
removeNotification
(
createLoadingId
)
;
return
"error"
;
}
}
;
tip
ℹ️ Note: You need to get
symbol
from the Portkey wallet.
Follow Steps to get NFT symbol from Portkey Wallet:
Open Portkey Wallet.
Go to the NFTs tab.
You will find the SEED that you already got from the above
seed generation
step.
Click on the SEED to see details.
You will find the
Token Symbol
inside the
Token Creation via This Seed
section.
Copy and use that value of the token symbol.
What This Function Does:
Creates an Object with NFT Details
: It prepares the data needed to create a new NFT collection.
Calls Smart Contract Method
: It interacts with the blockchain smart contract to create the new NFT collection using the prepared data.
Logs Information
: It logs the result for debugging purposes.
Return Values of Object
: It returns necessary values as a object.
Next, we'll write the
Validate Collection Info Exist
function.
Step 2: Write the validates collection info exist function
Scroll up to find the comment
// step 2 - Validate Collection information exist
.
Replace the existing
validateNftCollectionInfo
function with this code snippet:
create-nft/index.tsx
// step 2 - Validate Collection information exist
// This function validates if the token collection information already exists on the main blockchain.
const
validateNftCollectionInfo
=
async
(
values
:
INftInput
)
=>
{
let
validateLoadingId
try
{
// Start Loading before initiate the transaction
validateLoadingId
=
toast
.
loading
(
<
CustomToast
title
=
"Transaction is getting validated on aelf blockchain. Please wait!"
message
=
"Validation means transaction runs through a consensus algorithm to be selected or rejected. Once the status changes process will complete. It usually takes some time in distributed systems."
/
>
)
;
// Create an object with the necessary information for token validation.
const
validateInput
=
{
symbol
:
values
.
symbol
,
// Symbol of the token
tokenName
:
values
.
tokenName
,
// Name of the token
totalSupply
:
values
.
totalSupply
,
// Total supply of the token
decimals
:
values
.
decimals
,
// Decimals of the token
issuer
:
currentWalletAddress
,
// Address of the token issuer
isBurnable
:
true
,
// Indicates if the token can be burned
issueChainId
:
sidechain_from_chain_id
,
// ID of the issuing chain
owner
:
currentWalletAddress
,
// Owner's wallet address
}
;
// get mainnet contract
const
aelfTokenContract
=
await
getTokenContract
(
aelf
,
wallet
)
;
// prepare Sign the transaction using contract method (ValidateTokenInfoExists Function)
const
signedTx
=
aelfTokenContract
.
ValidateTokenInfoExists
.
getSignedTx
(
validateInput
)
;
// send the transaction using signed Transaction
const
{
TransactionId
:
VALIDATE_TXID
}
=
await
aelf
.
chain
.
sendTransaction
(
signedTx
)
;
// get Validate Result
let
VALIDATE_TXRESULT
=
await
aelf
.
chain
.
getTxResult
(
VALIDATE_TXID
)
;
// we need to wait till our latest index Hight grater than or equal to our Transaction block number
let
heightDone
=
false
;
while
(
!
heightDone
)
{
// get latest index Hight
const
sideIndexMainHeight
=
await
GetParentChainHeight
(
)
;
if
(
// check the latest index Hight is grater than or equal
sideIndexMainHeight
>=
VALIDATE_TXRESULT
.
Transaction
.
RefBlockNumber
)
{
VALIDATE_TXRESULT
=
await
aelf
.
chain
.
getTxResult
(
VALIDATE_TXID
)
;
heightDone
=
true
;
}
}
console
.
log
(
"VALIDATE_TXRESULT"
,
VALIDATE_TXRESULT
)
;
// Update the Loading Message
toast
.
update
(
validateLoadingId
,
{
render
:
"Validating Token Successfully Executed"
,
type
:
"success"
,
isLoading
:
false
,
}
)
;
// Remove the Loading Message
removeNotification
(
validateLoadingId
)
;
// Return necessary details.
return
{
transactionId
:
VALIDATE_TXID
,
signedTx
:
signedTx
,
BlockNumber
:
VALIDATE_TXRESULT
.
BlockNumber
,
}
;
}
catch
(
error
:
any
)
{
// If there's an error, log it and alert the user.
console
.
error
(
error
.
message
,
"=====error in validateTokenInfoExist"
)
;
toast
.
update
(
validateLoadingId
,
{
render
:
error
.
message
,
type
:
"error"
,
isLoading
:
false
,
}
)
;
removeNotification
(
validateLoadingId
)
;
return
"error"
;
}
}
;
What This Function Does:
Creates an Object with Validate Collection Details
: It prepares the data needed to validate the token information.
Calls Smart Contract Method
: It interacts with the multi-token smart contract method to check if the token information already exists using the prepared data.
Return Values
: It returns necessary values as an object.
Next, we'll write the
Get the parent chain height
function.
Step 3: Write the get the parent chain height function
Scroll up to find the comment
// Step 3: Get the parent chain height
.
Replace the existing
GetParentChainHeight
function with the following code snippet:
create-nft/index.tsx
// Step 3: Get the parent chain height
// This function fetches the current height of the parent blockchain.
const
GetParentChainHeight
=
async
(
)
=>
{
try
{
const
tdvwCrossChainContract
=
await
getCrossChainContract
(
tdvw
,
wallet
)
;
// Call the smart contract method to get the parent chain height.
const
result
=
await
tdvwCrossChainContract
.
GetParentChainHeight
.
call
(
)
// Return the parent chain height if it exists, otherwise return an empty string.
return
result
?
(
result
.
value
as
string
)
:
""
;
}
catch
(
error
:
any
)
{
// If there's an error, log it and return an error status.
console
.
error
(
error
,
"=====error in GetParentChainHeight"
)
;
return
"error"
;
}
}
;
What This Function Does:
Calls Smart Contract Method
: It interacts with the side chain smart contract method to fetch the current height of the parent blockchain.
Returns Parent Chain's Height
: It returns the parent chain's height if it exists.
Next, we'll write the
Fetch the Merkle path
function.
Step 4: Write the fetch the merkle path function
Scroll up to find the comment
// step 4 - Fetch the merkle path by transaction Id
.
Replace the existing
getMerklePathByTxId
function with this code snippet:
create-nft/index.tsx
// step 4 - Fetch the merkle path by transaction Id
const
getMerklePathByTxId
=
async
(
aelf
:
any
,
txId
:
string
)
=>
{
try
{
const
{
MerklePathNodes
}
=
await
aelf
.
chain
.
getMerklePathByTxId
(
txId
)
;
const
formattedMerklePathNodes
=
MerklePathNodes
.
map
(
(
{
Hash
,
IsLeftChildNode
,
}
:
{
Hash
:
string
,
IsLeftChildNode
:
boolean
,
}
)
=>
(
{
hash
:
Hash
,
isLeftChildNode
:
IsLeftChildNode
,
}
)
)
;
return
{
merklePathNodes
:
formattedMerklePathNodes
}
;
}
catch
(
error
)
{
console
.
error
(
"Error fetching Merkle path:"
,
error
)
;
throw
new
Error
(
"Failed to get Merkle path by transaction ID."
)
;
}
}
;
What This Function Does:
Fetches Merkle Path
: It sends a request to fetch the merkle path using the transaction ID.
Parses Response
: It parses the response from the server as JSON.
Returns Merkle Path Nodes
: It extracts and returns the merkle path of the nodes from the JSON response.
Handles Errors
: If an error occurs, it clears the transaction status and logs the error.
Next, we'll write the
Create a Collection on the cross-chain
function.
Step 5: Write a function to create a collection on the side chain
Scroll up to find the comment
// step 5 - Create a collection on the dAppChain
.
Replace the existing
createCollectionOnSideChain
function with this code snippet:
create-nft/index.tsx
// step 5 - Create a collection on the dAppChain
const
createCollectionOnSideChain
=
async
(
transactionId
:
string
,
signedTx
:
string
,
BlockNumber
:
number
)
=>
{
let
crossChainLoadingId
;
try
{
crossChainLoadingId
=
toast
.
loading
(
"Creating Collection on SideChain..."
)
;
const
merklePath
=
await
getMerklePathByTxId
(
aelf
,
transactionId
)
;
const
tdvwTokenContract
=
await
getTokenContract
(
tdvw
,
wallet
)
;
const
CROSS_CHAIN_CREATE_TOKEN_PARAMS
=
{
fromChainId
:
mainchain_from_chain_id
,
parentChainHeight
:
""
+
BlockNumber
,
// @ts-ignore
transactionBytes
:
Buffer
.
from
(
signedTx
,
"hex"
)
.
toString
(
"base64"
)
,
merklePath
,
}
;
const
signedTx2
=
await
tdvwTokenContract
.
CrossChainCreateToken
.
getSignedTx
(
CROSS_CHAIN_CREATE_TOKEN_PARAMS
)
;
let
done
=
false
;
while
(
!
done
)
{
try
{
await
delay
(
10000
)
;
const
{
TransactionId
}
=
await
tdvw
.
chain
.
sendTransaction
(
signedTx2
)
;
const
txResult
=
await
tdvw
.
chain
.
getTxResult
(
TransactionId
)
;
if
(
txResult
.
Status
===
"SUCCESS"
||
txResult
.
Status
===
"MINED"
)
{
done
=
true
;
setIsNftCollectionCreated
(
true
)
;
toast
.
update
(
crossChainLoadingId
,
{
render
:
"Collection was Created Successfully On SideChain"
,
type
:
"success"
,
isLoading
:
false
,
}
)
;
removeNotification
(
crossChainLoadingId
)
;
toast
.
info
(
"You Can Create NFT now"
)
;
setTransactionStatus
(
false
)
;
}
}
catch
(
err
:
any
)
{
console
.
log
(
err
)
;
if
(
err
.
Error
.
includes
(
"Cross chain verification failed."
)
)
{
console
.
log
(
"Exit."
)
;
done
=
true
;
}
}
}
return
"success"
;
}
catch
(
error
:
any
)
{
toast
.
update
(
crossChainLoadingId
,
{
render
:
error
.
message
,
type
:
"error"
,
isLoading
:
false
,
}
)
;
removeNotification
(
validateLoadingId
)
;
return
"error"
;
}
}
;
What This Function Does:
Displays Loading Toast
: Shows a notification indicating the creation process of the collection on the dAppChain.
Fetches Merkle Path
: Retrieves the merkle path using the provided transactionId.
Prepares and Signs Transaction
: Constructs parameters for the cross-chain transaction and signs it.
Sends Transaction and Checks Status
: Sends the signed transaction and polls for its status every 10 seconds. Updates the notification and state, if successful.
Handles Errors
: Logs errors and returns "error" if something goes wrong.
Final Return
: Returns "success" upon successful completion.
Create NFT Token
Step 1: Write a Function to create NFTs on the mainchain
Now, let's write the create NFTs on mainchain function.
Scroll down to find the comment
// step 6 - Create an NFT on the mainchain
.
Replace the existing
createNFTOnMainChain
function with this code snippet:
create-nft/index.tsx
// step 6 - Create an NFT on the mainchain
const
createNFTOnMainChain
=
async
(
values
:
{
tokenName
:
string
;
symbol
:
string
;
totalSupply
:
string
;
}
)
=>
{
let
createMainChainNFTLoadingId
;
try
{
createMainChainNFTLoadingId
=
toast
.
loading
(
"Creating NFT on MainChain..."
)
;
// Preparing Parameter for Create Function
const
createNtfMainChainInput
=
{
tokenName
:
values
.
tokenName
,
symbol
:
values
.
symbol
,
totalSupply
:
values
.
totalSupply
,
issuer
:
currentWalletAddress
,
isBurnable
:
true
,
issueChainId
:
sidechain_from_chain_id
,
owner
:
currentWalletAddress
,
externalInfo
:
{
}
,
}
;
const
resultMainchain
=
await
mainChainSmartContract
?.
callSendMethod
(
"Create"
,
currentWalletAddress
,
createNtfMainChainInput
)
;
console
.
log
(
"========= result of createNewNft ========="
,
resultMainchain
)
;
toast
.
update
(
createMainChainNFTLoadingId
,
{
render
:
"NFT Created Successfully on MainChain"
,
type
:
"success"
,
isLoading
:
false
,
}
)
;
removeNotification
(
createMainChainNFTLoadingId
)
;
return
"success"
;
}
catch
(
error
:
any
)
{
if
(
!
createMainChainNFTLoadingId
)
{
return
"error"
;
}
toast
.
update
(
createMainChainNFTLoadingId
,
{
render
:
error
.
message
,
type
:
"error"
,
isLoading
:
false
,
}
)
;
removeNotification
(
createMainChainNFTLoadingId
,
5000
)
;
return
"error"
;
}
}
;
What this function does:
Displays Loading Toast:
Shows a notification indicating the creation process of the NFT on the mainchain.
Prepares Parameters:
Constructs input parameters for creating the NFT, including token details and the issuer's information.
Calls Smart Contract:
Sends a request to the mainchain smart contract to create the NFT using the prepared parameters.
Handles Success:
Updates the notification to show successful NFT creation.
Handles Errors:
Displays an error message if the operation fails and logs the error.
Final Return:
Returns
"success"
if the NFT is created successfully; otherwise, returns
"error"
.
Step 2: Write the Function for Validate NFT Info Exist
Now, let's write the Validate NFT Info Exist function.
Scroll down to find the comment
// step 7 - Validate an NFT token on the mainchain
.
Replace the existing
validateNftToken
function with this code snippet:
create-nft/index.tsx
// step 7 - Validate an NFT token on the maincgit stashhain
const
validateNftToken
=
async
(
values
:
INftParams
)
=>
{
let
validateNFTLoadingId
;
try
{
// Start Loading before initiate the transaction
validateNFTLoadingId
=
toast
.
loading
(
<
CustomToast
title
=
"Transaction is getting validated on aelf blockchain. Please wait!"
message
=
"Validation means transaction runs through a consensus algorithm to be selected or rejected. Once the status changes process will complete. It usually takes some time in distributed systems."
/
>
)
;
// Create an object with the necessary information for token validation.
const
validateInput
=
{
symbol
:
values
.
symbol
,
tokenName
:
values
.
tokenName
,
totalSupply
:
values
.
totalSupply
,
issuer
:
currentWalletAddress
,
isBurnable
:
true
,
issueChainId
:
sidechain_from_chain_id
,
owner
:
currentWalletAddress
,
externalInfo
:
{
}
,
}
;
// get mainnet contract
const
aelfTokenContract
=
await
getTokenContract
(
aelf
,
wallet
)
;
// prepare Sign the transaction using contract method (ValidateTokenInfoExists Function)
const
signedTx
=
aelfTokenContract
.
ValidateTokenInfoExists
.
getSignedTx
(
validateInput
)
;
// send the transaction using signed Transaction
const
{
TransactionId
:
VALIDATE_TXID
}
=
await
aelf
.
chain
.
sendTransaction
(
signedTx
)
;
await
delay
(
3000
)
;
// get Validate Result
let
VALIDATE_TXRESULT
=
await
aelf
.
chain
.
getTxResult
(
VALIDATE_TXID
)
;
await
delay
(
3000
)
;
// if dAppChain inde프로젝트로 돌아가기