bitpro가 원문에서 글자만 모은 내용이에요. 표·그림·수식은 빠지거나 깨질 수 있어요. 전체 41쪽 중 21쪽
not initialized."
}
;
}
ResetBoard
(
)
;
State
.
CurrentPlayer
.
Value
=
"X"
;
State
.
GameStatus
.
Value
=
"ongoing"
;
State
.
Winner
.
Value
=
""
;
return
new
StringValue
{
Value
=
"Game started. Player X's turn."
}
;
}
public
override
StringValue
MakeMove
(
MoveInput
input
)
{
if
(
State
.
GameStatus
.
Value
!=
"ongoing"
)
{
return
new
StringValue
{
Value
=
"Game is not ongoing."
}
;
}
var
board
=
GetBoardArray
(
)
;
if
(
board
[
input
.
X
,
input
.
Y
]
!=
""
)
{
return
new
StringValue
{
Value
=
"Invalid move. Cell is already occupied."
}
;
}
board
[
input
.
X
,
input
.
Y
]
=
State
.
CurrentPlayer
.
Value
;
SaveBoard
(
board
)
;
if
(
CheckWinner
(
)
)
{
State
.
GameStatus
.
Value
=
"finished"
;
State
.
Winner
.
Value
=
State
.
CurrentPlayer
.
Value
;
return
new
StringValue
{
Value
=
$"Player
{
State
.
CurrentPlayer
.
Value
}
wins!"
}
;
}
if
(
IsBoardFull
(
board
)
)
{
State
.
GameStatus
.
Value
=
"draw"
;
return
new
StringValue
{
Value
=
"It's a draw!"
}
;
}
State
.
CurrentPlayer
.
Value
=
State
.
CurrentPlayer
.
Value
==
"X"
?
"O"
:
"X"
;
return
new
StringValue
{
Value
=
$"Player
{
State
.
CurrentPlayer
.
Value
}
's turn."
}
;
}
public
override
Board
GetBoard
(
Empty
input
)
{
var
board
=
GetBoardArray
(
)
;
var
boardMessage
=
new
Board
(
)
;
for
(
var
i
=
0
;
i
<
3
;
i
++
)
// Adjusted to 3 for a 3x3 Tic-Tac-Toe board
{
var
row
=
new
List
<
string
>
(
)
;
for
(
var
j
=
0
;
j
<
3
;
j
++
)
{
row
.
Add
(
board
[
i
,
j
]
)
;
}
boardMessage
.
Rows
.
Add
(
string
.
Join
(
","
,
row
)
)
;
}
return
boardMessage
;
}
public
override
GameStatus
GetGameStatus
(
Empty
input
)
{
return
new
GameStatus
{
Status
=
State
.
GameStatus
.
Value
,
Winner
=
State
.
Winner
.
Value
}
;
}
public
override
BoolValue
GetInitialStatus
(
Empty
input
)
{
return
new
BoolValue
{
Value
=
State
.
Initialized
.
Value
}
;
}
private
void
ResetBoard
(
)
{
var
emptyBoard
=
new
string
[
BoardSize
,
BoardSize
]
;
for
(
var
i
=
0
;
i
<
BoardSize
;
i
++
)
{
for
(
var
j
=
0
;
j
<
BoardSize
;
j
++
)
{
emptyBoard
[
i
,
j
]
=
""
;
}
}
SaveBoard
(
emptyBoard
)
;
}
private
string
[
,
]
GetBoardArray
(
)
{
var
boardString
=
State
.
Board
.
Value
;
var
rows
=
boardString
.
Split
(
';'
)
;
var
board
=
new
string
[
BoardSize
,
BoardSize
]
;
for
(
var
i
=
0
;
i
<
BoardSize
;
i
++
)
{
var
cells
=
rows
[
i
]
.
Split
(
','
)
;
for
(
var
j
=
0
;
j
<
BoardSize
;
j
++
)
{
board
[
i
,
j
]
=
cells
[
j
]
;
}
}
return
board
;
}
private
void
SaveBoard
(
string
[
,
]
board
)
{
var
rows
=
new
string
[
BoardSize
]
;
for
(
var
i
=
0
;
i
<
BoardSize
;
i
++
)
{
rows
[
i
]
=
string
.
Join
(
","
,
board
[
i
,
0
]
,
board
[
i
,
1
]
,
board
[
i
,
2
]
)
;
}
State
.
Board
.
Value
=
string
.
Join
(
";"
,
rows
)
;
}
private
bool
CheckWinner
(
)
{
var
board
=
GetBoardArray
(
)
;
var
player
=
State
.
CurrentPlayer
.
Value
;
for
(
var
i
=
0
;
i
<
BoardSize
;
i
++
)
{
if
(
board
[
i
,
0
]
==
player
&&
board
[
i
,
1
]
==
player
&&
board
[
i
,
2
]
==
player
)
return
true
;
if
(
board
[
0
,
i
]
==
player
&&
board
[
1
,
i
]
==
player
&&
board
[
2
,
i
]
==
player
)
return
true
;
}
if
(
board
[
0
,
0
]
==
player
&&
board
[
1
,
1
]
==
player
&&
board
[
2
,
2
]
==
player
)
return
true
;
if
(
board
[
0
,
2
]
==
player
&&
board
[
1
,
1
]
==
player
&&
board
[
2
,
0
]
==
player
)
return
true
;
return
false
;
}
private
bool
IsBoardFull
(
string
[
,
]
board
)
{
for
(
var
i
=
0
;
i
<
BoardSize
;
i
++
)
{
for
(
var
j
=
0
;
j
<
BoardSize
;
j
++
)
{
if
(
board
[
i
,
j
]
==
""
)
return
false
;
}
}
return
true
;
}
}
}
Building Smart Contract
Build the smart contract code with the following command inside
src
folder:
Terminal
dotnet build
You should see
TicTacToe.dll.patched
in the directory
1-smart-contract/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 TicTacToe.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 Tic-Tac-Toe 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/tic-tac-toe/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 Tic-Tac-Toe 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 Tic-Tac-Toe smart contract for this tutorial.
Step 1. Locate the File:
Go to the
src/hooks/useSmartContract.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:
useSmartContract.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 TicTacToe Smart Contract
//Replace with Address of Deployed Smart Contract
const
address
=
"your_deployed_tic_tac_toe_contract_address"
;
// 2. get the TicTacToe contract
const
contract
=
chain
?.
getContract
(
address
)
;
setSmartContract
(
contract
)
;
}
catch
(
error
)
{
console
.
log
(
error
,
"====error"
)
;
}
}
tip
ℹ️ Note: You are to replace the address placeholder with your deployed Tic Tac Toe smart contract address from "Deploy Smart Contract" step!
example: Replace with Address of Deployed Smart Contract
const address = "your_deployed_tic_tac_toe_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 the 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:
useSmartContract.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 Tic-Tac-Toe smart contract including Tic-Tac-Toe Game Play related functionalities. This setup will enable our frontend components to perform actions like
initializeContract
,
startGame
,
makeMove
,
getGameStatus
and
getLatestBoard
etc.
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 Tic-Tac-Toe dApp. It allows users to connect their Portkey wallet with the Tic-Tac-Toe 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.
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.
Check Contract Initialization
Step 1: Locate the File
Go to the
src/pages/home/index.tsx
file. This file contains all the functionalities like show
initializeContract
,
startGame
,
makeMove
,
getGameStatus
and
getLatestBoard
etc.
Step 2: Prepare a Function to Check Whether the Contract is Initialized or not
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
checkIsContractInitialized
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
,
}
)
;
return
;
}
catch
(
error
:
any
)
{
// Update Loading Message with Error
toast
.
update
(
initializeLoadingId
as
Id
,
{
render
:
error
.
message
,
type
:
"error"
,
isLoading
:
false
,
}
)
;
return
;
}
finally
{
// Remove Loading Message
removeNotification
(
initializeLoadingId
as
Id
)
;
}
}
;
Fetch Game Status
Write the function to fetch the game status.
Find the comment
// step 3 - Fetch Game Status function
on same file.
Replace the existing
getGameStatus
function with this code snippet:
home/index.tsx
// step 3 - Fetch Game Status function
const
getGameStatus
=
async
(
isFirstCheck
?
:
boolean
)
=>
{
try
{
const
result
=
await
smartContract
?.
callViewMethod
(
"GetGameStatus"
,
""
)
;
console
.
log
(
"result"
,
result
)
;
if
(
result
?.
data
.
status
===
"finished"
)
{
if
(
!
isFirstCheck
)
{
toast
.
success
(
`
${
result
.
data
.
winner
}
is Winner 🥳 🏆
`
)
;
setWinner
(
result
.
data
.
winner
)
;
}
}
else
{
setIsStarted
(
true
)
;
getLatestBoard
(
)
;
}
}
catch
(
error
:
any
)
{
toast
.
error
(
error
.
message
)
;
}
finally
{
setLoading
(
false
)
;
return
;
}
}
;
What This Function Does:
Calls Smart Contract Method
: It interacts with the blockchain smart contract to fetch the game status using
getGameStatus
Function.
Next, we'll write the
Fetch Board Data
function.
Fetch Board Data
Write the function to fetch the board data.
Scroll down to find the comment
// step 4 - Fetch Latest Board Data from the Contract
.
Replace the existing
getLatestBoard
function with this code snippet:
home/index.tsx
// step 4 - Fetch Latest Board Data from the Contract
const
getLatestBoard
=
async
(
)
=>
{
try
{
const
result
=
await
smartContract
?.
callViewMethod
(
"GetBoard"
,
""
)
;
const
arrayData
=
result
?.
data
.
rows
.
map
(
(
rowData
:
any
)
=>
rowData
.
split
(
","
)
.
map
(
(
item
:
string
)
=>
(
item
===
""
?
null
:
item
)
)
)
.
flat
(
)
;
getNextTurn
(
arrayData
)
;
setBoard
(
arrayData
)
;
}
catch
(
error
:
any
)
{
toast
.
error
(
error
.
message
)
;
}
finally
{
return
;
}
}
;
What This Function Does:
Calls Smart Contract Method
: It interacts with the blockchain smart contract to fetch the latest board data using
getLatestBoard
Function.
Convert Respnose Data into Array
: It converts object of the response data to an array to adjust every move into the UI board.
Next, we'll write the
Start Game
function.
Start Game
Write the Function to start the Tic-Tac-Toe game.
Scroll down to find the comment
// step 5 - Start Game function
.
Replace the existing
startGame
function with the following code snippet:
home/index.tsx
// step 5 - Start Game function
const
startGame
=
async
(
)
=>
{
try
{
if
(
!
currentWalletAddress
)
{
toast
.
info
(
"Please Login First"
)
;
return
;
}
if
(
!
isContractInitialized
)
{
await
initializeContract
(
)
;
}
await
smartContract
?.
callSendMethod
(
"StartGame"
,
currentWalletAddress
)
;
setTurnType
(
"X"
)
;
setIsStarted
(
true
)
;
getLatestBoard
(
)
;
toast
.
success
(
"Game Has been Started ✅"
)
;
}
catch
(
error
:
any
)
{
toast
.
error
(
error
.
message
)
;
}
}
;
What This Function Does:
Calls Smart Contract Method
: It interacts with the blockchain smart contract to start the game by using the
startGame
Function.
Next, we'll write the
Make the Move
function.
Perform the Move
Write a function to make the move.
Scroll down to find the comment
// step 6 - Make the Move Function
.
Replace the existing
makeMove
function with this code snippet:
home/index.tsx
// step 6 - Make the Move Function
const
makeMove
=
async
(
x
:
number
,
y
:
number
)
=>
{
try
{
if
(
!
currentWalletAddress
)
{
toast
.
info
(
"Please Login First"
)
;
return
;
}
const
moveIndex
=
getIndexFromPosition
(
x
,
y
)
;
setMoveLoadingIndex
(
moveIndex
as
number
)
;
const
params
=
{
x
:
x
,
y
:
y
,
}
;
await
smartContract
?.
callSendMethod
(
"MakeMove"
,
currentWalletAddress
,
params
)
;
// toast.success("Your Move Submitted");
changeTurn
(
)
;
await
getLatestBoard
(
)
;
await
getGameStatus
(
)
;
}
catch
(
error
:
any
)
{
toast
.
error
(
error
.
message
)
;
}
finally
{
}
}
;
What This Function Does:
Calls Smart Contract Method
: It interacts with the blockchain smart contract to make the move by an input from a player using
makeMove
function by passing
X
and
Y
value of the Board.
Now that we've written all the necessary frontend functions and components for play the Tic-Tac-Toe page, we're ready to run the Tic-Tac-Toe dApp application in the next step.
Run Application
In this step, we will run the Tic-Tac-Toe dApp application.
To begin, run the following command on your terminal.
Terminal
npm
run dev
info
Note
: Ensure that you are running this command under the
tic-tac-toe/2-dapp
folder.
You should observe the following as shown below.
Upon clicking on the
localhost URL
, you should be directed to the Tic-Tac-Toe dApp landing page as shown below.
tip
If you are developing and testing this with github codespace, you can use
port forward
to test the web server that is running in codespace, here is the link on how to use
port forward
for codespace
https://docs.github.com/en/codespaces/developing-in-a-codespace/forwarding-ports-in-your-codespace
Usually codespaces automatically forward ports, you should see a pop-up message at the bottom right of your codespace browser window as shown in the diagram below:
Click the link to open the Tic-Tac-Toe dApp in the browser.
Create Portkey Wallet
info
Portkey is the first AA wallet from aelf's ecosystem, migrating users, developers and projects from Web2 to Web3 with DID solution.
Users can swiftly log into Portkey via their Web2 social info with no private keys o프로젝트로 돌아가기