Skip to content

Commit

Permalink
Merge branch 'main' into issue/131
Browse files Browse the repository at this point in the history
  • Loading branch information
yawn-c111 committed Oct 3, 2024
2 parents 1c351ce + cd6588e commit 21a2b8f
Show file tree
Hide file tree
Showing 8 changed files with 420 additions and 24 deletions.
4 changes: 3 additions & 1 deletion pkgs/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
"@hatsprotocol/sdk-v1-subgraph": "^1.0.0",
"commander": "^12.1.0",
"dotenv": "^16.4.5",
"pinata-web3": "^0.5.0",
"ts-node": "^10.9.2",
"typescript": "^5.6.2",
"viem": "^2.21.15"
"viem": "^2.21.15",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/commander": "^2.12.2",
Expand Down
37 changes: 22 additions & 15 deletions pkgs/cli/src/commands/hats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
createHat,
mintHat,
} from "../modules/hatsProtocol";
import { PinataSDK } from "pinata-web3";
import { getAccount } from "../services/wallet";
import { publicClient, rootProgram, walletClient } from "..";
import { Address } from "viem";
Expand Down Expand Up @@ -33,8 +34,9 @@ hatsCommands
.description("Show all of the Hats that are associated with the tree ID")
.option("-id, --treeId <treeId>", "Tree ID")
.action(async (options) => {
const { chain } = rootProgram.opts();
// ツリー情報を全て取得する。
const tree = await getTreeInfo(Number(options.treeId), options.chainId);
const tree = await getTreeInfo(Number(options.treeId), chain);

console.log(tree);
});
Expand All @@ -47,8 +49,9 @@ hatsCommands
.description("Show all of the wears that are associated with the hat ID")
.option("-id, --hatId <hatId>", "Hat ID")
.action(async (options) => {
const { chain } = rootProgram.opts();
// ツリー情報を全て取得する。
const wearers = await getWearersInfo(options.hatId, options.chainId);
const wearers = await getWearersInfo(options.hatId, chain);

console.log(wearers);
});
Expand All @@ -75,32 +78,34 @@ hatsCommands
hatsCommands
.command("createHat")
.description("Create Hat")
.requiredOption("-hid", "--hatId <hatId>", "Hat ID")
.requiredOption("-img", "--imageUri <imageURI>", "Image URI")
.option("-det", "--details <details>", "Details")
.option("-max", "--maxSupply <maxSupply>", "Max Supply")
.option("-el", "--eligibility <eligibility>", "Eligibility Address")
.option("-tgl", "--toggle <toggle>", "Toggle")
.option("-mut", "--mutable <mutable>", "Mutable")
.requiredOption("-phid, --parentHatId <parentHatId>", "Parent Hat ID")
.requiredOption("-img, --imageURI <imageURI>", "Image URI")
.option("-det , --details <details>", "Details")
.option("-max, --maxSupply <maxSupply>", "Max Supply")
.option("-el, --eligibility <eligibility>", "Eligibility Address")
.option("-tgl, --toggle <toggle>", "Toggle")
.option("-mut, --mutable <mutable>", "Mutable")
.action(
async ({
hatId,
parentHatId,
details,
maxSupply,
eligibility,
toggle,
mutable,
imageURI,
}) => {
await createHat({
parentHatId: BigInt(hatId),
const transactionHash = await createHat({
parentHatId: BigInt(parentHatId),
details,
maxSupply,
eligibility: eligibility as Address,
toggle: toggle as Address,
mutable: mutable,
mutable: mutable == "true",
imageURI,
});

console.log("Transaction hash: ", transactionHash);
}
);

Expand All @@ -110,8 +115,10 @@ hatsCommands
hatsCommands
.command("mintHat")
.description("Mint Hat")
.requiredOption("-hid", "--hatId <hatId>", "Hat ID")
.requiredOption("-hid, --hatId <hatId>", "Hat ID")
.requiredOption("--wearer <wearer>", "Wearer address")
.action(async ({ hatId, wearer }) => {
await mintHat({ hatId, wearer });
const transactionHash = await mintHat({ hatId, wearer });

console.log("Transaction hash: ", transactionHash);
});
111 changes: 111 additions & 0 deletions pkgs/cli/src/commands/pinata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { Command } from "commander";
import { getJwt, setJwt } from "../services/pinata";
import { PinataSDK } from "pinata-web3";

export const pinataCommands = new Command();

// ###############################################################
// CLI init setup
// ###############################################################

pinataCommands
.name("pinata")
.description("This is a CLI for pinata")
.version("1.0.0");

/**
* PinataのJWTを設定するコマンド
*/
pinataCommands
.command("setJwt")
.description("Set a jwt of Pinata")
.requiredOption("--jwt <JWT>")
.action(({ jwt }) => {
setJwt(jwt);
});

/**
* Hatのメタデータをipfs上にアップロードするコマンド
*/
interface Responsibility {
label: string;
description?: string;
link?: string;
}
interface Eligibility {
manual: boolean;
criteria: string[];
}
interface Toggle {
manual: boolean;
criteria: string[];
}

pinataCommands
.command("uploadMetadata")
.description("Upload the hat metadata on ipfs.")
.requiredOption("-n, --name <name>", "Hat Name")
.requiredOption("-d, --description <description>", "Hat Details")
.option(
"-r, --responsibilities <label>,<description>,<link>",
"Responsibilities (may be specified multiple times to define multiple responsibilities)",
(value, previous: Responsibility[]) => {
const [label, description, link] = value.split(",");
return previous
? previous.concat([{ label, description, link }])
: [{ label, description, link }];
},
[]
)
.option(
"-a, --authorities <authorities>",
"Authority (may be specified multiple times to define multiple authorities)",
(value, previous: string[]) =>
previous ? previous.concat([value]) : [value],
[]
)
.option(
"-e, --eligibility <manual>,<criteria...>",
"Eligibility (<manual> is a boolean value, <criteria... > can be specified multiple times, separated by commas, to define multiple criteria.)",
(value) => {
const [manual, ...criteria] = value.split(",");
return { manual: manual === "true", criteria } satisfies Eligibility;
}
)
.option(
"-t, --toggle <manual> <criteria...>",
"Toggle (<manual> is a boolean value, <criteria... > can be specified multiple times, separated by spaces, to define multiple criteria.)",
(value) => {
const [manual, ...criteria] = value.split(",");
return { manual: manual === "true", criteria } satisfies Toggle;
}
)
.action(
async ({
name,
description,
responsibilities,
authorities,
eligibility,
toggle,
}) => {
const { jwt } = getJwt();

const pinata = new PinataSDK({ pinataJwt: jwt });

const upload = await pinata.upload.json({
type: "1.0",
data: {
name,
description,
responsibilities,
authorities: authorities,
eligibility,
toggle,
},
});

console.log("CID:", upload.IpfsHash);
console.log("URI:", `ipfs://${upload.IpfsHash}`);
}
);
1 change: 0 additions & 1 deletion pkgs/cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export const hatsContractBaseConfig = {
};

export const hatsTimeFrameContractBaseConfig = {
address: "0xd4a66507ea8c8382fa8474ed6cae4163676a434a" as Address,
abi: HATS_TIME_FRAME_MODULE_ABI,
};

Expand Down
2 changes: 2 additions & 0 deletions pkgs/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { getPublicClient } from "./modules/viem";
import { getWalletClient } from "./services/wallet";
import { skipPreActionCommands } from "./config";
import { bigbangCommands } from "./commands/bigbang";
import { pinataCommands } from "./commands/pinata";

export const rootProgram = new Command();

Expand All @@ -35,5 +36,6 @@ rootProgram.addCommand(bigbangCommands);
rootProgram.addCommand(hatsCommands);
rootProgram.addCommand(walletCommands);
rootProgram.addCommand(splitsCommands);
rootProgram.addCommand(pinataCommands);

rootProgram.parse(process.argv);
33 changes: 31 additions & 2 deletions pkgs/cli/src/modules/hatsProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
hatsTimeFrameContractBaseConfig,
} from "../config";
import { publicClient, walletClient } from "..";
import { hatIdToTreeId } from "@hatsprotocol/sdk-v1-core";

// ###############################################################
// Read with subgraph
Expand Down Expand Up @@ -103,6 +104,26 @@ export const getWearerInfo = async (walletAddress: string, chainId: number) => {
return wearer;
};

export const getHatsTimeframeModuleAddress = async (
hatId: string,
chainId: number
) => {
const treeId = hatIdToTreeId(BigInt(hatId));
const { hats } = await getTreeInfo(treeId, chainId);
const hatterHat = hats?.find((hat) => hat.levelAtLocalTree === 1);
if (!hatterHat) {
throw new Error("Hatter hat not found");
}

const wearers = await getWearersInfo(hatterHat.id, chainId);

if (wearers.length === 0) {
throw new Error("No wearers found for hatter hat");
}

return wearers[0].id;
};

// ###############################################################
// Write with viem
// ###############################################################
Expand Down Expand Up @@ -133,7 +154,9 @@ export const createHat = async (args: {
args.imageURI,
],
});
walletClient.writeContract(request);
const transactionHash = walletClient.writeContract(request);

