IPrivacy.withdraw

withdraw(PrivacyWithdrawRequest request) external returns (bool success)

프라이버시 풀에서 값을 해지합니다. nullifier가 가리키는 노트를 소비하고 amount(aokrw 코인 문자열)를 recipient에게 공개 상태로 지급합니다. 프리컴파일은 nullifier가 root 아래의 미사용 노트에 대응하며 그 노트의 값이 인출 금액을 감당한다는 것을 증명하는 영지식 증명을 검증합니다. 이전 초안과 달리, withdraw는 은닉 풀로 되돌리는 잔돈을 생성하지 않습니다. newNoteCommitmentencryptedNote가 없어졌으며, 남는 값은 사전에 별도의 은닉 전송으로 처리해야 합니다.

파라미터

이름 타입 필수 설명
request.proof bytes nullifier, root, amount를 미사용 노트에 결속시키는 영지식 증명입니다. 증명 검증은 1,500,000의 고정 가스 비용이 추가됩니다.
request.root bytes 증명이 결속된 Merkle 루트 스냅샷입니다. 체인이 여전히 보관 중인 루트여야 합니다.
request.nullifier bytes 소비되는 노트의 nullifier입니다. 사용 완료 집합에 이미 존재해서는 안 됩니다.
request.amount string 인출 금액을 코인 문자열로 지정합니다(예: 1,000만 OKRW는 "10000000000000000000000000aokrw"). 프라이버시 네이티브 denom과 일치해야 합니다.
request.recipient address 해지된 자금을 받을 EOA 또는 스마트 계정 주소입니다. 0 주소는 허용되지 않습니다.
request.chainId string 페이로드가 결속된 체인 식별자입니다. 체인 자체의 id와 같아야 하며, 불일치 시 revert됩니다.
request.expiresAtUnix uint64 유닉스 초 단위 만료 시각입니다. 만료된 인출은 "withdraw payload has expired" 문자열 revert됩니다.

반환값

타입: bool

해지에 성공하면 true를 반환합니다. 실패는 false 반환이 아니라 평문 문자열 revert로 처리됩니다.

에러

코드 이름 설명
string-revert "withdraw payload has expired" expiresAtUnix가 블록 시각 이하이면 평문 문자열 revert됩니다.
string-revert "expiresAtUnix is required" expiresAtUnix가 0이면 평문 문자열 revert됩니다.
string-revert "privacy precompile only supports native denom %q, got %q" amount 코인 문자열이 체인의 프라이버시 네이티브 denom(마루에서는 aokrw)이 아닌 다른 denom을 사용하면 평문 문자열 revert됩니다.
string-revert "invalid zero address" recipient가 0 주소이면 평문 문자열 revert됩니다.

예제

수신자에게 1,000만 OKRW 해지

withdraw는 nullifier가 가리키는 노트를 전부 해지합니다. 노트 값이 amount보다 크다면 사전에 은닉 전송으로 분할해야 합니다. 이 호출은 더 이상 풀로 되돌리는 잔돈을 생성하지 않습니다.

import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";

const PRIVACY = "0x100000000000000000000000000000000000000b" as const;

const privacyAbi = [{
  type: "function",
  name: "withdraw",
  stateMutability: "nonpayable",
  inputs: [{
    name: "request",
    type: "tuple",
    components: [
      { name: "proof",         type: "bytes"   },
      { name: "root",          type: "bytes"   },
      { name: "nullifier",     type: "bytes"   },
      { name: "amount",        type: "string"  },
      { name: "recipient",     type: "address" },
      { name: "chainId",       type: "string"  },
      { name: "expiresAtUnix", type: "uint64"  },
    ],
  }],
  outputs: [{ type: "bool" }],
}] as const;

// TODO: replace with a real spender key before production.
const wallet = createWalletClient({
  account: privateKeyToAccount(process.env.SPENDER_KEY as `0x${string}`),
  transport: http("https://rpc-testnet.maroo.io"),
});

await wallet.writeContract({
  address: PRIVACY,
  abi: privacyAbi,
  functionName: "withdraw",
  args: [{
    proof:         "0x1b3e..." as `0x${string}`,
    root:          "0x0a91..." as `0x${string}`,
    nullifier:     "0xdead..." as `0x${string}`,
    amount:        "10000000000000000000000000aokrw", // 10,000,000 OKRW
    // TODO: replace with the real recipient before production.
    recipient:     "0x5aB7c1e40b8dA46f9c7e29D3fA614e97b8f0Ac21",
    chainId:       "maroo-testnet",
    expiresAtUnix: BigInt(Math.floor(Date.now() / 1000) + 300),
  }],
});

만료되거나 잘못된 페이로드 처리

다른 IPrivacy 메서드와 마찬가지로, 실패는 원문 문자열 revert로 도달합니다. 타입 지정 오류 이름이 아니라 정확한 사유 문자열로 분기합니다.

import { BaseError, ContractFunctionRevertedError } from "viem";

try {
  await wallet.writeContract({ /* …withdraw call from previous example… */ });
} catch (err) {
  if (err instanceof BaseError) {
    const revert = err.walk(e => e instanceof ContractFunctionRevertedError);
    if (revert instanceof ContractFunctionRevertedError) {
      console.error("privacy withdraw reverted:", revert.reason);
      // e.g. "withdraw payload has expired"
      //      "invalid zero address"
    }
  }
  throw err;
}
ESC
검색어를 입력하세요