From df13a8fc787210fdb7ed4293eb7412b659c6ae25 Mon Sep 17 00:00:00 2001 From: yawn <69970183+yawn-c111@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:39:25 +0900 Subject: [PATCH 1/6] feat: viem client and sendEth --- pkgs/cli/src/commands/wallet.ts | 18 +++++----- pkgs/cli/src/modules/viem.ts | 58 ++++++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/pkgs/cli/src/commands/wallet.ts b/pkgs/cli/src/commands/wallet.ts index 6da93ab..1c0b2f0 100644 --- a/pkgs/cli/src/commands/wallet.ts +++ b/pkgs/cli/src/commands/wallet.ts @@ -1,11 +1,9 @@ import { Command } from "commander"; import { getEthAddress, sendEth } from "../modules/viem"; -import { listProfiles, saveProfile } from "../services/wallet"; +import { getWallet, listProfiles, saveProfile } from "../services/wallet"; export const walletCommands = new Command(); -const { TOBAN_PRIVATE_KEY } = process.env; - walletCommands .name("wallet") .description("This is a CLI function for toban project") @@ -39,11 +37,11 @@ walletCommands walletCommands .command("sendEth") .description("Send ETH") - .action(async () => { - console.log("Start send ETH"); - const address1 = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"; - - await sendEth(TOBAN_PRIVATE_KEY as `0x${string}`, address1); - - console.log("End send ETH"); + .requiredOption("--name ", "Wallet name") + .requiredOption("--receiver ", "Receiver address") + .requiredOption("--amount ", "Amount") + .option("--chainId ", "chainId") + .action(async ({ name, receiver, amount, chainId }) => { + const account = getWallet(name); + await sendEth(account, receiver, amount, chainId); }); diff --git a/pkgs/cli/src/modules/viem.ts b/pkgs/cli/src/modules/viem.ts index 0f703c6..6387f09 100644 --- a/pkgs/cli/src/modules/viem.ts +++ b/pkgs/cli/src/modules/viem.ts @@ -1,28 +1,62 @@ -import { createWalletClient, http, parseEther } from "viem"; +import { + Chain, + createWalletClient, + http, + parseEther, + PrivateKeyAccount, +} from "viem"; import { privateKeyToAccount } from "viem/accounts"; -import { hardhat } from "viem/chains"; +import { hardhat, sepolia, holesky } from "viem/chains"; -/** - * ETHを送金するためのメソッド - * @param secretKey - * @param to - * @returns - */ -export const sendEth = async (secretKey: `0x${string}`, to: `0x${string}`) => { - const account = privateKeyToAccount(secretKey); +const chains = [hardhat, sepolia, holesky]; + +function getChainById(chainId: number | string): Chain { + const numericChainId = Number(chainId); + + const chain = chains.find((c) => c.id === numericChainId); + + if (!chain) { + throw new Error(`Chain with id ${numericChainId} not found`); + } + + return chain; +} + +export const setClient = async ( + account: PrivateKeyAccount, + chainId?: number | undefined +) => { + const chain = chainId ? getChainById(chainId) : holesky; const client = createWalletClient({ account, - chain: hardhat, + chain, transport: http(), }); + return client; +}; + +export const sendEth = async ( + account: PrivateKeyAccount, + to: `0x${string}`, + amount: string, + chainId?: number +) => { + const client = await setClient(account, chainId); + const hash = await client.sendTransaction({ account, to: to, - value: parseEther("0.001"), + value: parseEther(amount), }); + console.log(`Transaction sent: ${hash}`); + console.log(`From: ${account.address}`); + console.log(`To: ${to}`); + console.log(`Amount: ${amount} ETH`); + console.log(`Chain ID: ${client.chain.id}`); + return hash; }; From 015c0b998cb9e4dfec35635293da1b025a8db30f Mon Sep 17 00:00:00 2001 From: yawn <69970183+yawn-c111@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:15:43 +0900 Subject: [PATCH 2/6] update: getWallet and sendEth --- pkgs/cli/src/commands/wallet.ts | 6 +++--- pkgs/cli/src/modules/viem.ts | 25 +++++++++++++++---------- pkgs/cli/src/services/wallet.ts | 11 +++++++++-- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/pkgs/cli/src/commands/wallet.ts b/pkgs/cli/src/commands/wallet.ts index 17d9450..9bcc2c6 100644 --- a/pkgs/cli/src/commands/wallet.ts +++ b/pkgs/cli/src/commands/wallet.ts @@ -57,7 +57,7 @@ walletCommands .requiredOption("--receiver ", "Receiver address") .requiredOption("--amount ", "Amount") .option("--chainId ", "chainId") - .action(async ({ name, receiver, amount, chainId }) => { - const account = getWallet(name); - await sendEth(account, receiver, amount, chainId); + .action(async ({ name, receiver, amount }) => { + const wallet = await getWallet(name); + await sendEth(wallet, receiver, amount); }); diff --git a/pkgs/cli/src/modules/viem.ts b/pkgs/cli/src/modules/viem.ts index 6387f09..35bab5f 100644 --- a/pkgs/cli/src/modules/viem.ts +++ b/pkgs/cli/src/modules/viem.ts @@ -4,6 +4,7 @@ import { http, parseEther, PrivateKeyAccount, + WalletClient, } from "viem"; import { privateKeyToAccount } from "viem/accounts"; import { hardhat, sepolia, holesky } from "viem/chains"; @@ -22,40 +23,44 @@ function getChainById(chainId: number | string): Chain { return chain; } -export const setClient = async ( +export const setWallet = async ( account: PrivateKeyAccount, chainId?: number | undefined ) => { const chain = chainId ? getChainById(chainId) : holesky; - const client = createWalletClient({ + const wallet = createWalletClient({ account, chain, transport: http(), }); - return client; + return wallet; }; export const sendEth = async ( - account: PrivateKeyAccount, + wallet: WalletClient, to: `0x${string}`, - amount: string, - chainId?: number + amount: string ) => { - const client = await setClient(account, chainId); + const account = wallet.account; + + if (!account) { + throw new Error("Client account is not defined"); + } - const hash = await client.sendTransaction({ + const hash = await wallet.sendTransaction({ account, - to: to, + to, value: parseEther(amount), + chain: wallet.chain, }); console.log(`Transaction sent: ${hash}`); console.log(`From: ${account.address}`); console.log(`To: ${to}`); console.log(`Amount: ${amount} ETH`); - console.log(`Chain ID: ${client.chain.id}`); + console.log(`Chain ID: ${wallet.chain?.id}`); return hash; }; diff --git a/pkgs/cli/src/services/wallet.ts b/pkgs/cli/src/services/wallet.ts index 3ed470f..b96e838 100644 --- a/pkgs/cli/src/services/wallet.ts +++ b/pkgs/cli/src/services/wallet.ts @@ -3,6 +3,7 @@ import path from "path"; import { Hex } from "viem"; import { privateKeyToAccount, privateKeyToAddress } from "viem/accounts"; const profilesPath = path.join(__dirname, "profiles.json"); +import { setWallet } from "../modules/viem"; export interface Profile { name: string; @@ -17,7 +18,7 @@ export const getProfiles = () => { return JSON.parse(data) as Profile[]; }; -export const getWallet = (name?: string) => { +export const getAccount = (name?: string) => { const profiles = getProfiles(); const profile = profiles.find((p) => p.name === name) || profiles[0]; @@ -26,6 +27,12 @@ export const getWallet = (name?: string) => { return privateKeyToAccount(profile.privateKey); }; +export const getWallet = (name?: string, chainId?: number | undefined) => { + const account = getAccount(name); + + return setWallet(account, chainId); +}; + export const saveProfile = (params: Profile) => { if (!params.privateKey.match(/^0x[0-9a-f]{64}$/)) { console.log("Invalid private key."); @@ -59,7 +66,7 @@ export const deleteProfile = (params: { name: string }) => { writeFileSync(profilesPath, JSON.stringify(profiles, null, 2)); console.log(`Profile "${params.name}" with private key has been deleted.`); -} +}; export const listProfiles = () => { const profiles = getProfiles(); From 2097527546c7d686cc9a295b291e6e15a5b73a13 Mon Sep 17 00:00:00 2001 From: yawn <69970183+yawn-c111@users.noreply.github.com> Date: Wed, 2 Oct 2024 22:01:01 +0900 Subject: [PATCH 3/6] feat: Added mintHat command --- pkgs/cli/src/abi/hatsTimeFrameModule.ts | 217 ++++++++++++++++++++++++ pkgs/cli/src/commands/wallet.ts | 23 ++- pkgs/cli/src/modules/hatsProtocol.ts | 27 +++ pkgs/cli/src/modules/viem.ts | 24 ++- yarn.lock | 31 +--- 5 files changed, 288 insertions(+), 34 deletions(-) create mode 100644 pkgs/cli/src/abi/hatsTimeFrameModule.ts diff --git a/pkgs/cli/src/abi/hatsTimeFrameModule.ts b/pkgs/cli/src/abi/hatsTimeFrameModule.ts new file mode 100644 index 0000000..4a842b2 --- /dev/null +++ b/pkgs/cli/src/abi/hatsTimeFrameModule.ts @@ -0,0 +1,217 @@ +export const HATS_TIME_FRAME_MODULE_ABI = [ + { + inputs: [ + { + internalType: "address", + name: "_trustedForwarder", + type: "address", + }, + { + internalType: "string", + name: "_version", + type: "string", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [], + name: "InvalidInitialization", + type: "error", + }, + { + inputs: [], + name: "NotInitializing", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint64", + name: "version", + type: "uint64", + }, + ], + name: "Initialized", + type: "event", + }, + { + inputs: [], + name: "HATS", + outputs: [ + { + internalType: "contract IHats", + name: "", + type: "address", + }, + ], + stateMutability: "pure", + type: "function", + }, + { + inputs: [], + name: "IMPLEMENTATION", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "pure", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "wearer", + type: "address", + }, + { + internalType: "uint256", + name: "hatId", + type: "uint256", + }, + ], + name: "getWearingElapsedTime", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "wearer", + type: "address", + }, + { + internalType: "uint256", + name: "hatId", + type: "uint256", + }, + ], + name: "getWoreTime", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "hatId", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "pure", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "forwarder", + type: "address", + }, + ], + name: "isTrustedForwarder", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "hatId", + type: "uint256", + }, + { + internalType: "address", + name: "wearer", + type: "address", + }, + ], + name: "mintHat", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "bytes", + name: "_initData", + type: "bytes", + }, + ], + name: "setUp", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "trustedForwarder", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "version", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "version_", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; diff --git a/pkgs/cli/src/commands/wallet.ts b/pkgs/cli/src/commands/wallet.ts index 9bcc2c6..2d113bd 100644 --- a/pkgs/cli/src/commands/wallet.ts +++ b/pkgs/cli/src/commands/wallet.ts @@ -1,11 +1,12 @@ import { Command } from "commander"; -import { getEthAddress, sendEth } from "../modules/viem"; +import { getPublicClient, sendEth } from "../modules/viem"; import { getWallet, listProfiles, saveProfile, deleteProfile, } from "../services/wallet"; +import { mintHat } from "../modules/hatsProtocol"; export const walletCommands = new Command(); @@ -57,7 +58,23 @@ walletCommands .requiredOption("--receiver ", "Receiver address") .requiredOption("--amount ", "Amount") .option("--chainId ", "chainId") - .action(async ({ name, receiver, amount }) => { - const wallet = await getWallet(name); + .action(async ({ name, receiver, amount, chainId }) => { + const wallet = await getWallet(name, chainId); await sendEth(wallet, receiver, amount); }); + +/** + * ロールを付与 + */ +walletCommands + .command("mintHat") + .description("Mint Hat") + .requiredOption("--name ", "Wallet name") + .requiredOption("--hatId ", "Hat ID") + .requiredOption("--wearer ", "Wearer address") + .option("--chainId ", "chainId") + .action(async ({ name, hatId, wearer, chainId }) => { + const publicClient = await getPublicClient(chainId); + const wallet = await getWallet(name, chainId); + await mintHat(publicClient, wallet, { hatId, wearer }); + }); diff --git a/pkgs/cli/src/modules/hatsProtocol.ts b/pkgs/cli/src/modules/hatsProtocol.ts index d0988e1..c2e2ac3 100644 --- a/pkgs/cli/src/modules/hatsProtocol.ts +++ b/pkgs/cli/src/modules/hatsProtocol.ts @@ -1,5 +1,7 @@ import { HatsSubgraphClient } from "@hatsprotocol/sdk-v1-subgraph"; +import { Address, PublicClient, WalletClient } from "viem"; import { base, optimism, sepolia } from "viem/chains"; +import { HATS_TIME_FRAME_MODULE_ABI } from "../abi/hatsTimeFrameModule"; // Subgraph用のインスタンスを生成 export const hatsSubgraphClient = new HatsSubgraphClient({ @@ -92,3 +94,28 @@ export const getWearerInfo = async (walletAddress: string) => { return wearer; }; + +const hatsTimeFrameContractBaseConfig = { + address: "0x0000000000000000000000000000000000004a75" as Address, + abi: HATS_TIME_FRAME_MODULE_ABI, +}; + +/** + * ロール付与 + */ +export const mintHat = async ( + publicClient: PublicClient, + walletClient: WalletClient, + args: { + hatId: bigint; + wearer: Address; + } +) => { + const { request } = await publicClient.simulateContract({ + ...hatsTimeFrameContractBaseConfig, + account: walletClient.account, + functionName: "mintHat", + args: [args.hatId, args.wearer], + }); + walletClient.writeContract(request); +}; diff --git a/pkgs/cli/src/modules/viem.ts b/pkgs/cli/src/modules/viem.ts index 35bab5f..5010da7 100644 --- a/pkgs/cli/src/modules/viem.ts +++ b/pkgs/cli/src/modules/viem.ts @@ -1,5 +1,6 @@ import { Chain, + createPublicClient, createWalletClient, http, parseEther, @@ -11,7 +12,7 @@ import { hardhat, sepolia, holesky } from "viem/chains"; const chains = [hardhat, sepolia, holesky]; -function getChainById(chainId: number | string): Chain { +export const getChainById = (chainId: number | string): Chain => { const numericChainId = Number(chainId); const chain = chains.find((c) => c.id === numericChainId); @@ -21,13 +22,30 @@ function getChainById(chainId: number | string): Chain { } return chain; -} +}; + +export const getChainOrDefault = ( + chainId: number | string | undefined +): Chain => { + return chainId ? getChainById(chainId) : holesky; +}; + +export const getPublicClient = async (chainId?: number | undefined) => { + const chain = getChainOrDefault(chainId); + + const publicClient = createPublicClient({ + chain, + transport: http(), + }); + + return publicClient; +}; export const setWallet = async ( account: PrivateKeyAccount, chainId?: number | undefined ) => { - const chain = chainId ? getChainById(chainId) : holesky; + const chain = getChainOrDefault(chainId); const wallet = createWalletClient({ account, diff --git a/yarn.lock b/yarn.lock index 125585a..9262b58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5330,7 +5330,7 @@ streamsearch@^1.1.0: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -5347,15 +5347,6 @@ string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -5441,7 +5432,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -5455,13 +5446,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -5973,16 +5957,7 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 99d621106e3d9d20ef4de30cb2496d43285404ac Mon Sep 17 00:00:00 2001 From: yawn <69970183+yawn-c111@users.noreply.github.com> Date: Wed, 2 Oct 2024 23:11:53 +0900 Subject: [PATCH 4/6] update: configs and chains --- pkgs/cli/src/config.ts | 13 +++++++++++++ pkgs/cli/src/modules/hatsProtocol.ts | 19 +++++-------------- pkgs/cli/src/modules/viem.ts | 4 ++-- 3 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 pkgs/cli/src/config.ts diff --git a/pkgs/cli/src/config.ts b/pkgs/cli/src/config.ts new file mode 100644 index 0000000..fbbb023 --- /dev/null +++ b/pkgs/cli/src/config.ts @@ -0,0 +1,13 @@ +import { Address } from "viem"; +import { HATS_ABI } from "./abi/hats"; +import { HATS_TIME_FRAME_MODULE_ABI } from "./abi/hatsTimeFrameModule"; + +export const hatsContractBaseConfig = { + address: "0x0000000000000000000000000000000000004a75" as Address, + abi: HATS_ABI, +}; + +export const hatsTimeFrameContractBaseConfig = { + address: "0xd4a66507ea8c8382fa8474ed6cae4163676a434a" as Address, + abi: HATS_TIME_FRAME_MODULE_ABI, +}; diff --git a/pkgs/cli/src/modules/hatsProtocol.ts b/pkgs/cli/src/modules/hatsProtocol.ts index bfe67d4..1bf7591 100644 --- a/pkgs/cli/src/modules/hatsProtocol.ts +++ b/pkgs/cli/src/modules/hatsProtocol.ts @@ -1,9 +1,10 @@ import { HatsSubgraphClient } from "@hatsprotocol/sdk-v1-subgraph"; -import { Address, getContract, PublicClient, WalletClient } from "viem"; +import { Address, PublicClient, WalletClient } from "viem"; import { base, optimism, sepolia } from "viem/chains"; -import { HATS_ABI } from "../abi/hats"; -import { HATS_TIME_FRAME_MODULE_ABI } from "../abi/hatsTimeFrameModule"; -import { simulateContract } from "viem/_types/actions/public/simulateContract"; +import { + hatsContractBaseConfig, + hatsTimeFrameContractBaseConfig, +} from "../config"; // ############################################################### // Read with subgraph @@ -105,16 +106,6 @@ export const getWearerInfo = async (walletAddress: string) => { // Write with viem // ############################################################### -const hatsContractBaseConfig = { - address: "0x0000000000000000000000000000000000004a75" as Address, - abi: HATS_ABI, -}; - -const hatsTimeFrameContractBaseConfig = { - address: "0x0000000000000000000000000000000000004a75" as Address, - abi: HATS_TIME_FRAME_MODULE_ABI, -}; - /** * 新規Hat作成 */ diff --git a/pkgs/cli/src/modules/viem.ts b/pkgs/cli/src/modules/viem.ts index 5010da7..267747b 100644 --- a/pkgs/cli/src/modules/viem.ts +++ b/pkgs/cli/src/modules/viem.ts @@ -10,7 +10,7 @@ import { import { privateKeyToAccount } from "viem/accounts"; import { hardhat, sepolia, holesky } from "viem/chains"; -const chains = [hardhat, sepolia, holesky]; +const chains = [hardhat, holesky, sepolia]; export const getChainById = (chainId: number | string): Chain => { const numericChainId = Number(chainId); @@ -27,7 +27,7 @@ export const getChainById = (chainId: number | string): Chain => { export const getChainOrDefault = ( chainId: number | string | undefined ): Chain => { - return chainId ? getChainById(chainId) : holesky; + return chainId ? getChainById(chainId) : sepolia; }; export const getPublicClient = async (chainId?: number | undefined) => { From 9fe6f7cadd3f6df303bd978c0e16ff56f6e6be04 Mon Sep 17 00:00:00 2001 From: yawn <69970183+yawn-c111@users.noreply.github.com> Date: Wed, 2 Oct 2024 23:24:52 +0900 Subject: [PATCH 5/6] update: Hats contract address for sepolia --- pkgs/cli/src/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/cli/src/config.ts b/pkgs/cli/src/config.ts index fbbb023..9594678 100644 --- a/pkgs/cli/src/config.ts +++ b/pkgs/cli/src/config.ts @@ -3,7 +3,7 @@ import { HATS_ABI } from "./abi/hats"; import { HATS_TIME_FRAME_MODULE_ABI } from "./abi/hatsTimeFrameModule"; export const hatsContractBaseConfig = { - address: "0x0000000000000000000000000000000000004a75" as Address, + address: "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137" as Address, abi: HATS_ABI, }; From 83497c618a32699844b6fff95b82b38501692e22 Mon Sep 17 00:00:00 2001 From: yu23ki14 Date: Thu, 3 Oct 2024 20:07:19 +0900 Subject: [PATCH 6/6] fix command --- pkgs/cli/src/commands/function.ts | 70 ---------------------------- pkgs/cli/src/commands/hats.ts | 23 +++++++-- pkgs/cli/src/commands/wallet.ts | 34 ++------------ pkgs/cli/src/config.ts | 2 + pkgs/cli/src/index.ts | 35 +++++++++++--- pkgs/cli/src/modules/hatsProtocol.ts | 22 ++++----- pkgs/cli/src/services/wallet.ts | 8 +++- 7 files changed, 70 insertions(+), 124 deletions(-) delete mode 100644 pkgs/cli/src/commands/function.ts diff --git a/pkgs/cli/src/commands/function.ts b/pkgs/cli/src/commands/function.ts deleted file mode 100644 index 5fda2d9..0000000 --- a/pkgs/cli/src/commands/function.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Command } from "commander"; - -export const functionCommands = new Command(); - -// ############################################################### -// CLI init setup -// ############################################################### - -functionCommands - .name("function") - .description("This is a CLI function for toban project") - .version("1.0.0"); - -// ############################################################### -// command setUp -// ############################################################### - -// シフトデータを保持するための簡易データ -let shifts = [ - { date: "2024-10-01", person: "Alice" }, - { date: "2024-10-02", person: "Bob" }, -]; - -/** - * シフトを一覧表示するコマンド - */ -functionCommands - .command("list") - .description("List all shifts") - .action(() => { - console.log("Current shifts:"); - shifts.forEach((shift, index) => { - console.log(`${index + 1}. ${shift.date} - ${shift.person}`); - }); - }); - -/** - * 新しいシフトを追加するコマンド - */ -functionCommands - .command("add ") - .description("Add a new shift") - .action((date, person) => { - shifts.push({ date, person }); - console.log(`Added new shift: ${date} - ${person}`); - }); - -/** - * ランダムで担当者を選ぶコマンド - */ -functionCommands - .command("random") - .description("Pick a random person for the shift") - .action(() => { - const randomIndex = Math.floor(Math.random() * shifts.length); - console.log( - `Selected: ${shifts[randomIndex].person} for ${shifts[randomIndex].date}` - ); - }); - -/** - * 引数を表示するだけのコマンド - */ -functionCommands - .command("show") - .description("Show the arguments") - .option("-t, --text ", "Show all arguments") - .action(async (options) => { - console.log("your args:", options.text); - }); diff --git a/pkgs/cli/src/commands/hats.ts b/pkgs/cli/src/commands/hats.ts index 46b3c41..3a46acd 100644 --- a/pkgs/cli/src/commands/hats.ts +++ b/pkgs/cli/src/commands/hats.ts @@ -3,7 +3,10 @@ import { getTreeInfo, getWearerInfo, getWearersInfo, + mintHat, } from "../modules/hatsProtocol"; +import { getAccount } from "../services/wallet"; +import { publicClient, rootProgram, walletClient } from ".."; export const hatsCommands = new Command(); @@ -29,7 +32,7 @@ hatsCommands .option("-id, --treeId ", "Tree ID") .action(async (options) => { // ツリー情報を全て取得する。 - const tree = await getTreeInfo(Number(options.treeId)); + const tree = await getTreeInfo(Number(options.treeId), options.chainId); console.log(tree); }); @@ -43,7 +46,7 @@ hatsCommands .option("-id, --hatId ", "Hat ID") .action(async (options) => { // ツリー情報を全て取得する。 - const wearers = await getWearersInfo(options.hatId); + const wearers = await getWearersInfo(options.hatId, options.chainId); console.log(wearers); }); @@ -57,7 +60,21 @@ hatsCommands .option("-addr, --address
", "Wallet Address") .action(async (options) => { // 特定のウォレットアドレスに紐づく情報を全て取得する。 - const wearer = await getWearerInfo(options.address); + const address = + options.address || getAccount(rootProgram.opts().profile).address; + const wearer = await getWearerInfo(address, rootProgram.opts().chain); console.log(wearer); }); + +/** + * ロールを付与 + */ +hatsCommands + .command("mintHat") + .description("Mint Hat") + .requiredOption("--hatId ", "Hat ID") + .requiredOption("--wearer ", "Wearer address") + .action(async ({ hatId, wearer }) => { + await mintHat({ hatId, wearer }); + }); diff --git a/pkgs/cli/src/commands/wallet.ts b/pkgs/cli/src/commands/wallet.ts index 2d113bd..a087141 100644 --- a/pkgs/cli/src/commands/wallet.ts +++ b/pkgs/cli/src/commands/wallet.ts @@ -1,12 +1,7 @@ import { Command } from "commander"; -import { getPublicClient, sendEth } from "../modules/viem"; -import { - getWallet, - listProfiles, - saveProfile, - deleteProfile, -} from "../services/wallet"; -import { mintHat } from "../modules/hatsProtocol"; +import { sendEth } from "../modules/viem"; +import { listProfiles, saveProfile, deleteProfile } from "../services/wallet"; +import { walletClient } from ".."; export const walletCommands = new Command(); @@ -54,27 +49,8 @@ walletCommands walletCommands .command("sendEth") .description("Send ETH") - .requiredOption("--name ", "Wallet name") .requiredOption("--receiver ", "Receiver address") .requiredOption("--amount ", "Amount") - .option("--chainId ", "chainId") - .action(async ({ name, receiver, amount, chainId }) => { - const wallet = await getWallet(name, chainId); - await sendEth(wallet, receiver, amount); - }); - -/** - * ロールを付与 - */ -walletCommands - .command("mintHat") - .description("Mint Hat") - .requiredOption("--name ", "Wallet name") - .requiredOption("--hatId ", "Hat ID") - .requiredOption("--wearer ", "Wearer address") - .option("--chainId ", "chainId") - .action(async ({ name, hatId, wearer, chainId }) => { - const publicClient = await getPublicClient(chainId); - const wallet = await getWallet(name, chainId); - await mintHat(publicClient, wallet, { hatId, wearer }); + .action(async ({ receiver, amount }) => { + await sendEth(walletClient, receiver, amount); }); diff --git a/pkgs/cli/src/config.ts b/pkgs/cli/src/config.ts index 9594678..4bd8d43 100644 --- a/pkgs/cli/src/config.ts +++ b/pkgs/cli/src/config.ts @@ -2,6 +2,8 @@ import { Address } from "viem"; import { HATS_ABI } from "./abi/hats"; import { HATS_TIME_FRAME_MODULE_ABI } from "./abi/hatsTimeFrameModule"; +export const skipPreActionCommands = ["wallet>add", "wallet>list"]; + export const hatsContractBaseConfig = { address: "0x3bc1A0Ad72417f2d411118085256fC53CBdDd137" as Address, abi: HATS_ABI, diff --git a/pkgs/cli/src/index.ts b/pkgs/cli/src/index.ts index 34e3516..c3a1ee8 100755 --- a/pkgs/cli/src/index.ts +++ b/pkgs/cli/src/index.ts @@ -1,12 +1,35 @@ #!/usr/bin/env node -import { program } from "commander"; -import { functionCommands } from "./commands/function"; +import { Command } from "commander"; import { hatsCommands } from "./commands/hats"; import { walletCommands } from "./commands/wallet"; +import { PublicClient, WalletClient } from "viem"; +import { getPublicClient } from "./modules/viem"; +import { getWalletClient } from "./services/wallet"; +import { skipPreActionCommands } from "./config"; -program.addCommand(functionCommands); -program.addCommand(hatsCommands); -program.addCommand(walletCommands); +export const rootProgram = new Command(); -program.parse(process.argv); +export let publicClient!: PublicClient; + +export let walletClient!: WalletClient; + +rootProgram + .version("0.0.1") + .option("--chain ", "chain id", "11155111") + .option("--profile ", "Wallet profile") + .hook("preAction", async (_, actionCommand) => { + const { chain, profile } = rootProgram.opts(); + const parentName = actionCommand.parent?.name(); + const commandName = actionCommand.name(); + + if (!skipPreActionCommands.includes(`${parentName}>${commandName}`)) { + publicClient = await getPublicClient(chain); + walletClient = await getWalletClient(profile, chain); + } + }); + +rootProgram.addCommand(hatsCommands); +rootProgram.addCommand(walletCommands); + +rootProgram.parse(process.argv); diff --git a/pkgs/cli/src/modules/hatsProtocol.ts b/pkgs/cli/src/modules/hatsProtocol.ts index 1bf7591..25a1d0c 100644 --- a/pkgs/cli/src/modules/hatsProtocol.ts +++ b/pkgs/cli/src/modules/hatsProtocol.ts @@ -5,6 +5,7 @@ import { hatsContractBaseConfig, hatsTimeFrameContractBaseConfig, } from "../config"; +import { publicClient, walletClient } from ".."; // ############################################################### // Read with subgraph @@ -31,9 +32,9 @@ export const hatsSubgraphClient = new HatsSubgraphClient({ /** * ツリー情報を取得するメソッド */ -export const getTreeInfo = async (treeId: number) => { +export const getTreeInfo = async (treeId: number, chainId: number) => { const tree = await hatsSubgraphClient.getTree({ - chainId: optimism.id, + chainId, treeId: treeId, props: { hats: { @@ -59,10 +60,10 @@ export const getTreeInfo = async (treeId: number) => { /** * 帽子の着用者のウォレットアドレスを一覧を取得するメソッド */ -export const getWearersInfo = async (hatId: string) => { +export const getWearersInfo = async (hatId: string, chainId: number) => { // get the first 10 wearers of a given hat const wearers = await hatsSubgraphClient.getWearersOfHatPaginated({ - chainId: optimism.id, + chainId, props: {}, hatId: BigInt(hatId), page: 0, @@ -75,10 +76,10 @@ export const getWearersInfo = async (hatId: string) => { /** * 特定のウォレットアドレスが着用している全てのHats情報を取得するメソッド */ -export const getWearerInfo = async (walletAddress: string) => { +export const getWearerInfo = async (walletAddress: string, chainId: number) => { // get the wearer of a given hat const wearer = await hatsSubgraphClient.getWearer({ - chainId: optimism.id, + chainId, wearerAddress: walletAddress as `0x${string}`, props: { currentHats: { @@ -142,14 +143,7 @@ export const createHat = async ( /** * ロール付与 */ -export const mintHat = async ( - publicClient: PublicClient, - walletClient: WalletClient, - args: { - hatId: bigint; - wearer: Address; - } -) => { +export const mintHat = async (args: { hatId: bigint; wearer: Address }) => { const { request } = await publicClient.simulateContract({ ...hatsTimeFrameContractBaseConfig, account: walletClient.account, diff --git a/pkgs/cli/src/services/wallet.ts b/pkgs/cli/src/services/wallet.ts index b96e838..6c4d388 100644 --- a/pkgs/cli/src/services/wallet.ts +++ b/pkgs/cli/src/services/wallet.ts @@ -22,12 +22,16 @@ export const getAccount = (name?: string) => { const profiles = getProfiles(); const profile = profiles.find((p) => p.name === name) || profiles[0]; - if (!profile) throw "Profile not found."; + if (!profile) + throw "Profile not found. Please add a profile with wallet add command."; return privateKeyToAccount(profile.privateKey); }; -export const getWallet = (name?: string, chainId?: number | undefined) => { +export const getWalletClient = ( + name?: string, + chainId?: number | undefined +) => { const account = getAccount(name); return setWallet(account, chainId);