return transactionHash;
};

/**
Expand All @@ -142,9 +165,15 @@ export const createHat = async (args: {
export const mintHat = async (args: { hatId: bigint; wearer: Address }) => {
const { request } = await publicClient.simulateContract({
...hatsTimeFrameContractBaseConfig,
address: await getHatsTimeframeModuleAddress(
args.hatId.toString(),
Number(publicClient.chain?.id)
),
account: walletClient.account,
functionName: "mintHat",
args: [args.hatId, args.wearer],
});
walletClient.writeContract(request);
const transactionHash = await walletClient.writeContract(request);

return transactionHash;
};
20 changes: 20 additions & 0 deletions pkgs/cli/src/services/pinata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { existsSync, readFileSync, writeFileSync } from "fs";
import path from "path";
const pinataPath = path.join(__dirname, "pinata.json");

export interface Pinata {
jwt: string
}

export const getJwt = () => {
if (!existsSync(pinataPath)) {
writeFileSync(pinataPath, JSON.stringify({ jwt: "" }));
}

const data = readFileSync(pinataPath, "utf8");
return JSON.parse(data) as Pinata;
};

export const setJwt = (jwt: string) => {
writeFileSync(pinataPath, JSON.stringify({ jwt: jwt }));
}
Loading

0 comments on commit 21a2b8f

Please sign in to comment.