From 25215b9078812c71f5793359b63f97d669df6772 Mon Sep 17 00:00:00 2001 From: 0xaptosj <129789810+0xaptosj@users.noreply.github.com> Date: Mon, 29 Apr 2024 18:25:48 -0700 Subject: [PATCH 1/2] deploy to object --- frontend/.env | 1 - frontend/gen_abi.sh | 9 ++ frontend/package.json | 3 +- frontend/src/app/home/Connected.tsx | 6 +- frontend/src/app/home/Mint/index.tsx | 4 +- frontend/src/app/home/Pet/Actions.tsx | 6 +- frontend/src/app/home/Pet/Details.tsx | 6 +- .../src/hooks/useGetAptogotchiCollection.ts | 6 +- frontend/src/utils/abi.ts | 144 ++++++++++++++++++ frontend/src/utils/env.ts | 2 - move/Move.toml | 4 +- move/contract_address.txt | 1 + move/scripts/script_create_gotchi.move | 2 +- move/scripts/script_feed_gotchi.move | 4 +- move/sh_scripts/move_publish.sh | 20 ++- move/sh_scripts/move_upgrade.sh | 17 +++ move/sources/aptogotchi.move | 27 ++-- 17 files changed, 221 insertions(+), 41 deletions(-) create mode 100755 frontend/gen_abi.sh create mode 100644 frontend/src/utils/abi.ts create mode 100644 move/contract_address.txt create mode 100755 move/sh_scripts/move_upgrade.sh diff --git a/frontend/.env b/frontend/.env index bf221677..68d066d0 100644 --- a/frontend/.env +++ b/frontend/.env @@ -1,4 +1,3 @@ -NEXT_PUBLIC_CONTRACT_ADDRESS="0xa3527451930153f0da31da246da3837c5c6ee3156389a075af4bf6465c91aef1" NEXT_PUBLIC_BODY_OPTIONS=5 NEXT_PUBLIC_EAR_OPTIONS=6 NEXT_PUBLIC_FACE_OPTIONS=4 diff --git a/frontend/gen_abi.sh b/frontend/gen_abi.sh new file mode 100755 index 00000000..716e3e07 --- /dev/null +++ b/frontend/gen_abi.sh @@ -0,0 +1,9 @@ +#! /bin/bash + +NETWORK=testnet + +CONTRACT_ADDRESS=$(cat ../move/contract_address.txt) + +PACKAGE_NAME=$(cat ../move/sources/$(ls ../move/sources/ | head -n 1) | head -n 1 | sed -n 's/module [^:]*::\(.*\) {/\1/p') + +echo "export const ABI = $(curl https://fullnode.$NETWORK.aptoslabs.com/v1/accounts/$CONTRACT_ADDRESS/module/$PACKAGE_NAME | sed -n 's/.*"abi":\({.*}\).*}$/\1/p') as const" > src/utils/abi.ts diff --git a/frontend/package.json b/frontend/package.json index dfb9e389..fb509566 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,8 @@ "build": "next build", "start": "next start", "lint": "next lint", - "aptos": "aptos" + "aptos": "aptos", + "gen-abi": "./gen_abi.sh" }, "dependencies": { "@aptos-labs/ts-sdk": "^1.5.1", diff --git a/frontend/src/app/home/Connected.tsx b/frontend/src/app/home/Connected.tsx index f3ad7741..4092aa7b 100644 --- a/frontend/src/app/home/Connected.tsx +++ b/frontend/src/app/home/Connected.tsx @@ -4,9 +4,9 @@ import { useState, useEffect, useCallback } from "react"; import { Pet } from "./Pet"; import { useWallet } from "@aptos-labs/wallet-adapter-react"; import { Mint } from "./Mint"; -import { NEXT_PUBLIC_CONTRACT_ADDRESS } from "@/utils/env"; import { getAptosClient } from "@/utils/aptosClient"; import { Modal } from "@/components/Modal"; +import { ABI } from "@/utils/abi"; const TESTNET_ID = "2"; @@ -21,14 +21,14 @@ export function Connected() { const [hasPet] = await aptosClient.view({ payload: { - function: `${NEXT_PUBLIC_CONTRACT_ADDRESS}::main::has_aptogotchi`, + function: `${ABI.address}::main::has_aptogotchi`, functionArguments: [account.address], }, }); if (hasPet as boolean) { const response = await aptosClient.view({ payload: { - function: `${NEXT_PUBLIC_CONTRACT_ADDRESS}::main::get_aptogotchi`, + function: `${ABI.address}::main::get_aptogotchi`, functionArguments: [account.address], }, }); diff --git a/frontend/src/app/home/Mint/index.tsx b/frontend/src/app/home/Mint/index.tsx index 3e188f1e..9038b06e 100644 --- a/frontend/src/app/home/Mint/index.tsx +++ b/frontend/src/app/home/Mint/index.tsx @@ -1,9 +1,9 @@ import { useState } from "react"; import { useWallet } from "@aptos-labs/wallet-adapter-react"; -import { NEXT_PUBLIC_CONTRACT_ADDRESS } from "@/utils/env"; import { getAptosClient } from "@/utils/aptosClient"; import { ShufflePetImage } from "@/app/home/Pet/ShufflePetImage"; import { DEFAULT_PET, PetParts } from "@/app/home/Pet"; +import { ABI } from "@/utils/abi"; const aptosClient = getAptosClient(); @@ -28,7 +28,7 @@ export function Mint({ fetchPet }: MintProps) { const response = await signAndSubmitTransaction({ sender: account.address, data: { - function: `${NEXT_PUBLIC_CONTRACT_ADDRESS}::main::create_aptogotchi`, + function: `${ABI.address}::main::create_aptogotchi`, typeArguments: [], functionArguments: [ newName, diff --git a/frontend/src/app/home/Pet/Actions.tsx b/frontend/src/app/home/Pet/Actions.tsx index 8a2886c0..755da88b 100644 --- a/frontend/src/app/home/Pet/Actions.tsx +++ b/frontend/src/app/home/Pet/Actions.tsx @@ -5,11 +5,11 @@ import { useWallet } from "@aptos-labs/wallet-adapter-react"; import { Pet } from "."; import { getAptosClient } from "@/utils/aptosClient"; import { - NEXT_PUBLIC_CONTRACT_ADDRESS, NEXT_PUBLIC_ENERGY_CAP, NEXT_PUBLIC_ENERGY_DECREASE, NEXT_PUBLIC_ENERGY_INCREASE, } from "@/utils/env"; +import { ABI } from "@/utils/abi"; const aptosClient = getAptosClient(); @@ -52,7 +52,7 @@ export function Actions({ const response = await signAndSubmitTransaction({ sender: account.address, data: { - function: `${NEXT_PUBLIC_CONTRACT_ADDRESS}::main::feed`, + function: `${ABI.address}::main::feed`, typeArguments: [], functionArguments: [NEXT_PUBLIC_ENERGY_INCREASE], }, @@ -89,7 +89,7 @@ export function Actions({ const response = await signAndSubmitTransaction({ sender: account.address, data: { - function: `${NEXT_PUBLIC_CONTRACT_ADDRESS}::main::play`, + function: `${ABI.address}::main::play`, typeArguments: [], functionArguments: [NEXT_PUBLIC_ENERGY_DECREASE], }, diff --git a/frontend/src/app/home/Pet/Details.tsx b/frontend/src/app/home/Pet/Details.tsx index 1960677c..10ad2ecc 100644 --- a/frontend/src/app/home/Pet/Details.tsx +++ b/frontend/src/app/home/Pet/Details.tsx @@ -6,8 +6,8 @@ import { HealthBar } from "@/components/HealthBar"; import { Pet } from "."; import { Dispatch, SetStateAction, useState } from "react"; import { useWallet } from "@aptos-labs/wallet-adapter-react"; -import { NEXT_PUBLIC_CONTRACT_ADDRESS } from "@/utils/env"; import { getAptosClient } from "@/utils/aptosClient"; +import { ABI } from "@/utils/abi"; export interface PetDetailsProps { pet: Pet; @@ -32,10 +32,10 @@ export function PetDetails({ pet, setPet }: PetDetailsProps) { const response = await signAndSubmitTransaction({ sender: account.address, data: { - function: `${NEXT_PUBLIC_CONTRACT_ADDRESS}::main::set_name`, + function: `${ABI.address}::main::set_name`, typeArguments: [], functionArguments: [newName], - } + }, }); await aptosClient.waitForTransaction({ transactionHash: response.hash }); diff --git a/frontend/src/hooks/useGetAptogotchiCollection.ts b/frontend/src/hooks/useGetAptogotchiCollection.ts index 2c823d00..4d89a5db 100644 --- a/frontend/src/hooks/useGetAptogotchiCollection.ts +++ b/frontend/src/hooks/useGetAptogotchiCollection.ts @@ -1,9 +1,9 @@ import { useCallback, useState } from "react"; import { useWallet } from "@aptos-labs/wallet-adapter-react"; import { getAptosClient } from "@/utils/aptosClient"; -import { NEXT_PUBLIC_CONTRACT_ADDRESS } from "@/utils/env"; import { queryAptogotchiCollection } from "@/graphql/queryAptogotchiCollection"; import { padAddressIfNeeded } from "@/utils/address"; +import { ABI } from "@/utils/abi"; const aptosClient = getAptosClient(); @@ -39,7 +39,7 @@ export function useGetAptogotchiCollection() { const aptogotchiCollectionAddressResponse = (await aptosClient.view({ payload: { - function: `${NEXT_PUBLIC_CONTRACT_ADDRESS}::main::get_aptogotchi_collection_address`, + function: `${ABI.address}::main::get_aptogotchi_collection_address`, }, })) as [`0x${string}`]; @@ -65,7 +65,7 @@ export function useGetAptogotchiCollection() { .map((holder) => aptosClient.view({ payload: { - function: `${NEXT_PUBLIC_CONTRACT_ADDRESS}::main::get_aptogotchi`, + function: `${ABI.address}::main::get_aptogotchi`, functionArguments: [holder.owner_address], }, }) diff --git a/frontend/src/utils/abi.ts b/frontend/src/utils/abi.ts new file mode 100644 index 00000000..cf7b7956 --- /dev/null +++ b/frontend/src/utils/abi.ts @@ -0,0 +1,144 @@ +export const ABI = { + address: "0xb686acdc6c166f92aa2090f005acc275b258c5d91653df9b3b8af21e7c104773", + name: "main", + friends: [], + exposed_functions: [ + { + name: "create_aptogotchi", + visibility: "public", + is_entry: true, + is_view: false, + generic_type_params: [], + params: ["&signer", "0x1::string::String", "u8", "u8", "u8"], + return: [], + }, + { + name: "feed", + visibility: "public", + is_entry: true, + is_view: false, + generic_type_params: [], + params: ["&signer", "u64"], + return: [], + }, + { + name: "get_aptogotchi", + visibility: "public", + is_entry: false, + is_view: true, + generic_type_params: [], + params: ["address"], + return: [ + "0x1::string::String", + "u64", + "u64", + "0xb686acdc6c166f92aa2090f005acc275b258c5d91653df9b3b8af21e7c104773::main::AptogotchiParts", + ], + }, + { + name: "get_aptogotchi_address", + visibility: "public", + is_entry: false, + is_view: true, + generic_type_params: [], + params: ["address"], + return: ["address"], + }, + { + name: "get_aptogotchi_collection_address", + visibility: "public", + is_entry: false, + is_view: true, + generic_type_params: [], + params: [], + return: ["address"], + }, + { + name: "has_aptogotchi", + visibility: "public", + is_entry: false, + is_view: true, + generic_type_params: [], + params: ["address"], + return: ["bool"], + }, + { + name: "play", + visibility: "public", + is_entry: true, + is_view: false, + generic_type_params: [], + params: ["&signer", "u64"], + return: [], + }, + { + name: "set_name", + visibility: "public", + is_entry: true, + is_view: false, + generic_type_params: [], + params: ["signer", "0x1::string::String"], + return: [], + }, + { + name: "set_parts", + visibility: "public", + is_entry: true, + is_view: false, + generic_type_params: [], + params: ["&signer", "u8", "u8", "u8"], + return: [], + }, + ], + structs: [ + { + name: "Aptogotchi", + is_native: false, + abilities: ["key"], + generic_type_params: [], + fields: [ + { name: "name", type: "0x1::string::String" }, + { name: "birthday", type: "u64" }, + { name: "energy_points", type: "u64" }, + { + name: "parts", + type: "0xb686acdc6c166f92aa2090f005acc275b258c5d91653df9b3b8af21e7c104773::main::AptogotchiParts", + }, + { name: "mutator_ref", type: "0x4::token::MutatorRef" }, + { name: "burn_ref", type: "0x4::token::BurnRef" }, + ], + }, + { + name: "AptogotchiParts", + is_native: false, + abilities: ["copy", "drop", "store", "key"], + generic_type_params: [], + fields: [ + { name: "body", type: "u8" }, + { name: "ear", type: "u8" }, + { name: "face", type: "u8" }, + ], + }, + { + name: "MintAptogotchiEvent", + is_native: false, + abilities: ["drop", "store"], + generic_type_params: [], + fields: [ + { name: "token_name", type: "0x1::string::String" }, + { name: "aptogotchi_name", type: "0x1::string::String" }, + { + name: "parts", + type: "0xb686acdc6c166f92aa2090f005acc275b258c5d91653df9b3b8af21e7c104773::main::AptogotchiParts", + }, + ], + }, + { + name: "ObjectController", + is_native: false, + abilities: ["key"], + generic_type_params: [], + fields: [{ name: "app_extend_ref", type: "0x1::object::ExtendRef" }], + }, + ], +} as const; diff --git a/frontend/src/utils/env.ts b/frontend/src/utils/env.ts index f1bcc739..831fee39 100644 --- a/frontend/src/utils/env.ts +++ b/frontend/src/utils/env.ts @@ -1,5 +1,3 @@ -export const NEXT_PUBLIC_CONTRACT_ADDRESS = process.env - .NEXT_PUBLIC_CONTRACT_ADDRESS as `0x${string}`; export const NEXT_PUBLIC_BODY_OPTIONS = process.env.NEXT_PUBLIC_BODY_OPTIONS; export const NEXT_PUBLIC_EAR_OPTIONS = process.env.NEXT_PUBLIC_EAR_OPTIONS; export const NEXT_PUBLIC_FACE_OPTIONS = process.env.NEXT_PUBLIC_FACE_OPTIONS; diff --git a/move/Move.toml b/move/Move.toml index 42b905fc..3d75f33e 100644 --- a/move/Move.toml +++ b/move/Move.toml @@ -4,7 +4,7 @@ version = "1.0.0" upgrade_policy = "compatible" [addresses] -aptogotchi = "_" +aptogotchi_addr = "_" [dependencies] AptosFramework = { git = "https://github.com/aptos-labs/aptos-core.git", rev = "main", subdir = "aptos-move/framework/aptos-framework"} @@ -12,4 +12,4 @@ AptosTokenObjects = {git = "https://github.com/aptos-labs/aptos-core.git", rev = # For use when developing and testing this module [dev-addresses] -aptogotchi = "0x100" +aptogotchi_addr = "0x100" diff --git a/move/contract_address.txt b/move/contract_address.txt new file mode 100644 index 00000000..827fd5b6 --- /dev/null +++ b/move/contract_address.txt @@ -0,0 +1 @@ +0xb686acdc6c166f92aa2090f005acc275b258c5d91653df9b3b8af21e7c104773 diff --git a/move/scripts/script_create_gotchi.move b/move/scripts/script_create_gotchi.move index 8a9e2e34..05902c66 100644 --- a/move/scripts/script_create_gotchi.move +++ b/move/scripts/script_create_gotchi.move @@ -3,6 +3,6 @@ script { fun create_gotchi(user: &signer) { let gotchi_name = utf8(b"gotchi"); - aptogotchi::main::create_aptogotchi(user, gotchi_name, 1, 1, 1); + aptogotchi_addr::main::create_aptogotchi(user, gotchi_name, 1, 1, 1); } } diff --git a/move/scripts/script_feed_gotchi.move b/move/scripts/script_feed_gotchi.move index 129598ef..fcd983ed 100644 --- a/move/scripts/script_feed_gotchi.move +++ b/move/scripts/script_feed_gotchi.move @@ -3,7 +3,7 @@ script { // Increasing gotchi's energy point by 2. fun feed_gotchi(user: &signer) { let energy_points = 1; - aptogotchi::main::feed(user, energy_points); - aptogotchi::main::feed(user, energy_points); + aptogotchi_addr::main::feed(user, energy_points); + aptogotchi_addr::main::feed(user, energy_points); } } diff --git a/move/sh_scripts/move_publish.sh b/move/sh_scripts/move_publish.sh index 961ac33d..56564fe2 100755 --- a/move/sh_scripts/move_publish.sh +++ b/move/sh_scripts/move_publish.sh @@ -2,15 +2,21 @@ set -e -echo "##### Publishing module #####" +echo "##### Deploy module under a new object #####" # Profile is the account you used to execute transaction # Run "aptos init" to create the profile, then get the profile name from .aptos/config.yaml -PROFILE=to_fill +PUBLISHER_PROFILE=testnet-profile-1 -ADDR=0x$(aptos config show-profiles --profile=$PROFILE | grep 'account' | sed -n 's/.*"account": \"\(.*\)\".*/\1/p') +PUBLISHER_ADDR=0x$(aptos config show-profiles --profile=$PUBLISHER_PROFILE | grep 'account' | sed -n 's/.*"account": \"\(.*\)\".*/\1/p') -aptos move publish \ - --assume-yes \ - --profile $PROFILE \ - --named-addresses aptogotchi=$ADDR +OUTPUT=$(aptos move create-object-and-publish-package \ + --address-name aptogotchi_addr \ + --named-addresses aptogotchi_addr=$PUBLISHER_ADDR \ + --profile $PUBLISHER_PROFILE \ + --assume-yes) + +# Extract the deployed contract address and save it to a file +echo "$OUTPUT" | grep "Code was successfully deployed to object address" | awk '{print $NF}' | sed 's/\.$//' > contract_address.txt +echo "Contract deployed to address: $(cat contract_address.txt)" +echo "Contract address saved to contract_address.txt" diff --git a/move/sh_scripts/move_upgrade.sh b/move/sh_scripts/move_upgrade.sh new file mode 100755 index 00000000..65f2b658 --- /dev/null +++ b/move/sh_scripts/move_upgrade.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +echo "##### Upgrade published module #####" + +# Profile is the account you used to execute transaction +# Run "aptos init" to create the profile, then get the profile name from .aptos/config.yaml +PUBLISHER_PROFILE=testnet-profile-1 + +CONTRACT_ADDRESS=$(cat contract_address.txt) + +aptos move upgrade-object-package \ + --object-address $CONTRACT_ADDRESS \ + --named-addresses aptogotchi_addr=$CONTRACT_ADDRESS \ + --profile $PUBLISHER_PROFILE \ + --assume-yes diff --git a/move/sources/aptogotchi.move b/move/sources/aptogotchi.move index ee872200..cdef69a9 100644 --- a/move/sources/aptogotchi.move +++ b/move/sources/aptogotchi.move @@ -1,4 +1,4 @@ -module aptogotchi::main { +module aptogotchi_addr::main { use aptos_framework::event; use aptos_framework::object; use aptos_framework::timestamp; @@ -29,7 +29,6 @@ module aptogotchi::main { const ENERGY_UPPER_BOUND: u64 = 10; const NAME_UPPER_BOUND: u64 = 40; - struct AptogotchiParts has copy, drop, key, store { body: u8, ear: u8, @@ -73,7 +72,6 @@ module aptogotchi::main { // Face value range is [0, 3] inslusive const FACE_MAX_VALUE: u8 = 3; - // This function is only called once when the module is published for the first time. fun init_module(account: &signer) { let constructor_ref = object::create_named_object( @@ -90,8 +88,10 @@ module aptogotchi::main { create_aptogotchi_collection(app_signer); } + // ================================= Helper Functions ================================= // + fun get_app_signer_addr(): address { - object::create_object_address(&@aptogotchi, APP_OBJECT_SEED) + object::create_object_address(&@aptogotchi_addr, APP_OBJECT_SEED) } fun get_app_signer(): signer acquires ObjectController { @@ -113,6 +113,8 @@ module aptogotchi::main { ); } + // ================================= Entry Functions ================================= // + // Create an Aptogotchi token object public entry fun create_aptogotchi( user: &signer, @@ -230,6 +232,8 @@ module aptogotchi::main { gotchi.parts.face = face; } + // ================================= View Functions ================================== // + // Get reference to Aptogotchi token object (CAN'T modify the reference) #[view] public fun get_aptogotchi_address(creator_addr: address): (address) { @@ -277,7 +281,8 @@ module aptogotchi::main { (gotchi.name, gotchi.birthday, gotchi.energy_points, gotchi.parts) } - // ==== TESTS ==== + // ================================= Unit Tests ================================== // + // Setup testing environment #[test_only] use aptos_framework::account::create_account_for_test; @@ -295,7 +300,7 @@ module aptogotchi::main { } // Test creating an Aptogotchi - #[test(aptos = @0x1, account = @aptogotchi, creator = @0x123)] + #[test(aptos = @0x1, account = @aptogotchi_addr, creator = @0x123)] fun test_create_aptogotchi( aptos: &signer, account: &signer, @@ -310,8 +315,8 @@ module aptogotchi::main { } // Test getting an Aptogotchi, when user has not minted - #[test(aptos = @0x1, account = @aptogotchi, creator = @0x123)] - #[expected_failure(abort_code = 851969, location = aptogotchi::main)] + #[test(aptos = @0x1, account = @aptogotchi_addr, creator = @0x123)] + #[expected_failure(abort_code = 851969, location = aptogotchi_addr::main)] fun test_get_aptogotchi_without_creation( aptos: &signer, account: &signer, @@ -324,7 +329,7 @@ module aptogotchi::main { } // Test getting an Aptogotchi, when user has not minted - #[test(aptos = @0x1, account = @aptogotchi, creator = @0x123)] + #[test(aptos = @0x1, account = @aptogotchi_addr, creator = @0x123)] fun test_feed_and_play( aptos: &signer, account: &signer, @@ -347,8 +352,8 @@ module aptogotchi::main { } // Test getting an Aptogotchi, when user has not minted - #[test(aptos = @0x1, account = @aptogotchi, creator = @0x123)] - #[expected_failure(abort_code = 524291, location = aptogotchi::main)] + #[test(aptos = @0x1, account = @aptogotchi_addr, creator = @0x123)] + #[expected_failure(abort_code = 524291, location = aptogotchi_addr::main)] fun test_create_aptogotchi_twice( aptos: &signer, account: &signer, From d3185426d498af73d816e1364fa8a332c5f0243e Mon Sep 17 00:00:00 2001 From: 0xaptosj <129789810+0xaptosj@users.noreply.github.com> Date: Mon, 29 Apr 2024 18:32:51 -0700 Subject: [PATCH 2/2] update readme --- README.md | 7 +++---- move/README.MD | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4ff623b2..35ab4ca7 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ We introduce the following concepts: This dApp will be used as an educational demo hosted on [Aptos Learn](https://learn.aptoslabs.com/). +You can find more instructions about the Move contract in move/README.md. + --- This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). @@ -21,10 +23,7 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next First, run the development server: ```bash -npm run dev -# or -yarn dev -# or +cd frontend pnpm dev ``` diff --git a/move/README.MD b/move/README.MD index b13e7e77..9fcc4e09 100644 --- a/move/README.MD +++ b/move/README.MD @@ -1,18 +1,32 @@ # Aptogotchi -## Publish Modules +## Publish and Upgrade Module ```shell +# publish module ./sh_scripts/move_publish.sh ``` +After publishing, go to frontend directory to re-generate the ABI. + +```shell +cd ../frontend +pnpm generate +``` + +Upgrade published module, this only works when you don't have breaking change. +If you have breaking change, you need to publish to a new address. +```shell +./sh_scripts/move_upgrade.sh +``` + ## Run unit tests ```shell ./sh_scripts/move_test.sh ``` -## Run move scripts +## Run Move scripts Create gotchi. ```shell