diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a4be1c..58f4e4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## Unreleased +### Added + +- Built-in ABI defaults for all mapped write commands, removing the need to manually look up ABI signatures for mapped execution and `--help`. + +### Changed + +- Expanded mapped metadata coverage beyond auctions: + - canonical Base addresses now auto-resolve for high-confidence command families (Aavegotchi diamond, GBM diamond, Forge diamond, GLTR staking, Merkle distributor). + - command families with dynamic target contracts still auto-resolve ABI and only require `--address`. + ## 0.2.4 - 2026-02-27 ### Added diff --git a/README.md b/README.md index c3088de..d7422c0 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ Planned domain namespaces are stubbed for parity tracking: - `gotchi`, `portal`, `wearables`, `items`, `inventory`, `baazaar`, `auction`, `lending`, `staking`, `gotchi-points`, `realm`, `alchemica`, `forge`, `token` Many Base-era write flows are already executable as mapped aliases in those namespaces (internally routed through `onchain send`). +Mapped writes now include built-in ABI defaults, so `--abi-file` is no longer required for mapped command execution/help. Example with built-in defaults: `ag auction bid --args-json '[...]' --dry-run --json` Example with explicit metadata: `ag lending create --abi-file ./abis/GotchiLendingFacet.json --address 0x... --args-json '[...]' --json` diff --git a/src/commands/mapped-defaults.ts b/src/commands/mapped-defaults.ts index 22c58e0..4f0baae 100644 --- a/src/commands/mapped-defaults.ts +++ b/src/commands/mapped-defaults.ts @@ -1,6 +1,12 @@ import { parseAbi, type Abi } from "viem"; -import { BASE_GBM_DIAMOND } from "../subgraph/sources"; +import { + BASE_AAVEGOTCHI_DIAMOND, + BASE_FORGE_DIAMOND, + BASE_GBM_DIAMOND, + BASE_GLTR_STAKING, + BASE_MERKLE_DISTRIBUTOR, +} from "../subgraph/sources"; export interface MappedWriteDefaults { address?: `0x${string}`; @@ -8,16 +14,90 @@ export interface MappedWriteDefaults { source: string; } +const AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI = parseAbi([ + "function addGotchiLending(uint32,uint96,uint32,uint8[3],address,address,uint32,address[],uint256)", + "function agreeGotchiLending(uint32,uint32,uint96,uint32,uint8[3])", + "function batchDropClaimXPDrop(bytes32[],address[],uint256[][],bytes32[][],uint256[][],uint256[][])", + "function batchExecuteERC1155Listing((uint256,address,uint256,uint256,uint256,address)[])", + "function cancelERC1155Listing(uint256)", + "function cancelERC721Listing(uint256)", + "function cancelGotchiLending(uint32)", + "function claimAavegotchi(uint256,uint256)", + "function claimAndEndGotchiLending(uint32)", + "function createWhitelist(string,address[])", + "function decreaseAndDestroy(uint256,uint256)", + "function equipDelegatedWearables(uint256,uint16[16],uint256[16])", + "function openPortals(uint256[])", + "function spendSkillPoints(uint256,int16[4])", + "function transferEscrow(uint256,address,address,uint256)", + "function updateERC1155ListingPriceAndQuantity(uint256,uint256,uint256)", + "function updateWhitelist(uint32,address[])", + "function useConsumables(uint256,uint256[],uint256[])", +]); + const GBM_MAPPED_WRITE_ABI = parseAbi([ - "function buyNow(uint256 _auctionID)", - "function cancelAuction(uint256 _auctionID)", - "function commitBid(uint256 _auctionID,uint256 _bidAmount,uint256 _highestBid,address _tokenContract,uint256 _tokenID,uint256 _amount,bytes _unused)", - "function swapAndCommitBid((address tokenIn,uint256 swapAmount,uint256 minGhstOut,uint256 swapDeadline,address recipient,uint256 auctionID,uint256 bidAmount,uint256 highestBid,address tokenContract,uint256 _tokenID,uint256 _amount,bytes _signature) ctx)", - "function createAuction((uint80 startTime,uint80 endTime,uint56 tokenAmount,uint8 category,bytes4 tokenKind,uint256 tokenID,uint96 buyItNowPrice,uint96 startingBid) _info,address _tokenContract,uint256 _auctionPresetID) returns (uint256)", + "function buyNow(uint256)", + "function cancelAuction(uint256)", + "function commitBid(uint256,uint256,uint256,address,uint256,uint256,bytes)", + "function createAuction((uint80,uint80,uint56,uint8,bytes4,uint256,uint96,uint96),address,uint256)", + "function swapAndBuyNow((address,uint256,uint256,uint256,address,uint256))", + "function swapAndCommitBid((address,uint256,uint256,uint256,address,uint256,uint256,uint256,address,uint256,uint256,bytes))", +]); + +const FORGE_MAPPED_WRITE_ABI = parseAbi([ + "function claimForgeQueueItems(uint256[])", + "function forgeWearables(uint256[],uint256[],uint40[])", + "function reduceQueueTime(uint256[],uint40[])", + "function smeltWearables(uint256[],uint256[])", +]); + +const GLTR_STAKING_MAPPED_WRITE_ABI = parseAbi(["function batchHarvest(uint256[])"]); +const WRAP_MAPPED_WRITE_ABI = parseAbi(["function enterWithUnderlying(uint256)", "function leaveToUnderlying(uint256)"]); +const GHST_STAKING_MAPPED_WRITE_ABI = parseAbi(["function withdrawFromPool(address,uint256)"]); +const SOCKET_VAULT_MAPPED_WRITE_ABI = parseAbi(["function bridge(address,uint256,uint256,address,bytes,bytes)"]); +const MERKLE_DISTRIBUTOR_MAPPED_WRITE_ABI = parseAbi(["function claim(uint256,bytes32[])"]); +const GOTCHI_POINTS_MAPPED_WRITE_ABI = parseAbi(["function convertAlchemica(address,uint256,uint256,uint256,uint256)"]); +const ERC20_MAPPED_WRITE_ABI = parseAbi(["function approve(address,uint256)"]); +const ERC1155_MAPPED_WRITE_ABI = parseAbi([ + "function safeTransferFrom(address,address,uint256,uint256,bytes)", + "function setApprovalForAll(address,bool)", ]); const MAPPED_WRITE_DEFAULTS: Record = { - "auction bid": { + "lending create": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "lending agree": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "token approve": { + abi: ERC20_MAPPED_WRITE_ABI, + source: "canonical.erc20", + }, + "gotchi xp claim-batch": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "baazaar listing batch-execute": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "realm harvest batch": { + address: BASE_GLTR_STAKING, + abi: GLTR_STAKING_MAPPED_WRITE_ABI, + source: "base.gltr-staking", + }, + "token bridge": { + abi: SOCKET_VAULT_MAPPED_WRITE_ABI, + source: "canonical.socket-vault", + }, + "baazaar buy-now": { address: BASE_GBM_DIAMOND, abi: GBM_MAPPED_WRITE_ABI, source: "base.gbm-diamond", @@ -32,16 +112,145 @@ const MAPPED_WRITE_DEFAULTS: Record = { abi: GBM_MAPPED_WRITE_ABI, source: "base.gbm-diamond", }, + "baazaar cancel-erc1155": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "baazaar cancel-erc721": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "lending cancel": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "forge claim": { + address: BASE_MERKLE_DISTRIBUTOR, + abi: MERKLE_DISTRIBUTOR_MAPPED_WRITE_ABI, + source: "base.merkle-distributor", + }, + "portal claim": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "lending claim-end": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "forge queue claim": { + address: BASE_FORGE_DIAMOND, + abi: FORGE_MAPPED_WRITE_ABI, + source: "base.forge-diamond", + }, + "auction bid": { + address: BASE_GBM_DIAMOND, + abi: GBM_MAPPED_WRITE_ABI, + source: "base.gbm-diamond", + }, + "gotchi-points convert-alchemica": { + abi: GOTCHI_POINTS_MAPPED_WRITE_ABI, + source: "canonical.gotchi-points", + }, "auction create": { address: BASE_GBM_DIAMOND, abi: GBM_MAPPED_WRITE_ABI, source: "base.gbm-diamond", }, + "lending whitelist create": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "staking unstake-destroy": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "staking enter-underlying": { + abi: WRAP_MAPPED_WRITE_ABI, + source: "canonical.wrap", + }, + "gotchi equip-delegated": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "forge craft": { + address: BASE_FORGE_DIAMOND, + abi: FORGE_MAPPED_WRITE_ABI, + source: "base.forge-diamond", + }, + "staking leave-underlying": { + abi: WRAP_MAPPED_WRITE_ABI, + source: "canonical.wrap", + }, + "portal open": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "forge speedup": { + address: BASE_FORGE_DIAMOND, + abi: FORGE_MAPPED_WRITE_ABI, + source: "base.forge-diamond", + }, + "inventory transfer": { + abi: ERC1155_MAPPED_WRITE_ABI, + source: "canonical.erc1155", + }, + "token set-approval-for-all": { + abi: ERC1155_MAPPED_WRITE_ABI, + source: "canonical.erc1155", + }, + "forge smelt": { + address: BASE_FORGE_DIAMOND, + abi: FORGE_MAPPED_WRITE_ABI, + source: "base.forge-diamond", + }, + "gotchi spend-skill-points": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "baazaar swap-buy-now": { + address: BASE_GBM_DIAMOND, + abi: GBM_MAPPED_WRITE_ABI, + source: "base.gbm-diamond", + }, "auction swap-bid": { address: BASE_GBM_DIAMOND, abi: GBM_MAPPED_WRITE_ABI, source: "base.gbm-diamond", }, + "lending transfer-escrow": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "baazaar update-erc1155": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "lending whitelist update": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "gotchi feed": { + address: BASE_AAVEGOTCHI_DIAMOND, + abi: AAVEGOTCHI_DIAMOND_MAPPED_WRITE_ABI, + source: "base.aavegotchi-diamond", + }, + "staking withdraw-pool": { + abi: GHST_STAKING_MAPPED_WRITE_ABI, + source: "canonical.ghst-staking", + }, }; export function getMappedWriteDefaults(commandPath: string[]): MappedWriteDefaults | undefined { diff --git a/src/commands/mapped-runtime.test.ts b/src/commands/mapped-runtime.test.ts index 637772e..7bd3af6 100644 --- a/src/commands/mapped-runtime.test.ts +++ b/src/commands/mapped-runtime.test.ts @@ -62,20 +62,24 @@ describe("mapped command execution defaults", () => { }); }); - it("keeps non-defaulted mapped commands requiring explicit metadata", async () => { + it("allows ABI-only defaults for commands that still need explicit address", async () => { runOnchainSendWithFunctionMock.mockResolvedValue({ ok: true }); - const result = await runMappedDomainCommand(createCtx(["baazaar", "buy-now"])); + const result = await runMappedDomainCommand(createCtx(["token", "approve"])); const call = runOnchainSendWithFunctionMock.mock.calls[0]; const defaultsArg = call?.[3] as { abi?: unknown; address?: unknown; source?: unknown }; expect(runOnchainSendWithFunctionMock).toHaveBeenCalledTimes(1); - expect(defaultsArg.abi).toBeUndefined(); + expect(defaultsArg.abi).toBeDefined(); expect(defaultsArg.address).toBeUndefined(); - expect(defaultsArg.source).toBeUndefined(); + expect(defaultsArg.source).toBe("canonical.erc20"); expect(result).toMatchObject({ - mappedMethod: "buyNow", - defaults: null, + mappedMethod: "approve", + defaults: { + source: "canonical.erc20", + address: null, + abi: "available", + }, result: { ok: true }, }); }); diff --git a/src/commands/mapped.test.ts b/src/commands/mapped.test.ts index ffd6d08..88b6563 100644 --- a/src/commands/mapped.test.ts +++ b/src/commands/mapped.test.ts @@ -1,6 +1,8 @@ import { describe, expect, it } from "vitest"; import { findMappedFunction, listMappedCommands, listMappedCommandsForRoot } from "./mapped"; +import { getMappedWriteDefaults } from "./mapped-defaults"; +import { BASE_AAVEGOTCHI_DIAMOND, BASE_FORGE_DIAMOND, BASE_GBM_DIAMOND, BASE_GLTR_STAKING } from "../subgraph/sources"; describe("mapped domain commands", () => { it("resolves known mapping", () => { @@ -22,4 +24,18 @@ describe("mapped domain commands", () => { expect(all).toContain("baazaar buy-now"); expect(all).toContain("token approve"); }); + + it("provides built-in ABI defaults for every mapped command", () => { + const all = listMappedCommands(); + const missing = all.filter((command) => !getMappedWriteDefaults(command.split(" "))?.abi); + expect(missing).toEqual([]); + }); + + it("pins canonical contract addresses for high-confidence commands", () => { + expect(getMappedWriteDefaults(["lending", "create"])?.address).toBe(BASE_AAVEGOTCHI_DIAMOND); + expect(getMappedWriteDefaults(["auction", "bid"])?.address).toBe(BASE_GBM_DIAMOND); + expect(getMappedWriteDefaults(["forge", "craft"])?.address).toBe(BASE_FORGE_DIAMOND); + expect(getMappedWriteDefaults(["realm", "harvest", "batch"])?.address).toBe(BASE_GLTR_STAKING); + expect(getMappedWriteDefaults(["token", "approve"])?.address).toBeUndefined(); + }); }); diff --git a/src/output.test.ts b/src/output.test.ts index c93953c..a720570 100644 --- a/src/output.test.ts +++ b/src/output.test.ts @@ -18,7 +18,16 @@ describe("help output", () => { expect(text).toContain("Mapped to onchain function:"); expect(text).toContain("buyNow"); expect(text).toContain("--args-json"); - expect(text).toContain("--abi-file --address <0x...> --args-json"); + expect(text).toContain("ag baazaar buy-now --profile --args-json"); + expect(text).toContain("address: 0x80320a0000c7a6a34086e2acad6915ff57ffda31"); + }); + + it("prints ABI defaults while still requiring --address for generic token approve", () => { + const text = buildHelpText(["token", "approve"]); + expect(text).toContain("approve(address,uint256)"); + expect(text).toContain("ag token approve --profile --address <0x...> --args-json"); + expect(text).toContain("source: canonical.erc20"); + expect(text).toContain("--abi-file (override built-in ABI)"); }); it("prints built-in mapped defaults for auction bid without --abi-file", () => { diff --git a/src/subgraph/sources.ts b/src/subgraph/sources.ts index 90b9a63..0c4b6e0 100644 --- a/src/subgraph/sources.ts +++ b/src/subgraph/sources.ts @@ -8,6 +8,9 @@ export const GBM_BASE_ENDPOINT = export const BASE_AAVEGOTCHI_DIAMOND = "0xa99c4b08201f2913db8d28e71d020c4298f29dbf" as const; export const BASE_GBM_DIAMOND = "0x80320a0000c7a6a34086e2acad6915ff57ffda31" as const; +export const BASE_FORGE_DIAMOND = "0x50af2d63b839aa32b4166fd1cb247129b715186c" as const; +export const BASE_GLTR_STAKING = "0xab449dca14413a6ae0bcea9ea210b57ace280d2c" as const; +export const BASE_MERKLE_DISTRIBUTOR = "0xf50326e1a6c6949cc390c4efe8ea538e29a4fa11" as const; const SOURCE_MAP: Record = { "core-base": {