IPrivacy.transfer
transfer(PrivacyTransferRequest request) external returns (bool success) 하나 이상의 셸디드 노트를 소비하고 Groth16 증명으로 새 노트를 생성합니다. msg.sender가 effective sender이며, PCL은 호출자를 기준으로 정책을 평가합니다. 실패는 IPrivacy에 선언된 타입 지정 커스텀 오류(요청 만료, nullifier/커밋먼트 충돌, Merkle 용량, 트랜잭션당 한도) 또는 IPrecompile에서 상속된 오류로 revert됩니다. PCL 정책 실패는 그 대신 IPcl의 PCL ReasonCode로 노출됩니다.
파라미터
| 이름 | 타입 | 필수 | 설명 |
|---|---|---|---|
request | PrivacyTransferRequest | ✓ | 셸디드 transfer 페이로드입니다. 증명, Merkle 루트, nullifier들, 새 커밋먼트들, 암호문들, 뷰 태그들, 공개 필드, expiresAtUnix를 포함합니다. expiresAtUnix는 필수이며 현재 블록 시간보다 커야 합니다. 0이거나 과거이면 PrivacyRequestExpired(expiresAtUnix)로 revert됩니다. |
반환값
타입:
bool 성공 시 true를 반환합니다. 실패는 false 반환이 아니라 타입 지정 커스텀 오류로 revert됩니다.
에러
| 코드 | 이름 | 설명 |
|---|---|---|
PrivacyNonPayable | PrivacyNonPayable | msg.value가 0이 아닐 때 revert됩니다. value를 받는 메서드는 deposit뿐입니다. 페이로드는 메서드 이름입니다. |
PrivacyRequestExpired | PrivacyRequestExpired | request.expiresAtUnix가 0이거나 이미 지났을 때 revert됩니다. 페이로드에는 unix 타임스탬프가 담깁니다. |
PrivacyNullifierAlreadySpent | PrivacyNullifierAlreadySpent | 요청의 nullifier가 이미 온체인에 기록되어 있을 때 revert됩니다(이중 지출 시도). |
PrivacyCommitmentAlreadyExists | PrivacyCommitmentAlreadyExists | 요청의 새 커밋먼트가 이미 Merkle 트리에 존재할 때 revert됩니다. |
PrivacyMerkleCapacityExceeded | PrivacyMerkleCapacityExceeded | 새 커밋먼트를 추가하면 셸디드 풀 Merkle 트리 용량을 초과할 때 revert됩니다. 페이로드는 출력 개수입니다. |
PrivacyTxLimitExceeded | PrivacyTxLimitExceeded | 현재 트랜잭션이 이미 너무 많은 stateful privacy 호출을 수행했을 때 revert됩니다(전역 한도). |
PrivacySenderTxLimitExceeded | PrivacySenderTxLimitExceeded | effective sender가 이 트랜잭션에서 이미 너무 많은 stateful privacy 호출을 수행했을 때 revert됩니다. 페이로드는 (sender, limit)입니다. |
InvalidNumberOfArgs | InvalidNumberOfArgs | IPrecompile에서 상속됩니다. ABI 인코딩된 호출이 정확히 하나의 인자를 담고 있지 않을 때 revert됩니다. |
UnknownMethod | UnknownMethod | IPrecompile에서 상속됩니다. ABI selector가 프리컴파일의 어떤 함수와도 일치하지 않을 때 revert됩니다. |
예제
직접 셸디드 transfer
PCL과 sender별 rate-limit 집계에서 msg.sender가 effective sender로 처리됩니다.
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { privacyAbi } from "@maroo-chain/contracts/abi/IPrivacy";
const PRIVACY = "0x100000000000000000000000000000000000000b" as const;
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.SENDER_KEY as `0x${string}`),
transport: http("https://rpc-testnet.maroo.io"),
});
// `request` is prepared off-chain; expiresAtUnix must be in the future.
await wallet.writeContract({
address: PRIVACY,
abi: privacyAbi,
functionName: "transfer",
args: [request],
}); 셸디드 풀 충돌 디코드
두 오류 모두 클라이언트의 노트 집합이 온체인 트리에 비해 오래되었다는 뜻입니다. 재시도 전에 갱신합니다.
import { decodeErrorResult } from "viem";
import { privacyAbi } from "@maroo-chain/contracts/abi/IPrivacy";
try {
await wallet.writeContract({
address: PRIVACY,
abi: privacyAbi,
functionName: "transfer",
args: [request],
});
} catch (err: any) {
if (!err?.data) throw err;
const decoded = decodeErrorResult({ abi: privacyAbi, data: err.data });
if (decoded.errorName === "PrivacyNullifierAlreadySpent") {
// The user tried to spend a note that is already gone — refresh their note
// set from the indexer and rebuild the proof.
console.warn("note already spent — reload note set");
} else if (decoded.errorName === "PrivacyMerkleCapacityExceeded") {
const [count] = decoded.args as [bigint];
console.warn(`shielded tree is full (${count} outputs requested)`);
} else {
throw err;
}
}