# OnchainVerifiable 컨트랙트 개발하기

OnchainVerifier 컨트랙트는 이미 배포돼 있어요. 누구나 GIWA 에서 스마트컨트랙트를 작성할 때 바로 쓸 수 있어요.

## isVerified

```solidity
function isVerified(address addr, DojangAttesterId attesterId) external view returns (bool);
```

특정 지갑이 검증(Verified)됐는지 바로 확인할 수 있는 뷰 함수에요. DeFi 프로토콜, 지갑, 게이트키핑이 필요할 때 “검증된 사용자만 이용 가능” 같은 정책을 쉽게 붙일 수 있어요.

### 배포 된 OnchainVerifiable 컨트랙트

<table><thead><tr><th width="103.91796875">Network</th><th width="286.11328125">OnchainVerifier contract Address</th><th>Verfier ID</th></tr></thead><tbody><tr><td>Testnet</td><td><a href="https://sepolia-explorer.giwa.io/address/0xd5077b67dcb56caC8b270C7788FC3E6ee03F17B9?tab=contract">0xd5077b67dcb56caC8b270C7788FC3E6ee03F17B9</a></td><td>0xd99b42e778498aa3c9c1f6a012359130252780511687a35982e8e52735453034</td></tr></tbody></table>

## Example

### Verified ERC20

이 예제는 OpenZeppelin ERC20 표준을 그대로 사용하면서, 토큰 전송 시 OnchainVerifiable 컨트랙트의 isVerified 함수를 호출하여 전송을 허용/차단하는 방법을 보여줍니다.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

type DojangAttesterId is bytes32;

interface IVerifier {
    function isVerified(address primaryAddress, DojangAttesterId attesterId) external view returns (bool);
}

contract VerifiedERC20 is ERC20 {
    IVerifier public immutable verifier;
    DojangAttesterId public immutable attesterId;
    error NotVerified(address addr);

    constructor(
        string memory name_,
        string memory symbol_,
        address verifier_,
        DojangAttesterId attesterId_,
        uint256 initialSupply
    ) ERC20(name_, symbol_) {
        require(verifier_ != address(0), "verifier is zero");
        verifier = IVerifier(verifier_);
        attesterId = attesterId_;
        _mint(msg.sender, initialSupply);
    }

    // OZ v5 단일 훅: 모든 이동(mint/burn/transfer)이 이 경로를 탑니다.
    function _update(address from, address to, uint256 value) internal override {
        // 일반 transfer(= 양쪽 모두 비제로)에서만 검증
        if (from != address(0) && to != address(0)) {
            // 토큰 보유자(from)가 검증되어야 함
            if (!verifier.isVerified(from, attesterId)) revert NotVerified(from);

            // 필요 시 호출자(transferFrom의 spender)도 검증
            if (!verifier.isVerified(_msgSender(), attesterId)) revert NotVerified(_msgSender());

            // 필요 시 수신자(to)도 검증
            if (!verifier.isVerified(to, attesterId)) revert NotVerified(to);
        }

        super._update(from, to, value);
    }
}

```

### Verified ERC721

이 예제는 OpenZeppelin ERC721 표준을 그대로 사용하면서, NFT 전송 시 외부 검증 컨트랙트의 isVerified 함수를 호출하여 전송을 허용/차단하는 방법을 보여줍니다.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

type DojangAttesterId is bytes32;

interface IVerifier {
    function isVerified(address addr, DojangAttesterId attesterId) external view returns (bool);
}

contract VerifiedERC721 is ERC721, Ownable {
    IVerifier public immutable verifier;

    DojangAttesterId public constant UPBIT_KOREA =
        DojangAttesterId.wrap(0xd99b42e778498aa3c9c1f6a012359130252780511687a35982e8e52735453034);

    error NotVerified(address account);

    constructor(
        string memory name_,
        string memory symbol_,
        address verifier_
    ) ERC721(name_, symbol_) Ownable(msg.sender) {
        require(verifier_ != address(0), "verifier is zero");
        verifier = IVerifier(verifier_);
    }

    function mint(address to, uint256 tokenId) external onlyOwner {
        if (!verifier.isVerified(to, UPBIT_KOREA)) revert NotVerified(to);
        _mint(to, tokenId);
    }
 
    // OpenZeppelin v5 단일 훅: 민트/번/전송 모두 이 경로를 탑니다.
    function _update(address to, uint256 tokenId, address auth)
        internal
        override
        returns (address from)
    {
        // 상태 변경 전에 "기존 소유자" 파악 (민트면 address(0))
        address prevOwner = _ownerOf(tokenId);

        // 일반 전송에서만 검증 (민트/번 제외)
        if (prevOwner != address(0) && to != address(0)) {
            if (!verifier.isVerified(prevOwner, UPBIT_KOREA)) revert NotVerified(prevOwner);
            if (!verifier.isVerified(_msgSender(), UPBIT_KOREA)) revert NotVerified(_msgSender());
            if (!verifier.isVerified(to, UPBIT_KOREA)) revert NotVerified(to);
        }

        // 실제 상태 업데이트 (from 반환값은 이전 소유자)
        return super._update(to, tokenId, auth);
    }
}

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.giwa.io/get-started/smart-contract/onchainverifiable.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
