IAgent.getAgentIds
getAgentIds(
address wallet,
PageRequest pageRequest
) external view returns (uint256[] agentIds, PageResponse pageResponse) 에이전트 지갑을 기준으로 하는 페이지네이션 조회입니다. ERC-8004 에이전트 지갑이 wallet인 에이전트의 ID를 반환합니다. 에이전트 지갑은 register 시 등록자로 설정되고 setAgentWallet / unsetAgentWallet으로 바뀌며, 에이전트 NFT가 이전되면 해당 항목이 지워집니다. 따라서 어떤 주소가 소유한 에이전트 목록이 아닙니다. 소유 목록은 IdentityRegistry의 ERC-721 기능(balanceOf, Transfer 이벤트)으로 구합니다. IdentityRegistry 스토리지를 컨트랙트 내부에서 순회하는 비용이 너무 크기 때문에 Agent 프리컴파일이 이 메서드를 제공하며, 체인 측 인덱스에서 읽습니다. 등록, 에이전트 URI, 메타데이터, 에이전트 지갑, ERC-721 소유권은 0x8004000000000000000000000000000000000001의 ERC-8004 IdentityRegistry preinstall에 있으므로 직접 호출합니다. 레지스트리에는 attest나 revoke 함수가 없습니다.
파라미터
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
wallet | address | ✓ | 조회할 지갑 주소입니다. |
pageRequest | tuple | ✓ | 표준 페이지네이션 튜플로 (bytes key, uint64 offset, uint64 limit, bool countTotal, bool reverse) 형식입니다. key와 offset 중 하나만 지정합니다. 둘 다 지정하면 QueryFailed로 revert됩니다. ABI 디코딩되지 않는 튜플은 ABISetupFailed로 revert됩니다. |
반환값
tuple (uint256[] agentIds, PageResponse pageResponse)를 반환합니다. 마지막 페이지에 도달하면 pageResponse.nextKey가 비어 있습니다.
에러
| 코드 | 이름 | 설명 |
|---|---|---|
InvalidNumberOfArgs | InvalidNumberOfArgs | 공유 IPrecompile 오류 집합에 InvalidNumberOfArgs(uint256 expected, uint256 got)로 선언되어 있지만, getAgentIds는 인자 개수가 틀려도 이 오류로 revert되지 않습니다. calldata가 두 인자를 담기에 짧으면 ABISetupFailed로 revert되고, 두 인자 뒤에 붙은 바이트는 무시됩니다. |
InvalidAddress | InvalidAddress | 공유 IPrecompile 오류 집합에 InvalidAddress(string bad)로 선언되어 있지만, wallet 인자에서는 발생하지 않습니다. wallet 자리의 32바이트 값은 상위 바이트가 0이 아니어도 그대로 디코딩되며, 조회에는 하위 20바이트가 쓰입니다. |
InvalidPageRequest | InvalidPageRequest | 공유 IPrecompile 오류 집합에 InvalidPageRequest(string method, uint256 index, string value)로 선언되어 있지만, 잘못된 pageRequest는 이 오류를 반환하지 않습니다. ABI 디코딩되지 않는 튜플은 ABISetupFailed로, key와 offset을 모두 지정한 요청은 QueryFailed로 revert됩니다. |
QueryFailed | QueryFailed | 체인 측 인덱스 조회가 실패하거나 요청을 거절할 때 revert됩니다. 예를 들어 pageRequest에 key와 offset을 모두 지정하면 사유가 invalid request, either offset or key is expected, got both입니다. QueryFailed(string queryMethod, string reason) 형태로 인코딩됩니다. |
ABISetupFailed | ABISetupFailed | calldata를 프리컴파일 ABI로 디코딩할 수 없을 때 revert됩니다. 알 수 없는 4바이트 selector이면 사유가 no method with id: 0x…이고, 인자가 짧거나 ABI 디코딩되지 않으면 디코더의 메시지(예: abi: cannot marshal in to go type: length insufficient 32 require 64)가 사유가 됩니다. ABISetupFailed(string reason) 형태로 인코딩됩니다. 프리컴파일에 UnknownMethod도 선언되어 있지만, 알 수 없는 selector는 이 오류로 revert되지 않습니다. |
예제
지갑의 모든 agent ID 페이지네이션
nextKey가 비게 될 때까지 반복하여 전체 목록을 수집합니다.
import { createPublicClient, http } from "viem";
const AGENT_PRECOMPILE = "0x100000000000000000000000000000000000000A" as const;
const agentAbi = [{
type: "function", name: "getAgentIds", stateMutability: "view",
inputs: [
{ name: "wallet", type: "address" },
{ name: "pageRequest", type: "tuple", components: [
{ name: "key", type: "bytes" },
{ name: "offset", type: "uint64" },
{ name: "limit", type: "uint64" },
{ name: "countTotal", type: "bool" },
{ name: "reverse", type: "bool" },
]},
],
outputs: [
{ name: "agentIds", type: "uint256[]" },
{ name: "pageResponse", type: "tuple", components: [
{ name: "nextKey", type: "bytes" },
{ name: "total", type: "uint64" },
]},
],
}] as const;
const publicClient = createPublicClient({ transport: http("https://rpc-testnet.maroo.io") });
// TODO: replace with the AGENT WALLET (IdentityRegistry.getAgentWallet(agentId)) before production —
// not the owner: the index is keyed by agent wallet. For the agents an address owns,
// use the registry's ERC-721 balanceOf / Transfer events.
const wallet = "0x8F3ac2B1d9E74c05A6B18FE27Dc4913e5A0F7b62";
let key: `0x${string}` = "0x";
const allIds: bigint[] = [];
do {
const [ids, page] = await publicClient.readContract({
address: AGENT_PRECOMPILE,
abi: agentAbi,
functionName: "getAgentIds",
args: [wallet, { key, offset: 0n, limit: 100n, countTotal: false, reverse: false }],
});
allIds.push(...ids);
key = page.nextKey as `0x${string}`;
} while (key !== "0x"); 공유 IPrecompile 오류 디코드
잘못된 calldata는 ABISetupFailed로, 인덱스가 거절한 조회는 QueryFailed로 revert되므로 디코더에 두 오류를 모두 넣습니다. 나머지 항목은 공유 IPrecompile 시그니처이며, 예전의 별도 정의 InvalidArgsLength(method, got, want)는 사라졌습니다.
import { BaseError, ContractFunctionRevertedError, decodeErrorResult } from "viem";
const iPrecompileErrorsAbi = [
{ type: "error", name: "InvalidNumberOfArgs",
inputs: [{ name: "expected", type: "uint256" }, { name: "got", type: "uint256" }] },
{ type: "error", name: "InvalidAddress", inputs: [{ name: "bad", type: "string" }] },
{ type: "error", name: "InvalidPageRequest",
inputs: [
{ name: "method", type: "string" },
{ name: "index", type: "uint256" },
{ name: "value", type: "string" },
] },
{ type: "error", name: "QueryFailed",
inputs: [{ name: "queryMethod", type: "string" }, { name: "reason", type: "string" }] },
{ type: "error", name: "ABISetupFailed", inputs: [{ name: "reason", type: "string" }] },
] as const;
try {
await publicClient.readContract({
address: AGENT_PRECOMPILE,
abi: agentAbi,
functionName: "getAgentIds",
args: [wallet, { key: "0x", offset: 0n, limit: 100n, countTotal: false, reverse: false }],
});
} catch (err) {
// viem keeps the revert data on the ContractFunctionRevertedError in the cause chain, not on err.
const revert = err instanceof BaseError ? err.walk((e) => e instanceof ContractFunctionRevertedError) : null;
if (revert instanceof ContractFunctionRevertedError && revert.raw) {
const decoded = decodeErrorResult({ abi: iPrecompileErrorsAbi, data: revert.raw });
console.error("getAgentIds reverted:", decoded.errorName, decoded.args);
} else {
throw err;
}
}