# 소개

Kaia Name Service를 소개합니다.

Kaia Name Service(KNS)는 [Ethereum Name Service](https://ens.domains)로부터 영감을 받은 Kaia 블록체인 위에서 탈중앙화된 방식으로 동작하는 네임 서비스입니다.

KNS의 기능은 'kns.kaia'와 같이 사람이 읽을 수 있는 도메인을 Kaia 주소 및 그와 관련된 메타데이터에 매핑하는 것입니다. 또 대표 도메인 기능을 통해 Kaia 주소를 도메인으로 매핑할 수도 있습니다.

KNS는 KIP-17 표준의 NFT이며 [Opensea.io](https://opensea.io/)에서 2차 거래가 가능합니다.

KNS는 Web3의 모든 유저가 온체인 아이덴티티를 갖고 있는 미래를 지향합니다. Klip, Kaia 지갑을 소유한 사용자는 3글자 이상의 다른 사용자가 소유하고 있지 않은 어떤 문자열의 도메인이든 신규로 등록할 수 있습니다.


# .kaia 도메인

'.kaia' 최상위 도메인은 스마트 컨트랙트에 의해 소유되며 'kns.kaia'같은 하위 도메인은 해당 컨트랙트와 상호작용하여 누구나 허가 없이 등록할 수 있습니다.

도메인은 1년 단위로 등록이 가능하며 도메인의 글자 수에 따른 가격 정책을 가지고 있습니다. [비용 페이지](/domain/price)에서 정확한 가격을 확인하실 수 있습니다.

등록 가능한 도메인은 3글자 이상 255글자 이하의 알파벳 소문자 또는 숫자로 구성된 문자열로 제한됩니다. IDN homograph attack (유사한 형태의 다른 글자로 이루어진 문자열로 사용자를 혼동시키는 공격)을 방지하기 위해 사용 가능한 문자를 제한하였습니다.


# 비용

등록 비용은 1년을 기준으로 도메인의 글자 수에 따라 책정되어 있습니다.

|        |         |
| ------ | ------- |
| 글자수    | 가격 (1년) |
| 3글자    | 500 USD |
| 4글자    | 150 USD |
| 5글자    | 30 USD  |
| 6글자    | 20 USD  |
| 7글자 이상 | 10 USD  |


# 프로필

KNS 도메인은 Kaia 주소를 포함한 다양한 프로필 정보를 담을 수 있습니다.

### Kaia 주소

Kaia 주소를 등록하여 해당 주소를 대체하여 KNS 도메인을 사용할 수 있도록 합니다. 앞으로 점점 더 많은 Kaia 서비스에서 KNS를 지원하게 될 것입니다.

### 소개 및 아바타

나의 아이덴티티를 드러내는 짧은 소개 문구와 아바타(프로필 사진)를 등록할 수 있습니다. 아바타를 등록하면 도메인 이미지의 배경이 등록한 이미지로 변경됩니다.

### 소셜

이메일, 디스코드, 트위터, 카카오톡 ID 등 다른 사람에게 공유할 소셜 정보를 등록할 수 있습니다.


# KNS 토큰

KNS 프로토콜의 거버넌스 토큰 $KNS를 소개합니다.

![](/files/EqoAnxsIF9jQ3Suru2hw)

## 토큰 정보

* 토큰 종류 : KIP-7
* 토큰 이름 : Klaytn Name Service Token
* 티커 : KNS
* 최대 발행량 : 10,000,000,000
* 컨트랙트 주소 : 0x949fc808138081ab1fcbcbb5f311440cf2c3ff73

## 토큰 설명

KNS 토큰은 KNS 프로토콜의 거버넌스 토큰입니다.

KNS 메인 페이지 내에서 다음과 같은 DeFi 서비스를 제공합니다.

1. KAIA와 KNS 간의 스왑
2. KAIA-KNS 풀의 유동성 공급
3. KNS를 sKNS로 스테이킹

유동성 공급과 스테이킹을 통해 KNS 홀더들은 지속적인 보상을 받습니다.

KNS 토큰을 보유하고 있다면 누구나 거버넌스 토큰을 사용하여 안건을 만들고 투표를 할 수 있습니다.


# 토크노믹스

KNS 토큰의 자세한 정보를 알려드립니다.

## 토큰 분배

![](/files/Ckhy1bdzyr1vtppkT4FK)

* **Community Rewards (50%)**
  * 총 수량 5,000,000,000 KNS (50억개)
    * KAIA-KNS LP staking reward 3,000,000,000 KNS (30억개)
    * KNS governance staking reward 2,000,000,000 KNS (20억개)
  * 유동성 공급 보상, 스테이킹 보상 등으로 총 48개월에 걸쳐 분배됩니다.
* **Treasury (15.75%)**
  * 총 수량 1,575,000,000 KNS (15억 7500만개)
  * 거버넌스 출시 이후 DAO에 의해 운영되는 자금입니다.
* **Initial LP (0.75%)**
  * 총 수량 75,000,000 KNS (7500만개)
  * 최초 유동성 풀(KAIA-KNS) 형성시 사용됩니다.
* **Marketing & Partnership (5%)**
  * 총 수량 500,000,000 KNS (5억개)
  * 향후 마케팅 또는 타 프로젝트와의 협업에 사용됩니다.
* **Airdrop (0.5%)**
  * 총 수량 50,000,000 KNS (5000만개)
  * 2022년 3월 28일부터 2022년 4월10일까지 진행한 도메인 경매에서 도메인을 낙찰받은 분들에게 에어드랍됩니다.
* **Team (15%)**
  * 총 수량 1,500,000,000 KNS (15억개)
  * KNS 개발 팀에게 분배됩니다.
  * 6개월 락업 이후, 30개월에 걸쳐 선형 분배됩니다. (도합 36개월)
* **Investors (13%)**
  * 총 수량 1,300,000,000 KNS (13억개)
  * KNS 기관투자자들에게 분배됩니다.
  * 6개월 락업 이후, 30개월에 걸쳐 선형 분배됩니다. (도합 36개월)

## 수수료 분배

도메인 등록으로 지불된 KAIA의 10%는 KNS 팀에게, 40%는 Treasury로, 50%는 GradualDistributor로 분배됩니다.

![KNS 캐쉬 플로우 (Kaia 변경 이전 이미지입니다)](/files/fZK3NjY31hY2a5Kug7OJ)

구체적인 과정은 다음과 같습니다.

1. 도메인 등록이 발생하고 KaiaRegistrarController에 들어온 KAIA는 PrimaryDistributor로 이동합니다.
2. PrimaryDistributor는 받은 KAIA의 10%를 팀 지갑으로, 40%를 Treasury로, 50%를 GradualDistributor로 분배합니다.
3. GradualDistributor는 분배받은 KAIA를 도메인 등록 기간에 걸쳐 서서히 SecondaryDistributor로 보냅니다.
4. SecondaryDistributor는 받은 KAIA의 50%로 KNS를 매수하여 sKNS 홀더들에게 분배하고, 나머지 50%는 절반의 KAIA로 KNS를 매수하여 KAIA-KNS 풀에 유동성을 공급합니다. 해당 LP 토큰은 Treasury에 보관되어 POL(Protocol Owned Liquidity)가 됩니다.


# sKNS 토큰

![](/files/d0PjrxrOSHLTCa4CaH66)

## 토큰 정보

* 토큰 종류 : KIP-7
* 토큰 이름 : Staked Klaytn Name Service Token
* 티커 : sKNS
* 최대 발행량 : N/A
* 컨트랙트 주소 : 0x08da4d66604154e1c43689b8b25aeed7d0343617

## 토큰 설명

sKNS 토큰은 KNS 토큰을 거버넌스 스테이킹한 징표 토큰입니다.

KNS/sKNS 교환 비율은 1에서부터 시작하여 KNS 보상이 지급 됨에 따라 점점 증가합니다. 따라서 별도의 클레임 없이 향후 언스테이킹을 통해 총 KNS 개수가 늘어나개 됩니다.


# 도메인

## **도메인 검색**

![](/files/ZojiAkHw6GICAl4BPr6q)

도메인 페이지로 들어가면 검색창과 최근 등록된 도메인 또는 만료 예정인 도메인 목록을 볼 수 있습니다.

![](/files/d2ouA3E7z2iZuoY1zwAv)

검색창을 사용하여 도메인을 검색할 수 있습니다

## **도메인 상세정보**

![](/files/B8qq3yeRFDBqIawuYA8i)

특정 도메인을 클릭하게 되면 해당 도메인의 \[상세정보]탭에서 상세정보를 확인할 수 있습니다. 상세정보는 소유자, 만료일, 프로필을 포함합니다.

![](/files/7stTHJrP5sz6CF1pPfS2)

도메인의 소유자라면 \[수정하기]를 통해 프로필 정보를 수정할 수 있습니다.

## **도메인 등록하기**

소유자가 없는 도메인 또는 만료된 도메인은 등록이 가능합니다.

![](/files/r3cLhiPXVRWLVt4eLIQ4)

\[등록하기]탭에서 1년 단위로 등록할 수 있습니다.

![](/files/C0bX7qZYsV6zTgM6vfmZ)

![](/files/McJ6qYkMvD6TtqKwhJ9g)

이때 프론트러닝을 방지하기 위해 두 차례 트랜잭션을 보내게 됩니다.


# 내 프로필

![](/files/wCNFgSobgK4EGIJ0eljC)

## **대표 도메인 설정**

보유한 도메인 중 대표 도메인을 설정하여 지갑 주소가 어떤 도메인으로 보일지 결정할 수 있습니다. 보유한 도메인 리스트 중에서 대표 도메인으로 설정할 도메인을 선택 후 \[설정]합니다.

## **내 도메인**

내가 보유한 도메인 목록을 확인할 수 있습니다. 도메인과 함께 도메인 등록비용과 만료일을 함께 볼 수 있습니다.

## **갱신하기**

![](/files/7CN67wHy6mCFKcrHcOlU)

도메인을 선택하여 선택한 도메인들을 동시에 갱신할 수 있습니다.


# 스왑

![](/files/YK5KNo1UFZtA6R7MNX0s)

## **스왑하기**

KAIA와 KNS 간의 스왑을 할 수 있습니다.

화살표 버튼으로 스왑 방향을 변경합니다.

최대 슬리피지를 선택하고 그에 따른 최소 수령량을 확인할 수 있습니다.

\[스왑하기]를 통해 트랜잭션을 보냅니다.


# LP 풀

KAIA-KNS 스왑 풀에 유동성을 공급합니다. APR(현 시점의 추정 연 이율)과 \[내 스테이킹된 LP]가 표시됩니다. 유동성 공급보상은 \[클레임 가능한 보상]에서 확인할 수 있고 \[클레임]을 통해 수령할 수 있습니다.

## **스테이킹**

![](/files/aiuK5DmidvX6vvFaz4VJ)

동일한 가치의 KAIA와 KNS를 사용하여 KAIA-KNS 풀에 유동성을 공급합니다.

1. KAIA와 KNS를 풀에 넣어 LP 토큰 수령
2. 수령한 LP토큰을 스테이킹 위 두 단계를 완료하여 유동성 공급 보상을 받을 수 있습니다.

## **언스테이킹**

![](/files/8T9Rvn0FXQmt2R9XxRX0)

스테이킹된 LP를 언스테이킹하고, LP 토큰을 사용하여 KAIA와 KNS를 풀로부터 회수할 수 있습니다.


# 거버넌스

거버넌스 페이지에서는 에어드랍 수령, KNS 스테이킹을 할 수 있습니다. KNS 런치 이전에 진행되었던 도메인 경매에서 낙찰받은 경우 에어드랍을 수령할 수 있습니다. KNS를 스테이킹하여 스테이킹 보상과 거버넌스 투표권을 얻을 수 있고 언스테이킹을 통해 그 동안 받은 스테이킹 보상을 수령할 수 있습니다. sKNS 스테이킹 수익률, KNS 가격, KNS/sKNS 교환 비율, 보유한 KNS와 sKNS 등을 확인할 수 있습니다.

## **에어드랍**

![](/files/Bsajsqx7edAINN62kaRo)

에어드랍 수량을 확인하고 \[클레임]을 통해 에어드랍을 수령합니다.

## **스테이킹 관리**

![](/files/id0vgAkHdX4tSwaLepPT)

![](/files/caw5JLmh5dYm4sKAOX8O)

\[스테이킹 관리]에서 스테이킹과 언스테킹을 할 수 있습니다.

KNS를 스테이킹하여 보상을 받을 수 있습니다. 스테이킹 수익률을 확인할 수 있고 \[스테이킹 관리]에서 스테이킹 중인 KNS를 확인하고 KNS를 스테이킹 또는 언스테이킹할 수 있습니다.

![](/files/MhZhb2OG4ijXVp9CQeze)

보상은 7일의 언스테이킹 기간 이후에 \[KNS 출금하기]를 통해 출금할 수 있습니다. 현재 언스테이킹 중인 KNS의 정보는 \[언스테이킹 로그]에서 확인하실 수 있습니다.

## **거버넌스**

거버넌스 투표는 탈중앙화 거버넌스 플랫폼 “Snapshot”을 통해 진행됩니다.

<https://snapshot.org/#/knsdao.eth> 에서 제안과 투표를 할 수 있습니다.


# 핵심 파트너

## [Krust](https://krustuniverse.com/)

![](/files/Lrvg62P8bxgm3N0w5d3a)

## [Hyperithm](https://hyperithm.com/)

![](/files/pakryLYQr8ZsVKMFBo5I)


# Defi

<table><thead><tr><th width="366">이름</th><th>로고</th></tr></thead><tbody><tr><td><a href="https://kokoa.finance/">KokoaFinance</a></td><td><img src="/files/zL9SPWCnB4ClY6odaS8k" alt=""></td></tr><tr><td><a href="https://kokonutswap.finance/">KokonutSwap</a></td><td><img src="/files/t6enHIugSbedRFoQARFD" alt=""></td></tr><tr><td><a href="https://eklipse.finance/">Eklipse</a></td><td><img src="/files/pBaw0mXbfL8LOWKez5K1" alt=""></td></tr><tr><td><a href="https://claimswap.org/">Claimswap</a></td><td><img src="/files/mnFUeJFPA5HkQqBFXnsd" alt=""></td></tr><tr><td><a href="https://swapscanner.io/ko">Swapscanner</a></td><td><img src="/files/9T9umuglJrkwKiIXeIi2" alt=""></td></tr></tbody></table>


# Game & NFT

| 이름                                                     | 로고                               |
| ------------------------------------------------------ | -------------------------------- |
| [Wemix](https://wemixnetwork.com/)                     | ![](/files/giCL6c1eJg3MQUX4JalC) |
| [Klaycity](https://klaycity.com/)                      | ![](/files/nmTN0WmtqYWiE9LezOn9) |
| [Klay Kingdoms](https://klaykingdoms.com/)             | ![](/files/bLcVwSlj9JyrPsT8QF5G) |
| [Sheepfarm](https://sheepfarm.io/)                     | ![](/files/Snr3mvUbe0yNjr6QmBjD) |
| [MetaToyDragonZ](https://metatoydragonz.io/)           | ![](/files/CF5hDRL7rlZ5xbTv1fZZ) |
| [Hero Blaze: Three Kingdoms](https://heroblaze3kd.io/) | ![](/files/wUIMF6KJYKj3X6vJlTnl) |
| [Hypesalt](https://hypesalt.com/)                      | ![](/files/UFhJy6PLJJ7dlIxKeRr6) |
| [Pado](https://trippy.place/)                          | ![](/files/jGjnauQ25u2MWQz8hJfG) |
| [Drawshop Kingdom Reverse](https://drawshop.io/)       | ![](/files/vPXHcqP5y59rbpSViHQH) |


# KNS 인테그레이션

KNS의 Resolution을 직접 적용하기 위한 가이드입니다.

## 기본 구조

바탕이 되는 큰 구조는 Ethereum Name Service를 참고하였습니다.

1. Registry는 각 도메인의 owner와 resolver에 대한 정보를 가지고 있습니다.
2. Resolver는 각 도메인에 대응되는 주소와 부가 정보들(이메일 주소, 아바타 등)을 담고 있습니다.

![](/files/4tFTfkNrpjhoSStjLCgI)

Registry 상의 owner는 도메인 NFT를 소유하고 있는 주소이며, 실제로 해당 도메인을 resolve한 결과는 resolver에 addr를 호출해야만 알아낼 수 있습니다. (위 그림 참조) 즉, Registry 상의 owner는 도메인을 resolve한 결과와 **다를 수 있습니다.**

등록된 도메인의 resolver는 PublicResolver인 것이 기본 상태입니다. PublicResolver는 addr 메소드를 가지고 있어, 적절한 주소로 resolve가 가능합니다. KNS 프론트엔드를 사용하면 resolver의 주소가 언제나 PublicResolver의 주소로 유지됩니다.

## Forward Resolution (도메인 **→** 주소)

[ethers](https://github.com/ethers-io/ethers.js)와 [eth-ens-namehash](https://github.com/danfinlay/eth-ens-namehash) 라이브러리를 활용한 forward resolution 구현입니다.

```typescript
const namehash = require("eth-ens-namehash");
const ethers = require("ethers");

/********************************************
    Declare provider and ABI to use here
********************************************/

async function forwardResolve(domain) {
    const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000";
    let registry = new ethers.Contract(
        REGISTRY_ADDRESS,
        REGISTRY_ABI,
        provider
    );
    let node = namehash.hash(domain);
    let resolverAddress = await registry.resolver(node);
    if (parseInt(resolverAddress, 16) === 0) {
        return EMPTY_ADDRESS;
    }
    let resolver = new ethers.Contract(
        resolverAddress, 
        PUBLIC_RESOLVER_ABI,
        provider
    );
    try {
        let address = await resolver.addr(node);
        return address;
    } catch(e) {
        console.error(e);
        return EMPTY_ADDRESS;
    }
}

forwardResolve("foo.klay").then(console.log);
```

컨트랙트 상에서 도메인은 raw string으로 관리되지 않고, bytes32 형의 [namehash ](https://docs.ens.domains/contract-api-reference/name-processing#hashing-names)값으로 관리됩니다. eth-ens-namehash 라이브러리는 이 namehash 값을 계산하는 함수를 제공합니다. 물론, 위 링크를 참고하여 직접 구현하셔도 무방합니다.

계산된 namehash값을 사용해 우선 Registry 컨트랙트에 도메인의 resolver address를 쿼리합니다. 얻은 resolver address가 0인 경우는 등록되지 않은 도메인이거나 resolver를 0 주소로 세팅한 경우이므로 forward resolution이 불가능하고 이 경우 빈 주소를 반환합니다.

그 외의 경우에는 반환된 resolver address에 addr 메소드를 호출해 주소를 얻어내면 됩니다. addr 메소드를 구현하지 않은 resolver로 설정될 가능성이 있으므로 위와 비슷한 형태의 예외 처리를 해 주시는 것을 권장드립니다.

0 주소가 반환되는 경우에는 반환값을 무시하도록 구현해야 합니다.

```typescript
const REGISTRY_ABI = ["function resolver(bytes32) view returns (address)"]
const PUBLIC_RESOLVER_ABI = ["function addr(bytes32) view returns (address)"]
```

Forward resolution을 위해 필요한 최소한의 ABI는 위와 같습니다.

```typescript
const REGISTRY_ADDRESS = "0x0892ed3424851d2Bab4aC1091fA93C9851Eb5d7D"
const PUBLIC_RESOLVER_ADDRESS = "0xe2AE210c9b8601E00edE4aE5b9B23a80dCD12e3C"
```

또한, 현재 Cypress에 deploy된 Registry와 Public Resolver의 주소는 위와 같습니다. (Public Resolver의 주소는 아래에서 사용됩니다.)

### 이벤트

주소가 바뀌는 것을 트래킹하기 위해서 PublicResolver의 주소에서 다음 이벤트를 구독하면 됩니다. 물론, PublicResolver가 아닌 custom resolver가 설정된 경우에는 트래킹이 불가능합니다.

> **event AddrChanged(bytes32 indexed node, address addr);**
>
> \_node\_는 도메인의 namehash 값이며, 그 도메인에 해당되는 주소가 \_addr\_로 바뀌었다는 의미입니다.

Registry 상에서 도메인의 owner나 resolver가 바뀐 경우에도 이전에 맵핑된 주소를 invalidate하고 새롭게 바뀐 주소를 쿼리해야 합니다. 이를 위해서는 Registry 주소에서 다음 이벤트들을 구독하면 됩니다.

> **event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);**
>
> \_tokenId\_는 도메인의 namehash 값을 bytes32에서 uint256으로 단순히 캐스팅한 값이며, 해당 \_tokenId\_의 소유자가 \_from\_에서 \_to\_로 바뀌었다는 의미입니다. Registry는 IKIP17을 구현하고 있으며, 이는 IKIP17의 Transfer 이벤트와 동일합니다.

> **event NewResolver(bytes32 indexed node, address resolver);**
>
> \_node\_는 도메인의 namehash 값이며, 해당 도메인의 resolver 주소가 \_resolver\_로 바뀌었다는 의미입니다.

종합하여, 이벤트를 구독해 캐시를 관리하는 버전의 forward resolution을 의사코드로 나타내면 다음과 같습니다.

```
// Forward resolution with caching
func forwardResolve(domain):
	namehash <- getNamehash(domain)
	if (namehash in cache):
		return cache[namehash]
	else:
		resolver <- getResolver(namehash)
		result <- resolver.addr(namehash)
		if (resolver == PUBLIC_RESOLVER):
			cache[namehash] <- result
		return result

// Daemon for listening events
func daemon():
	while (true):
		event <- listenEventFrom([REGISTRY, PUBLIC_RESOLVER])
		if (event.type == "AddrChanged"): // from PUBLIC_RESOLVER
			cache[event.node] <- event.addr
		elif (event.type == "Transfer"): // from REGISTRY
			invalidate cache[event.tokenId]
		elif (event.type == "NewResolver"): // from REGISTRY
			invalidate cache[event.node]
```

## **Reverse resolution (주소 → 도메인)**

{% hint style="info" %}
Reverse resolution의 작동 방식은 ENS와 유사하기 때문에 [ENS reverse resolution document](https://docs.ens.domains/dapp-developer-guide/resolving-names#reverse-resolution)를 참고하시면 이해에 도움이 될 수 있습니다.
{% endhint %}

Reverse resolution이란 하나의 주소에 매핑된 여러 개의 도메인 중 하나를, 그 주소의 “대표 도메인”으로 지정하는 기능입니다. 이를 위해 “addr.reverse”라는 특별한 도메인을 사용합니다. “addr.reverse” 도메인의 Registry 상 owner는 ReverseRegistrar라는 컨트랙트이며, 사용자들은 이 컨트랙트를 통해 서브도메인 “(주소).addr.reverse”의 소유권을 획득할 수 있습니다. “(주소).addr.reverse” 도메인의 resolver에 name(bytes32 namehash) 함수를 호출한 결과가 이 주소의 대표 도메인이 됩니다.

예를 들어, “0x314159265dd8dbb310642f98f50c066173c1259b” 주소는 ReverseRegistrar 컨트랙트를 통해 “314159265dd8dbb310642f98f50c066173c1259b.addr.reverse” 도메인의 소유권을 claim할 수 있으며, 해당 도메인의 resolver에 name(“0x9f3f…17e6”) 값을 “hello.klay”로 설정함으로써 “0x3141...259b”의 대표 도메인을 “hello.klay”로 설정할 수 있습니다. (“0x9f3f…17e6”는 “3141…259b.addr.reverse”의 namehash 값)

“(주소).addr.reverse” 도메인의 resolver는 일반적인 도메인과 다르게 기본적으로 DefaultReverseResolver로 설정되며, DefaultReverseResolver는 name(bytes32)를 지원합니다.

대표 도메인을 설정할 때에 실제로 해당 주소로 resolve되지 않는 도메인이 설정되거나, 대표 도메인으로 설정했던 도메인의 소유권이 변경되는 경우가 발생할 수 있으므로 reverse resolution을 실제로 구현할 때에는 몇 가지를 추가로 확인해 줘야 합니다. 구체적으로는 다음 과정을 거쳐야 합니다.

1. Registry에 “(주소).addr.reverse”의 resolver 주소를 쿼리합니다. (이 때 주소의 알파벳(a-f)는 모두 **소문자로** 입력해야 하며, 0x prefix는 **입력하지 않아야** 함에 유의.) 쿼리 결과가 0 주소를 반환한 경우 실패. (종료)
2. 1번의 결과로 나온 resolver에 name(bytes32 namehash)를 호출합니다. namehash는 “(주소).addr.reverse”의 namehash값입니다. 쿼리 결과가 빈 문자열을 반환한 경우 실패. (종료)
3. 2번의 결과로 나온 도메인의 namehash 값을 사용해 Registry에 resolver 주소를 쿼리합니다. 쿼리 결과가 0 주소를 반환한 경우 실패. (종료)
4. 3번의 결과로 나온 resolver 주소에 addr(bytes32 namehash)를 호출합니다. namehash는 2번의 결과로 나온 도메인의 namehash 값입니다. 쿼리 결과로 반환된 주소 값이 처음 입력된 주소 값과 다르면 실패. (종료)
5. 1\~4 과정을 실패 없이 모두 통과했다면 입력된 주소의 대표 도메인은 2번의 결과로 나온 도메인이 됩니다. 만약 중간에 실패했다면 대표 도메인이 설정되지 않았거나 잘못 설정된 경우입니다.

위에서 설명한 예시를 다시 살펴보면, 1번 과정에서는 Registry에 resolver(“0x9f3f…17e6”)를 호출해 “3141…259b.addr.reverse”에 해당되는 resolver의 주소를 얻을 것이며 (default는 DefaultReverseResolver 주소), 2번 과정에서는 해당 resolver 주소에 name(“0x9f3f…17e6”)를 호출해 “hello.klay”를 반환받고, 3번 과정에서는 Registry에 “hello.klay”의 namehash 값인 “0x6f37…b595”를 사용해 resolver(“0x6f37…b595”)를 호출해 “hello.klay”의 resolver 주소를 얻고 (default는 PublicResolver 주소), 4번 과정에서는 해당 resolver 주소에 addr(“0x6f37…b595”)를 호출해 실제로 “0x3141…259b” 주소로 resolve된다면 성공, 그렇지 않다면 실패합니다.

**위 모든 과정은 ReverseRecords 컨트랙트의 getName(address) 함수에 구현되어 있습니다.** 따라서 가장 간단하게는 다음과 같이 reverse resolution을 구현할 수 있습니다.

```typescript
import "ethers";

async function reverseResolve(address: any) {
  let reverseRecords = new ethers.Contract(
    REVERSE_RECORDS_ADDRESS,
    REVERSE_RECORDS_ABI,
    provider
  );
  let name = await reverseRecords.getName(address);
  return name;
}

reverseResolve("0x314159265dd8dbb310642f98f50c066173c1259b").then(console.log);
```

Reverse resolution을 쿼리하는 데에 필요한 최소한의 ABI는 다음과 같습니다.

```typescript
const REVERSE_RECORDS_ADDRESS = "0x87f4483E4157a6592dd1d1546f145B5EE22c790a"
const REVERSE_RECORDS_ABI = ["function getName(address) view returns (string)"]
```

### 이벤트

Reverse resolution에 대해 캐싱을 수행하기 위해서는 event를 구독해야 합니다. 기본적으로 위에서 설명한 1\~4의 과정 중 하나라도 결과가 바뀐 경우에는 새롭게 쿼리를 해서 결과를 얻어야 합니다. 1\~4 과정 중 3\~4 과정은 2번 과정의 결과로 나온 도메인에 대해 forward resolution을 수행해 입력된 주소와 같은지를 확인하는 과정이므로 [Forward Resolution](https://github.com/klaytn-name-service/kns-gitbook/blob/main/dev/sdk.md#forward-resolution)에서 설명한 event들을 구독하는 것이 기본적으로 필요합니다.

추가로 1\~2 과정에서 DefaultReverseResolver에 설정된 name(대표 도메인)이 바뀌는 것을 확인하기 위해 다음 이벤트를 구독해야 합니다.

> **event NameChanged(bytes32 indexed node, string name);**
>
> \_node\_는 “(주소).addr.reverse”의 namehash 값이며, 그 주소의 이름이 \_name\_으로 바뀌었다는 뜻입니다.


# Reverse Resolution 예제 코드

## Constants

```javascript
const JSON_RPC_PROVIDER = "" // JSON-RPC provider url
const REVERSE_RECORDS_ADDRESS = "0x87f4483E4157a6592dd1d1546f145B5EE22c790a";
const REVERSE_RECORDS_ABI = [
  {
    type: "function",
    name: "getName",
    stateMutability: "view",
    inputs: [
      {
        internalType: "address",
        name: "addr",
        type: "address",
      },
    ],
    outputs: [
      {
        internalType: "string",
        name: "",
        type: "string",
      },
    ],
  },
];
```

## ethers.js

```javascript
import ethers from "ethers";

async function domainOrAddressEthers(address) {
  const provider = new ethers.providers.JsonRpcProvider(JSON_RPC_PROVIDER);
  const reverseRecords = new ethers.Contract(REVERSE_RECORDS_ADDRESS, REVERSE_RECORDS_ABI, provider);
  const domain = await reverseRecords.getName(address);
  if (domain === "") {
    return address;
  } else {
    return domain;
  }
}
```

## web3.js

```javascript
import Web3 from "web3";

async function domainOrAddressWeb3(address) {
  const web3 = new Web3(JSON_RPC_PROVIDER);
  const reverseRecords = new web3.eth.Contract(REVERSE_RECORDS_ABI, REVERSE_RECORDS_ADDRESS);
  const domain = await reverseRecords.methods.getName(address).call();
  if (domain === "") {
    return address;
  } else {
    return domain;
  }
}
```

## caver-js

```javascript
import Caver from "caver-js";

async function domainOrAddressCaver(address) {
  const caver = new Caver(JSON_RPC_PROVIDER);
  const reverseRecords = new caver.klay.Contract(REVERSE_RECORDS_ABI, REVERSE_RECORDS_ADDRESS);
  const domain = await reverseRecords.methods.getName(address).call();
  if (domain === "") {
    return address;
  } else {
    return domain;
  }
}
```

## 테스트용 주소 및 도메인

Reverse resolution 성공 시에는 아래의 주소를 받았을 때 올바르게 도메인을 반환해야 합니다.

* 주소: `0x0000ac03932ff48ee30209774e3f10fb0ac522e9`
* 도메인: `kns.klay`


# 컨트랙트 주소

KNS에 사용되는 스마트 컨트랙트 주소들입니다.

### Domain Registry

| 이름          | 주소                                         |
| ----------- | ------------------------------------------ |
| KNSRegistry | 0x0892ed3424851d2Bab4aC1091fA93C9851Eb5d7D |

### .klay

| 이름                      | 주소                                         |
| ----------------------- | ------------------------------------------ |
| BaseRegistrar           | 0x0345b51bD06b0993A6b56890308F5830F04f9498 |
| KlayRegistrarController | 0x43289Fb1683Fd821819F60000D87c8c0dC7d07Ba |
| PublicResolver          | 0x1cf101C4886DcB7D1A8B95801E7E787A42547CbC |

### Reverse Resolution

| 이름                     | 주소                                         |
| ---------------------- | ------------------------------------------ |
| ReverseRegistrar       | 0x580Dc66f1cf9D8D69cd330Df381Efc6672a6b02e |
| DefaultReverseResolver | 0x149E752F92aF8694202047d3Bb7da95Ee1912051 |
| ReverseRecords         | 0x87f4483E4157a6592dd1d1546f145B5EE22c790a |

### Tokenomics

| 이름                 | 주소                                         |
| ------------------ | ------------------------------------------ |
| KNSToken           | 0x949fC808138081aB1FcbcbB5F311440CF2C3Ff73 |
| StakedKNSToken     | 0x08dA4D66604154E1c43689B8B25aEeD7d0343617 |
| LpTokenStakingPool | 0x539e50513AA55d10FCFbe5063040A4875A68b383 |

### Dex

| 이름         | 주소                                         |
| ---------- | ------------------------------------------ |
| DexFactory | 0x54998fDEb031Be36E4e172F1F510De27ABf967A5 |
| DexRouter  | 0x93A6e8Bd9D77938070D5870D244f15AB3F83515f |


# 보안 감사

KNS의 스마트 컨트랙트는 1inch, SushiSwap과 같은 메이저 블록체인 프로젝트들과 동일하게 HAECHI LABS의 보안 감사를 통과하였습니다.

{% embed url="<https://drive.google.com/file/d/1emcaTt9p9ZA4faBqmS_ppe3AxG3wsRY5/view?usp=sharing>" %}


# Introduction

Welcome to Klaytn Name Service!

Klaytn Name Service(KNS) is a decentralized on-chain name service on the Klaytn blockchain, inspired by [Ethereum Name Service](https://ens.domains).

The main functionality of KNS is mapping a human-readable domain such as 'kns.klay' to Klaytn addresses and its related metadata. Also, by setting primary domains, you can map a Klaytn wallet address to a domain (reverse resolution).

KNS follows KIP-17 standards (NFT) and secondary purchase is available at [Opensea.io](https://opensea.io/).

KNS dreams of a future where all Web3 users has an on-chain identity. Any user owning either a Klip or a Kaikas wallet can register any domain longer than or equal to 3 letters (with certain rules) not taken by another user.


# .kaia Domains

The '.kaia' TLD (top-level domain) is owned by a smart contract, and any domain using the .kaia TLD such as 'kns.kaia' can be registered without permission.

The domains are available for registration on a yearly basis, and the registration fee differs by length. The pricing can be found on the [Registration Fee](/english/domain/price) page.

Domains must consist of roman alphabet small letters or numbers within 3 to 255 letters. This is a measure to prevent IDN homograph attacks (an attack that exploits users by using similar-looking letters and confusing them).


# Registration Fee

Registration fees are on an annual basis and are set as follows:&#x20;

| Length     | Price (Annual) |
| ---------- | -------------- |
| 3 letters  | 500 USD        |
| 4 letters  | 150 USD        |
| 5 letters  | 30 USD         |
| 6 letters  | 20 USD         |
| 7+ letters | 10 USD         |


# Profile

KNS domains can contain profile information including a Kaia address.

### Kaia Address

You can register your Kaia address and use the KNS domains you own instead. More and more services will be supporting KNS in the future.&#x20;

### Intro and Avatar

You can register a short intro and an avatar (profile picture). If you register an avatar, the default background image of your domain will be changed to it.

### Social

You can register email, Discord, Twitter, Kakaotalk ID and other social information that you wish to share with others.&#x20;


# KNS Token

$KNS is the governance token for KNS protocol.

![](/files/WULFxlnXWRg3LE5qeBLY)

## Token Information

* Token standard: KIP-7
* Token name: Klaytn Name Service Token
* Ticker: KNS
* Max supply: 10,000,000,000 (10 Billion)
* Contract address: 0x949fc808138081ab1fcbcbb5f311440cf2c3ff73

## Token Description

KNS is the governance token for KNS protocol.&#x20;

KNS web app offers the following de-fi services:

1. Swap between KAIA and KNS&#x20;
2. KAIA-KNS Liquidity pool provision&#x20;
3. Staking KNS into sKNS

KNS holers can earn rewards by providing liquidy or staking, and KNS team uses domain sales revenue to reward KNS holders indirectly.

Anyone holding KNS can use it to create proposals and set polls.


# Tokenomics

A deeper dive into $KNS token economy.

## Token Distribution

![](/files/vQfUvDeTkgpytkEZ4xYx)

* **Community Rewards (50%)**
  * Allocation: 5,000,000,000 KNS (5 billion KNS)
    * KNS governance staking reward 2,000,000,000 KNS (2 billion KNS)
    * KAIA-KNS LP staking reward 3,000,000,000 KNS (3 billion KNS)
  * Distributed over 48 months for liquidity pool and staking rewards
* **Treasury (15.75%)**
  * Allocation: 1,575,000,000 KNS (1.575 billion KNS)
  * Managed by DAO after the launching of the governance function&#x20;
* **Initial LP (0.75%)**
  * Allocation: 75,000,000 KNS (75 million KNS)
  * Used for providing liquidity the initial KAIA-KNS pool
* **Marketing & Partnership (5%)**
  * Allocation: 500,000,000 KNS (500 million KNS)
  * Used for marketing or cooperation with other projects
* **Airdrop (0.5%)**
  * Allocation: 50,000,000 KNS (50 million KNS)
  * Airdropped to the winners of KNS Domain Auction from March 28 to April 10, 2022
* **Team (15%)**
  * Allocation: 1,500,000,000 KNS (1.5 billion KNS)
  * Distributed to the KNS Dev Team
  * 30 months of linear distribution after 6 months of lock-up (Total 36 months)
* **Investors (13%)**
  * Allocation: 1,300,000,000 KNS (1.3 billion KNS)
  * Distributed to institutional investors of KNS&#x20;
  * 30 months of linear distribution after 6 months of lock-up (Total 36 months)

## Revenue Distribution

10% of domain sales revenue will be distributed to KNS Dev Team, 40% to the Treasury, and 50% to GradualDistributor.

![KNS Cash Flow Chart (before Kaia migration)](/files/pdSidL5tXb18TYCzydWg)

The details on the revenue distribution are as follows:

1. After domain registration, the revenue (KAIA) for KaiaRegistrarController is moved to PrimaryDistributor.
2. PrimaryDistributor distributes 10% to the team wallet, 40% to Treasury, and 50% to GradualDistributor.
3. GradualDistributor gradually sends distributed KAIA to SecondaryDistributor during the registration period of each domain.
4. SecondaryDistributor purchases KNS using 50% of the KAIA it received and distributes it to sKNS holders, and with the remaining 50%, it purchases KNS and provides liquidity to the KAIA-KNS pool. The LP tokens are managed by Treasury and become POL(Protocol Owned Liquidity).


# sKNS Token

![](/files/926u7kJUSBrtGy2nGg1G)

## Token Information

* Token standard: KIP-7
* Token name: Staked Klaytn Name Service Token
* Ticker: sKNS
* Max supply: N/A
* Contract address: 0x08da4d66604154e1c43689b8b25aeed7d0343617

## Token Description

$sKNS token is a sign for (governance) staking $KNS.

The $KNS/$sKNS conversion rate begins at 1 and gradually increases as $KNS tokenomics rewards $sKNS holders. Hence, without making additional claims, you will have an increased number of $KNS by unstaking.&#x20;


# Domains

## Search for Domains

<figure><img src="/files/8FjpXKVc4kpwAQPOXIiz" alt=""><figcaption></figcaption></figure>

By clicking the domain menu, you can find a search box, recently registered domains, and domains about to expire.&#x20;

<figure><img src="/files/T1oom2EWmaHkptjlAoQa" alt=""><figcaption></figcaption></figure>

Using the search box, you can search for any domain you want.&#x20;

## Domain Details

![](/files/ZLDMLzh7SLO5KsGVHETb)

Upon clicking any specific domain, you can look up its details in the \[Details] tab. The \[Details] tab will show the owner, expiration date, resolver, and profile.

![](/files/WAXK4hgem9eUqVo8mqKZ)

If you own the domain, you can click \[Edit] to edit profile information.&#x20;

## **Registering a Domain**&#x20;

Any domain without an owner is available for registration (purchase).

![](/files/xWQylIxlPKXBj2vcxWGw)

In the \[Register] tab, you can register a domain on a yearly basis.

![](/files/AwIhOcSHdoQSSdKPuxea)

![](/files/FopSkJVMIBP2llMSqeWS)

To prevent front-running, you will be sending two transactions.


# My Page

![](/files/yriFJyT9A5yAwZHxX3sJ)

## **Setting a Primary Domain**

You can set a primary domain and choose which domain you want to expose for your wallet address. Select a domain among the domains you own, and click \[Set].&#x20;

## **My Page**

You can find a list of domains you own. The name of the domain, registration fee, and expiration date will be displayed.

## **Renewing a Domain**

![](/files/KJE6ajQNNECNpOwrTFjr)

You can select multiple domains and renew them at the same time.&#x20;


# Swap

![](/files/3tV7gsFSKHaTWgX5KtLO)

## Swap

Swap provides an exchange function between KAIA and KNS. Use the arrow button to change the target crypto. Set max slippage and confirm the minimum amount to be received. Click \[Swap] to send a transaction.&#x20;


# LP Pool

You can provide liquidity to the KAIA-KNS swap pool. APR(Annual Percentage Rate) and \[Your Staked LP] are displayed on the page. The reward can be checked at the \[Claimable Reward] tab and claimed by clicking the \[Claim] button.&#x20;

## **Staking**

![](/files/Rimlv094N6NdPws5tIIm)

You can provide liquidity to the pool by using the same value of KAIA and KNS.&#x20;

1. Acquire LP tokens by providing KAIA and KNS to the pool&#x20;
2. Stake the LP tokens &#x20;

By finishing these two steps you can earn rewards for LP token staking.

## **Unstaking**

![](/files/iBxruYloRuGKuCsMSfkg)

You can unstake the staked LP and using the LP token, you can retrieve KAIA and KNS from the pool.&#x20;


# Governance

On the Governance page, you can receive airdrops and stake your KNS tokens.

If you are a winner of the domain auction that was held prior to the launch of KNS V1, you can claim your airdrop.&#x20;

You can stake your KNS tokens to earn staking rewards and governance voting rights and by unstaking, you can claim staking rewards.&#x20;

You can also check the APR of sKNS staking, KNS price, KNS/sKNS exchange rate, and the number of KNS and sKNS tokens you own.

## **Airdrop**

![](/files/FpMeoOIAvXpqvSzMhNeo)

Confirm your airdrop amount and click the \[Claim] button to claim the airdrop.

## **Manage Staking**

![](/files/Tj9sJDRG2fT6cWz5xASh)

![](/files/5kJp5oQjSJiiUpduA3Ib)

By clicking \[Stake / Unstake] button you can manage your staking and unstaking .

You can stake your KNS to earn staking rewards. You can also check staking APR, confirm the amount of KNS tokens staked and decide to stake or unstake KNS.&#x20;

![](/files/yy15XXDyv90InwBPDnEk)

Rewards can be withdrawn after 7 days of unstaking period by clicking \[Redeem KNS]. The information about unstaking can be found by clicking the \[Unstaking Log] button

## **Governance**

The governance is conducted through a decentralized governance platform “Snapshot.”

Suggestions and votes can be made at <https://snapshot.org/#/knsdao.eth>


# Core Partners

## Krust

![](/files/FKAGKSrRldItoLOkLEba)

## Hyperithm

![](/files/VyRwImgY1wBHdyS2t9oQ)


# Defi

| Partner                                     | Logo                             |
| ------------------------------------------- | -------------------------------- |
| [KokoaFinance](https://kokoa.finance/)      | ![](/files/ZWuuPlPStoMekVL66DmB) |
| [KokonutSwap](https://kokonutswap.finance/) | ![](/files/fKwxLpXP2hQmYskGqKAd) |
| [Eklipse](https://eklipse.finance/)         | ![](/files/4HAAaJHCPfBZrcBIHaA7) |
| [Claimswap](https://claimswap.org/)         | ![](/files/Y7iCY3x8mqdINfLGYPij) |
| [Swapscanner](https://swapscanner.io/ko)    | ![](/files/64Z7w98fUVOefZg3etHv) |


# Game & NFT

| Partner                                                | Logo                             |
| ------------------------------------------------------ | -------------------------------- |
| [Wemix](https://wemixnetwork.com/)                     | ![](/files/Y4kl7SPclcFBMxK5NhKj) |
| [Klaycity](https://klaycity.com/)                      | ![](/files/5WtYuBgdsA6Z8boWG7rS) |
| [Klay Kingdoms](https://klaykingdoms.com/)             | ![](/files/2CqYKm0EPNDwje9NDeg5) |
| [Sheepfarm](https://sheepfarm.io/)                     | ![](/files/gqPHbNGlZZsPHQqCQfLj) |
| [MetaToyDragonZ](https://metatoydragonz.io/)           | ![](/files/RhY5ja2sZSCySZt5CZWe) |
| [Hero Blaze: Three Kingdoms](https://heroblaze3kd.io/) | ![](/files/W7Vy4BeY2VeKDbMcsOZX) |
| [Hypesalt](https://hypesalt.com/)                      | ![](/files/S4auZja77kwNHot1QYNb) |
| [Pado ](https://trippy.place/)                         | ![](/files/WmYzmYLzcdZodH5Ue7Y9) |
| [Drawshop Kingdom Reverse](https://drawshop.io/)       | ![](/files/rPbCLPNeUHNr9ATTnubm) |


# KNS Integration

Guide for implementing KNS Resolution

## Basic Structure

For the basic structure, KNS referred to Ethereum Name Service(ENS).

1. `Registry` contains information about the `owner` and the `resolver`.
2. `Resolver` contains wallet addresses corresponding to each domain and additional information (email addresses, avatars, etc.)

![](/files/zehcMlxLsAWmFzMTeUmM)

The owner of the Registry is the holder of the domain NFT, but the result of resolving a domain can only be acquired by calling `addr` on a resolver. Please refer to the figure above. Hence, the owner of the Registry could be different from resolving a domain.&#x20;

By default, a registered domain's resolver should be PublicResolver. PublicResolver has the addr method and can resolve to any appropriate address. Using KNS's front end, you can maintain always maintain the resolver as PublicResolver.&#x20;

## Forward Resolution (Domain **→** Address)

This is an implementation of a forward resolution using [ethers](https://github.com/ethers-io/ethers.js) and [eth-ens-namehash](https://github.com/danfinlay/eth-ens-namehash) library.

```typescript
const namehash = require("eth-ens-namehash");
const ethers = require("ethers");

/********************************************
    Declare provider and ABI to use here
********************************************/

async function forwardResolve(domain) {
    const EMPTY_ADDRESS = "0x0000000000000000000000000000000000000000";
    let registry = new ethers.Contract(
        REGISTRY_ADDRESS,
        REGISTRY_ABI,
        provider
    );
    let node = namehash.hash(domain);
    let resolverAddress = await registry.resolver(node);
    if (parseInt(resolverAddress, 16) === 0) {
        return EMPTY_ADDRESS;
    }
    let resolver = new ethers.Contract(
        resolverAddress, 
        PUBLIC_RESOLVER_ABI,
        provider
    );
    try {
        let address = await resolver.addr(node);
        return address;
    } catch(e) {
        console.error(e);
        return EMPTY_ADDRESS;
    }
}

forwardResolve("foo.klay").then(console.log);
```

On the contract, the domains are not managed as raw strings; They are managed as a [namehash](https://docs.ens.domains/contract-api-reference/name-processing#hashing-names) of the bytes32 format. The eth-ens-namehash library provides a function for calculating the namehash value. Of course, you can build it on your own using the link above. &#x20;

Using the calculated namehash value, you first query a domain's resolver address to the contract Registry. If the returned resolver address is 0, it's either not a registered domain, or the resolver is set as 0. Since a forward resolution is impossible in this case, an empty address will be returned.

In other cases, use the returned resolver address and call addr method to obtain the address. There are possibilities where custom resolvers are set without the addr method, so we recommend setting an exception handling for such cases.

When a 0 address is returned, the returned value should be ignored.

```typescript
const REGISTRY_ABI = ["function resolver(bytes32) view returns (address)"]
const PUBLIC_RESOLVER_ABI = ["function addr(bytes32) view returns (address)"]
```

The minimum ABI for a forward resolution is as follows:

```typescript
const REGISTRY_ADDRESS = "0x0892ed3424851d2Bab4aC1091fA93C9851Eb5d7D"
const PUBLIC_RESOLVER_ADDRESS = "0xe2AE210c9b8601E00edE4aE5b9B23a80dCD12e3C"
```

Also, the above addresses are Registry and PublicResolver addresses deployed on the Cypress network. (The PublicResolver address is used later in this document.) &#x20;

### Event

To track changes in addresses, you can subscribe to the following event from the PublicResolver address. Of course, if the resolver is a custom resolver, tracking is not possible.

> **event AddrChanged(bytes32 indexed node, address addr);**
>
> \_node\_ is the namehash value of a domain, and this event means the address corresponding to the domain has been changed to \_addr\_.

When the owner or the resolver of a domain is changed, you should invalidate previously mapped addresses and query new addresses. For this, you should subscribe to the following events from the Registry address.

> **event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);**
>
> \_tokenId\_ is a simple casting value from bytes32 to unit256 for a namehash value of a domain, and this event means the owner of \_tokenId\_ has been change from \_from\_ to \_to\_. Registry follows IKIP17 standards, hence this event is same as a IKIP17 Transfer event.

> **event NewResolver(bytes32 indexed node, address resolver);**
>
> \_node\_ is the namehash value of a domain, and this event means the resolver address of the domain has been changed to \_resolver\_.

Taken together, the forward resolution of the version that manages the cache by subscribing to the events is represented in pseudocode in the following way:

```
// Forward resolution with caching
func forwardResolve(domain):
	namehash <- getNamehash(domain)
	if (namehash in cache):
		return cache[namehash]
	else:
		resolver <- getResolver(namehash)
		result <- resolver.addr(namehash)
		if (resolver == PUBLIC_RESOLVER):
			cache[namehash] <- result
		return result

// Daemon for listening events
func daemon():
	while (true):
		event <- listenEventFrom([REGISTRY, PUBLIC_RESOLVER])
		if (event.type == "AddrChanged"): // from PUBLIC_RESOLVER
			cache[event.node] <- event.addr
		elif (event.type == "Transfer"): // from REGISTRY
			invalidate cache[event.tokenId]
		elif (event.type == "NewResolver"): // from REGISTRY
			invalidate cache[event.node]
```

## **Reverse Resolution (Address → Domain)**

{% hint style="info" %}
The operation of reverse resolution is similar to how it works on ENS, so referring to [ENS reverse resolution document](https://docs.ens.domains/dapp-developer-guide/resolving-names#reverse-resolution) could help your understanding.
{% endhint %}

Reverse resolution is a function where you can set a primary domain of an address among many domains that might have been mapped to it. For this, we use a unique domain named “addr.reverse”. The owner of “addr.reverse” domain in Registry is the ReverseRegistrar contract, and through this contract, users can acquire the ownership of the subdomain “(address).addr.reverse”.

For example, the address “0x314159265dd8dbb310642f98f50c066173c1259b”, can claim the ownership of the domain “314159265dd8dbb310642f98f50c066173c1259b.addr.reverse” through the ReverseRegistrar contract, and can set "hello.klay" as the name(“0x9f3f…17e6”) value and set its primary domain as "hello.klay" (“0x9f3f…17e6” is the namehash value of “3141…259b.addr.reverse”).

The resolver of “(address).addr.reverse” domain is set as DefaultReverseResolver by default, unlike other normal domains, and DefaultReverseResolver supports name(bytes32).

When setting up a primary domain, there could be cases where it is set to a domain that is not actually resolved to that address, or the ownership of a primary domain is changed. So you need to check some additional items when implement the reverse resolution, specifically:  &#x20;

1. Query the resolver address of “(address).addr.reverse” to Registry. (When making the query, all the alphabets\[a-f] should be **lowercase**, and 0x prefix should **not** be entered.) If the query result returns 0, it's a failed query. (Exit)
2. Call name(bytes32 namehash) to the resolver as a result of process 1. The namehash is namehash value of “(address).addr.reverse”. If the query returns an empty string, it's a failed query. (Exit)
3. Using the namehash of a domain as a result of process 2, query the resolver address to Registry. If the query result returns 0, it's a failed query. (Exit)
4. Using the resolver address as a result of process 3, call addr(bytes32 namehash). The namehash is the namehash value of the domain resulting from process 2. If the address value returned is different from the address initially entered, it's a failed query. (Exit)
5. If process from 1 to 4 was successful without any failure, the primary domain of the entered domain should be the domain resulting from process 2. If you encounterd a failure during the process, either the primary domain was not set, or there could be an error in its setting.&#x20;

If we bring the example used above, for process 1, we will call resolver(“0x9f3f…17e6”) to Registry and get the resolver address corresponding to “3141…259b.addr.reverse”, which by default, will be DefaultReverseResolver address. For process 2, we will call name(“0x9f3f…17e6”) to that resolver address and get “hello.klay”. For process 3, we will use “0x6f37…b595”, which is the namehash value for “hello.klay”, and call resolver(“0x6f37…b595”) to the Registry and get the resolver address of “hello.klay”, which by default, will be PublicResolver address. For process 4, we will call addr(“0x6f37…b595”) to the actual resolver address and if it's resolved to “0x3141…259b” address, it's a success, and if not, it's a failure.&#x20;

**All of the processes above are implemented in the getName(address) function of the ReverseRecords contract.** So, most simply, the reverse resolution could be implemented in the following way:

```typescript
import "ethers";

async function reverseResolve(address: any) {
  let reverseRecords = new ethers.Contract(
    REVERSE_RECORDS_ADDRESS,
    REVERSE_RECORDS_ABI,
    provider
  );
  let name = await reverseRecords.getName(address);
  return name;
}

reverseResolve("0x314159265dd8dbb310642f98f50c066173c1259b").then(console.log);
```

The minimum ABI for a reverse resolution is as follows:&#x20;

```typescript
const REVERSE_RECORDS_ADDRESS = "0x87f4483E4157a6592dd1d1546f145B5EE22c790a"
const REVERSE_RECORDS_ABI = ["function getName(address) view returns (string)"]
```

### Event

The execute caching on reverse resolution, you must subscribe to events. Basically, if any of the processes from 1 to 4 above has a different result, you should make a new query to get a new result. Among processes 1 through 4, processes 3 and 4 are verification processes to check that it returns the same address as a forward resolution for domains returned from process 2, so it is essential to subscribe to the events in the [Forward Resolution](https://github.com/klaytn-name-service/kns-gitbook-en/blob/main/dev/sdk.md#forward-resolution).&#x20;

Additionally, to track the change of the name (primary domain) set in DefaultReverseResolver during processes 1 and 2, you should subscribe to the following event:

> **event NameChanged(bytes32 indexed node, string name);**
>
> \_node\_ is the namehash value of “(address).addr.reverse” and this event means the name of the address was changed to \_name\_.


# Reverse Resolution Sample Code

## Constants

```javascript
const JSON_RPC_PROVIDER = "" // JSON-RPC provider url
const REVERSE_RECORDS_ADDRESS = "0x87f4483E4157a6592dd1d1546f145B5EE22c790a";
const REVERSE_RECORDS_ABI = [
  {
    type: "function",
    name: "getName",
    stateMutability: "view",
    inputs: [
      {
        internalType: "address",
        name: "addr",
        type: "address",
      },
    ],
    outputs: [
      {
        internalType: "string",
        name: "",
        type: "string",
      },
    ],
  },
];
```

## ethers.js

```javascript
import ethers from "ethers";

async function domainOrAddressEthers(address) {
  const provider = new ethers.providers.JsonRpcProvider(JSON_RPC_PROVIDER);
  const reverseRecords = new ethers.Contract(REVERSE_RECORDS_ADDRESS, REVERSE_RECORDS_ABI, provider);
  const domain = await reverseRecords.getName(address);
  if (domain === "") {
    return address;
  } else {
    return domain;
  }
}
```

## web3.js

```javascript
import Web3 from "web3";

async function domainOrAddressWeb3(address) {
  const web3 = new Web3(JSON_RPC_PROVIDER);
  const reverseRecords = new web3.eth.Contract(REVERSE_RECORDS_ABI, REVERSE_RECORDS_ADDRESS);
  const domain = await reverseRecords.methods.getName(address).call();
  if (domain === "") {
    return address;
  } else {
    return domain;
  }
}
```

## caver-js

```javascript
import Caver from "caver-js";

async function domainOrAddressCaver(address) {
  const caver = new Caver(JSON_RPC_PROVIDER);
  const reverseRecords = new caver.klay.Contract(REVERSE_RECORDS_ABI, REVERSE_RECORDS_ADDRESS);
  const domain = await reverseRecords.methods.getName(address).call();
  if (domain === "") {
    return address;
  } else {
    return domain;
  }
}
```

## Sample Wallet Address and Domain

If the implementation of reverse resolution is successful, it should return the following domain when the following wallet address is fetched.

* Wallet Address: `0x0000ac03932ff48ee30209774e3f10fb0ac522e9`
* Domain: `kns.klay`


# Contract Address

A list of smart contract addresses for KNS

### Domain Registry

| Name        | Address                                    |
| ----------- | ------------------------------------------ |
| KNSRegistry | 0x0892ed3424851d2Bab4aC1091fA93C9851Eb5d7D |

### .klay

| Name                    | Address                                    |
| ----------------------- | ------------------------------------------ |
| BaseRegistrar           | 0x0345b51bD06b0993A6b56890308F5830F04f9498 |
| KlayRegistrarController | 0x43289Fb1683Fd821819F60000D87c8c0dC7d07Ba |
| PublicResolver          | 0x1cf101C4886DcB7D1A8B95801E7E787A42547CbC |

### Reverse Resolution

| Name                   | Address                                    |
| ---------------------- | ------------------------------------------ |
| ReverseRegistrar       | 0x580Dc66f1cf9D8D69cd330Df381Efc6672a6b02e |
| DefaultReverseResolver | 0x149E752F92aF8694202047d3Bb7da95Ee1912051 |
| ReverseRecords         | 0x87f4483E4157a6592dd1d1546f145B5EE22c790a |

### Tokenomics

| Name               | Address                                    |
| ------------------ | ------------------------------------------ |
| KNSToken           | 0x949fC808138081aB1FcbcbB5F311440CF2C3Ff73 |
| StakedKNSToken     | 0x08dA4D66604154E1c43689B8B25aEeD7d0343617 |
| LpTokenStakingPool | 0x539e50513AA55d10FCFbe5063040A4875A68b383 |

### Dex

| Name       | Address                                    |
| ---------- | ------------------------------------------ |
| DexFactory | 0x54998fDEb031Be36E4e172F1F510De27ABf967A5 |
| DexRouter  | 0x93A6e8Bd9D77938070D5870D244f15AB3F83515f |


# Audit

The smart contract of KNS has passed a security audit by HAECHI LABS, which major blockchain projects such as 1inch and SushiSwap have also taken.

{% embed url="<https://drive.google.com/file/d/1emcaTt9p9ZA4faBqmS_ppe3AxG3wsRY5/view?usp=sharing>" %}


