bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 41쪽 중 19쪽
nerating unique IDs
}
}
The
State.cs
file in an aelf blockchain smart contract holds the variables that store the contract's data, making sure this data is saved and accessible whenever the contract needs it.
Implement ToDo Smart Contract
The implementation of the ToDo App smart contract inside file
src/ToDoApp.cs
is as follows:
src/ToDoApp.cs
using
Google
.
Protobuf
.
WellKnownTypes
;
using
System
.
Collections
.
Generic
;
namespace
AElf
.
Contracts
.
ToDo
{
public
class
ToDo
:
ToDoContainer
.
ToDoBase
{
public
override
Empty
Initialize
(
Empty
input
)
{
if
(
State
.
Initialized
.
Value
)
{
return
new
Empty
(
)
;
}
State
.
Initialized
.
Value
=
true
;
State
.
Owner
.
Value
=
Context
.
Sender
;
State
.
TaskIds
.
Value
=
""
;
State
.
TaskCounter
.
Value
=
0
;
return
new
Empty
(
)
;
}
public
override
StringValue
CreateTask
(
TaskInput
input
)
{
if
(
!
State
.
Initialized
.
Value
)
{
return
new
StringValue
{
Value
=
"Contract not initialized."
}
;
}
var
taskId
=
(
State
.
TaskCounter
.
Value
+
1
)
.
ToString
(
)
;
State
.
TaskCounter
.
Value
++
;
var
timestamp
=
Context
.
CurrentBlockTime
.
Seconds
;
// Create task dictionary entry directly in ToDo class
State
.
Tasks
[
taskId
]
=
new
Task
{
TaskId
=
taskId
,
Name
=
input
.
Name
,
Description
=
input
.
Description
,
Category
=
input
.
Category
,
Status
=
"pending"
,
CreatedAt
=
timestamp
,
UpdatedAt
=
timestamp
,
Owner
=
Context
.
Sender
.
ToString
(
)
.
Trim
(
'"'
)
,
}
;
State
.
TaskExistence
[
taskId
]
=
true
;
// Append task ID to the list of IDs
var
existingTaskIds
=
State
.
TaskIds
.
Value
;
existingTaskIds
+=
string
.
IsNullOrEmpty
(
existingTaskIds
)
?
taskId
:
$",
{
taskId
}
"
;
State
.
TaskIds
.
Value
=
existingTaskIds
;
return
new
StringValue
{
Value
=
taskId
}
;
}
public
override
Empty
UpdateTask
(
TaskUpdateInput
input
)
{
var
task
=
State
.
Tasks
[
input
.
TaskId
]
;
if
(
task
==
null
)
{
return
new
Empty
(
)
;
// Handle case if task doesn't exist
}
task
.
Name
=
input
.
Name
??
task
.
Name
;
task
.
Description
=
input
.
Description
??
task
.
Description
;
task
.
Category
=
input
.
Category
??
task
.
Category
;
task
.
Status
=
input
.
Status
??
task
.
Status
;
task
.
UpdatedAt
=
Context
.
CurrentBlockTime
.
Seconds
;
State
.
Tasks
[
input
.
TaskId
]
=
task
;
return
new
Empty
(
)
;
}
public
override
Empty
DeleteTask
(
StringValue
input
)
{
State
.
Tasks
.
Remove
(
input
.
Value
)
;
State
.
TaskExistence
.
Remove
(
input
.
Value
)
;
// Remove task ID from the list of IDs
var
existingTaskIds
=
State
.
TaskIds
.
Value
.
Split
(
','
)
;
var
newTaskIds
=
new
List
<
string
>
(
existingTaskIds
.
Length
)
;
foreach
(
var
taskId
in
existingTaskIds
)
{
if
(
taskId
!=
input
.
Value
)
{
newTaskIds
.
Add
(
taskId
)
;
}
}
State
.
TaskIds
.
Value
=
string
.
Join
(
","
,
newTaskIds
)
;
return
new
Empty
(
)
;
}
public
override
TaskList
ListTasks
(
StringValue
input
)
{
var
owner
=
input
.
Value
;
// Get the owner value from the input
var
taskList
=
new
TaskList
(
)
;
var
taskIds
=
State
.
TaskIds
.
Value
.
Split
(
','
)
;
foreach
(
var
taskId
in
taskIds
)
{
var
task
=
State
.
Tasks
[
taskId
]
;
if
(
task
!=
null
&&
task
.
Owner
==
owner
)
// Filter tasks by owner
{
taskList
.
Tasks
.
Add
(
task
)
;
}
}
return
taskList
;
}
public
override
Task
GetTask
(
StringValue
input
)
{
var
task
=
State
.
Tasks
[
input
.
Value
]
;
if
(
task
==
null
)
{
return
new
Task
{
TaskId
=
input
.
Value
,
Name
=
"Task not found."
}
;
}
return
task
;
}
public
override
BoolValue
GetInitialStatus
(
Empty
input
)
{
return
new
BoolValue
{
Value
=
State
.
Initialized
.
Value
}
;
}
}
}
Building Smart Contract
Build the smart contract code with the following command inside
src
folder:
Terminal
dotnet build
You should see
ToDoApp.dll.patched
in the directory
ToDoApp/src/bin/Debug/net.6.0
Step 3 - Deploy Smart Contract
Create A Wallet
To send transactions on the aelf blockchain, you must have a wallet.
Run this command to create aelf wallet.
Terminal
aelf-command create
You will be prompted to save your account, please do
save
your account as shown below:
Terminal
? Save account info into a file?
(
Y/n
)
Y
Make sure to choose Y to save your account information.
tip
ℹ️ Note: If you do not save your account information (by selecting n or N), do not export the wallet password. Only
proceed to the next
step if you have saved your account information.
Next, enter and confirm your password. Then export your wallet password as shown below:
Linux and macOs
Windows
Terminal
export
WALLET_PASSWORD
=
"YOUR_WALLET_PASSWORD"
Command Prompt
$env
:WALLET_PASSWORD
=
"YOUR_WALLET_PASSWORD"
Acquire Testnet Tokens (Faucet) for Development
To deploy smart contracts or execute on-chain transactions on aelf, you'll require testnet ELF tokens.
Get ELF Tokens
Go to
https://faucet-ui.aelf.dev
Enter your address and click
Get Tokens
.
Deploy Smart Contract:
The smart contract needs to be deployed on the chain before users can interact with it.
Run the following command to deploy a contract. Remember to export the path of ToDoApp.dll.patched to CONTRACT_PATH.
Linux and macOs
Windows
Terminal
export
CONTRACT_PATH
=
$(
find
~+
.
-path
"*patched*"
|
head
-n
1
)
Terminal
aelf-deploy
-a
$WALLET_ADDRESS
-p
$WALLET_PASSWORD
-c
$CONTRACT_PATH
-e
https://tdvw-test-node.aelf.io/
Command Prompt
$CONTRACT_PATH
=
Get-ChildItem
-Recurse
-Filter
"*patched*"
|
Select-Object
-First
1
-ExpandProperty
FullName
$env
:CONTRACT_PATH
=
$CONTRACT_PATH
Command Prompt
aelf-deploy
-a
$env
:WALLET_ADDRESS
-p
$env
:WALLET_PASSWORD
-c
$env
:CONTRACT_PATH
-e
https://tdvw-test-node.aelf.io/
Please wait for approximately 1 to 2 minutes. If the deployment is successful, it will provide you with the contract address.
Copy the smart contract address from the
address
field
Export your smart contract address:
Linux and macOs
Windows
Terminal
export
CONTRACT_ADDRESS
=
"YOUR_SMART_CONTRACT_ADDRESS e.g. 2LUmicHyH4RXrMjG4beDwuDsiWJESyLkgkwPdGTR8kahRzq5XS"
Command Prompt
$env
:CONTRACT_ADDRESS
=
"YOUR_SMART_CONTRACT_ADDRESS e.g. 2LUmicHyH4RXrMjG4beDwuDsiWJESyLkgkwPdGTR8kahRzq5XS"
tip
ℹ️ Note: You are to copy the smart contract address as we will be referencing it in the next steps!
info
🎉 You have successfully deployed your ToDo dApp smart contract on the aelf testnet! In the next steps, we will be building the frontend components that allow us to interact with our deployed smart contract!
Step 4 - Interact with Your Deployed Smart Contract through dApp
Project Setup
Let's start by cloning the frontend project repository from github.
Terminal
git
clone https://github.com/AElfProject/aelf-samples.git
Next, navigate to the frontend project directory with this command:
Terminal
cd
aelf-samples/todo/2-dapp
Once you're inside the
2-dapp
directory, open the project with your preferred IDE (e.g., VSCode). You should see the project structure as shown below.
Install necessary libraries
Run this command in the terminal to install all necessary packages and libraries:
Terminal
npm
install
We are now ready to build the frontend components of our ToDo dApp.
Configure Portkey Provider & Write Connect Wallet Function
Now, we'll set up our Portkey wallet provider to allow users to connect their Portkey wallets to the dApp and interact with the smart contract. We'll be interacting with the already deployed ToDo smart contract for this tutorial.
Step 1. Locate the File:
Go to the
src/hooks/useTodoSmartContract.ts
file.
Step 2. Fetch the Smart Contract:
Find the comment
//Step A - Function to fetch a smart contract based on deployed wallet address
Replace the existing
fetchContract
function with this updated code:
useTodoSmartContract.ts
//Step A - Function to fetch a smart contract based on deployed wallet address
const
fetchContract
=
async
(
)
=>
{
if
(
!
provider
)
return
null
;
try
{
// 1. get the dAppChain tDVW using provider.getChain
const
chain
=
await
provider
?.
getChain
(
"tDVW"
)
;
if
(
!
chain
)
throw
new
Error
(
"No chain"
)
;
//Address of ToDo Smart Contract
//Replace with Address of Deployed Smart Contract
const
address
=
"your_deployed_todo_contract_address"
;
// 2. get the ToDo contract
const
todoContract
=
chain
?.
getContract
(
address
)
;
setSmartContract
(
todoContract
)
;
}
catch
(
error
)
{
console
.
log
(
error
,
"====error"
)
;
}
}
tip
ℹ️ Note: You are to replace the address placeholder with your deployed ToDo smart contract address from "Deploy Smart Contract" step!
example:
//Replace with Address of Deployed Smart Contract
const address = "your_deployed_todo__smart_contract_address";
Explanation:
fetchContract
Function
: This function fetches a smart contract based on the given chain symbol (e.g., "AELF" or "tDVW") and the contract address.
Check Provider
: If no provider is available, the function returns null.
Fetch Chain
: The function fetches chain information using the provider.
Get Contract
: It retrieves the smart contract instance from the chain.
AELF
represents the mainnet chain and
tDVW
represents the testnet chain respectively on aelf blockchain.
Step 3. Initialize and Fetch the Smart Contracts:
Find the comment
// Step B - Effect hook to initialize and fetch the smart contract when the provider changes.
Replace the existing
useEffect
hook with this updated code:
useTodoSmartContract.ts
// Step B - Effect hook to initialize and fetch the smart contract when the provider changes
useEffect
(
(
)
=>
{
fetchContract
(
)
;
}
,
[
provider
]
)
;
// Dependency array ensures this runs when the provider changes
Explanation:
useEffect
Hook
: This hook initializes and fetches the smart contracts when the provider changes.
Check Provider
: If no provider is available, the function returns null.
Fetch Contracts
: It fetches and sets the smart contracts.
By following these steps, we'll configure the Portkey provider to connect users' wallets to our app and interact with the ToDo smart contract including task management related functionalities. This setup will enable our frontend components to perform actions like
Create Task
,
Edit Task
, and
Delete Task
.
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 ToDo dApp. It allows users to connect their Portkey wallet with the ToDo 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 Task Form
Step 1: Locate the File
Go to the
src/pages/home/index.tsx
file. This file contains all the functionalities like show user's Task, CreateTask, UpdateTask, DeleteTask and Filter all Tasks, etc.
Step 2: Prepare Form to Create and Update Tasks
Find the comment
// Step D - Configure Todo Form
.
Replace the form variable with this code snippet:
home/index.tsx
// Step D - Configure Todo Form
const
form
=
useForm
<
z
.
infer
<
typeof
formSchema
>>
(
{
resolver
:
zodResolver
(
formSchema
)
,
defaultValues
:
{
name
:
""
,
description
:
""
,
}
,
}
)
;
Here's what the function does:
Initializes a new form variable with default values needed to create a task.
Fields include:
name
and
description
.
Now the form is ready for users to fill in the necessary details.
Check Contract Initialization
Scroll down to find the comment
// step 1 - Check if contract is initialized or not
.
Replace the existing
checkIsContractInitialized
function with this code snippet:
home/index.tsx
// step 1 - Check if contract is initialized or not
const
checkIsContractInitialized
=
async
(
)
=>
{
const
result
=
await
smartContract
?.
callViewMethod
(
"GetInitialStatus"
,
""
)
;
// Call the GetInitialStatus method which is present on Smart Contract
setIsContractInitialized
(
result
?.
data
?.
value
)
;
// Expect value True if it's Initialized otherwise NULL if it's not
}
;
Initialize Contract
Scroll down to find the comment
// step 2 - Initialize the smart contract
.
Replace the existing
initializeContract
function with this code snippet:
home/index.tsx
// step 2 - Initialize the smart contract
const
initializeContract
=
async
(
)
=>
{
let
initializeLoadingId
;
try
{
// Start Loading
initializeLoadingId
=
toast
.
loading
(
"Initializing a Contract.."
)
;
await
smartContract
?.
callSendMethod
(
"Initialize"
,
// Function Name
currentWalletAddress
as
string
,
// User Wallet Address
{
}
// No Arguments
)
;
// Update Loading Message with Success
toast
.
update
(
initializeLoadingId
,
{
render
:
"Contract Successfully Initialized"
,
type
:
"success"
,
isLoading
:
false
,
}
)
;
}
catch
(
error
:
any
)
{
// Update Loading Message with Error
toast
.
update
(
initializeLoadingId
as
Id
,
{
render
:
error
.
message
,
type
:
"error"
,
isLoading
:
false
,
}
)
;
}
finally
{
// Remove Loading Message
removeNotification
(
initializeLoadingId
as
Id
)
;
}
}
;
Create a New Task
Write the function to
Create a New Task
**
The
home/index.tsx
file includes the code to create tasks. It allows users to create new tasks.
Find the comment
// step 3 - Create a New Task using Smart Contract
.
Replace the existing
createNewTask
function with this code snippet:
home/index.tsx
// step 3 - Create a New Task using Smart Contract
const
createNewTask
=
async
(
values
:
{
name
:
string
;
description
:
string
;
}
)
=>
{
let
createLoadingId
;
try
{
// Start Loading
createLoadingId
=
toast
.
loading
(
"Creating a New Task.."
)
;
setFormLoading
(
true
)
;
// Prepare Arguments for Create a New Task
const
sendData
=
{
name
:
values
.
name
,
description
:
values
.
description
,
category
:
selectedCategory
?.
value
,
status
:
TASK_STATUS
.
pending
,
}
;
// Call CreateTask Function of Smart Contract
await
smartContract
?.
callSendMethod
(
"CreateTask"
,
currentWalletAddress
as
string
,
sendData
)
;
// Update Loading Message with Success
toast
.
update
(
createLoadingId
,
{
render
:
"New Task Successfully Created"
,
type
:
"success"
,
isLoading
:
false
,
}
)
;
// Get New Data from Contract
getTodoData
(
)
;
}
catch
(
error
:
any
)
{
// Update Loading Message with Error
toast
.
update
(
createLoadingId
as
Id
,
{
render
:
error
.
message
,
type
:
"error"
,
isLoading
:
false
,
}
)
;
}
finally
{
// Close Form Modal
handleCloseModal
(
)
;
// Remove Loading Message
removeNotification
(
createLoadingId
as
Id
)
;
setFormLoading
(
false
)
;
}
}
;
What This Function Does:
Creates an Object with Task Details
: It prepares the data needed to create a new task.
Calls Smart Contract Method
: It interacts with the blockchain smart contract to create the new task using the prepared data.
Next, we'll write the
Update an Existing Task
function.
Update an Existing Task
Write the function for update an existing task.
Scroll down to find the comment
// step 4 - Update an Existing Task
.
Replace the existing
updateTask
function with this code snippet:
home/index.tsx
// step 4 - Update an Existing Task
const
updateTask
=
async
(
values
:
{
name
:
string
;
description
:
string
}
)
=>
{
let
updateLoadingId
;
try
{
// Start Loading
updateLoadingId
=
toast
.
loading
(
"Updating a Task.."
)
;
setFormLoading
(
true
)
;
// Prepare Arguments for Update the Task
const
sendData
=
{
taskId
:
updateId
,
name
:
values
.
name
,
description
:
values
.
description
,
category
:
selectedCategory
?.
value
,
status
:
TASK_STATUS
.
pending
,
}
;
// Call UpdateTask Function of Smart Contract
await
smartContract
?.
callSendMethod
(
"UpdateTask"
,
currentWalletAddress
as
string
,
sendData
)
;
// Update Loading Message with Success
toast
.
update
(
updateLoadingId
,
{
render
:
"Task Successfully Updated"
,
type
:
"success"
,
isLoading
:
false
,
}
)
;
// Get New Data from Contract
getTodoData
(
)
;
}
catch
(
error
:
any
)
{
// Update Loading Message with Error
toast
.
update
(
updateLoadingId
as
Id
,
{
render
:
error
.
message
,
type
:
"error"
,
isLoading
:
false
,
}
)
;
}
finally
{
// Close Form Modal
handleCloseModal
(
)
;
// Remove Loading Message
removeNotification
(
updateLoadingId
as
Id
)
;
setFormLoading
(
false
)
;
}
}
;
What This Function Does:
Creates an Object with Updated Task Details
: It prepares the data needed for the updated task details
Calls Smart Contract Method
: It interacts with the blockchain smart contract to update the existing task using the prepared data.
Next, we'll write the
Update Task Status (completeTask)
function.
Update the Task Status
Write the Function to update the task status (completeTask).
Scroll down to find the comment
// step 5- Update Status from Pending to Completed of the Task
.
Replace the existing
completeTask
function with the following code snippet:
home/index.tsx
// step 5- Update Status from Pending to Completed of the Task
const
completeTask
=
async
(
data
:
ITodoObject
)
=>
{
let
completeLoadingId
;
try
{
// Start Loading
completeLoadingId
=
toast
.
loading
(
"Moving to Completed Task.."
)
;
setUpdateId
(
data
.
taskId
)
;
// set Update Id for Loading on Button
// Call UpdateTask Function of Smart Contract
await
smartContract
?.
callSendMethod
(
"UpdateTask"
,
currentWalletAddress
as
string
,
{
...
data
,
status
:
TASK_STATUS
.
completed
}
)
;
// Update Loading Message with Success
toast
.
update
(
completeLoadingId
,
{
render
:
"Task Moved to Completed"
,
type
:
"success"
,
isLoading
:
false
,
}
)
;
// Get New Data from Contract
await
getTodoData
(
)
;
}
catch
(
error
:
any
)
{
// Update Loading Message with Error
toast
.
update
(
completeLoadingId
as
Id
,
{
render
:
error
.
message
,
type
:
"error"
,
isLoading
:
false
,
}
)
;
}
finally
{
setUpdateId
(
null
)
;
// Remove Loading Message
removeNotification
(
completeLoadingId
as
Id
)
;
}
}
;
What This Function Does:
Ca프로젝트로 돌아가기