<p align="middle">
  <img src="https://www.rifos.org/assets/img/logo.svg" alt="logo" height="100" >
</p>
<h3 align="middle"><code>@rsksmart/rns-sdk</code></h3>
<p align="middle">
  RNS SDK
</p>
<p align="middle">
  <a href="https://github.com/rsksmart/rns-sdk/actions/workflows/ci.yml" alt="ci">
    <img src="https://github.com/rsksmart/rns-sdk/actions/workflows/ci.yml/badge.svg" alt="ci" />
  </a>
  <a href="https://github.com/rsksmart/rns-sdk/actions/workflows/publish.yml" alt="ci">
    <img src="https://github.com/rsksmart/rns-sdk/actions/workflows/publish.yml/badge.svg" alt="ci" />
  </a>
  <br />
  <a href="https://lgtm.com/projects/g/rsksmart/rns-sdk/context:javascript">
    <img src="https://img.shields.io/lgtm/grade/javascript/github/rsksmart/rns-sdk" />
  </a>
  <a href='https://coveralls.io/github/rsksmart/rns-sdk'>
    <img src='https://coveralls.io/repos/github/rsksmart/rns-sdk/badge.svg' alt='Coverage Status' />
  </a>
  <a href="https://badge.fury.io/js/%40rsksmart%2Frns-sdk">
    <img src="https://badge.fury.io/js/%40rsksmart%2Frns-sdk.svg" alt="npm" />
  </a>
</p>

## Features

- .rsk domains:
  - availability and prices
  - registrations

- Manage domains:
  - availability
  - Set subdomain owner
  - transfer
  - reclaim
  - Get/set domain owner
  - Get/set domain resolver

- Manage resolution:
  - Get/set `addr` resolution

## Usage

The library supports the following modules:
- .rsk domains registrations using `RSKRegistrar`
- .rsk domains registrations using the new partner registrar contracts `PartnerRegistrar`
- RNS domains admin using `RNS`
- Domain address resolution using `AddrResolver`
- Information on a partner configuration using `PartnerConfiguration`

You will need to use this addresses to initialize the library:

| Contract name | RSK Mainnet | RSK Testnet |
| - | - | - |
| RNS Registry (`rsnRegistryAddress`) | [`0xcb868aeabd31e2b66f74e9a55cf064abb31a4ad5`](https://explorer.rsk.co/address/0xcb868aeabd31e2b66f74e9a55cf064abb31a4ad5) | [`0x7d284aaac6e925aad802a53c0c69efe3764597b8`](https://explorer.testnet.rsk.co/address/0x7d284aaac6e925aad802a53c0c69efe3764597b8) |
| RIF Token ERC-677 ERC-20 (`rifTokenAddress`) | [`0x2acc95758f8b5f583470ba265eb685a8f45fc9d5`](https://explorer.rsk.co/address/0x2acc95758f8b5f583470ba265eb685a8f45fc9d5) | [`0x19f64674d8a5b4e652319f5e239efd3bc969a1fe`](https://explorer.testnet.rsk.co/address/0x19f64674d8a5b4e652319f5e239efd3bc969a1fe) |
| ERC-721 .rsk domains token (`rskOwnerAddress`) | [`0x45d3e4fb311982a06ba52359d44cb4f5980e0ef1`](https://explorer.rsk.co/address/0x45d3e4fb311982a06ba52359d44cb4f5980e0ef1) | [`0xca0a477e19bac7e0e172ccfd2e3c28a7200bdb71`](https://explorer.testnet.rsk.co/address/0xca0a477e19bac7e0e172ccfd2e3c28a7200bdb71) |
| .rsk domains registrar (`fifsAddrRegistrarAddress`) | [`0xd9c79ced86ecf49f5e4a973594634c83197c35ab`](https://explorer.rsk.co/address/0xd9c79ced86ecf49f5e4a973594634c83197c35ab) | [`0x90734bd6bf96250a7b262e2bc34284b0d47c1e8d`](https://explorer.testnet.rsk.co/address/0x90734bd6bf96250a7b262e2bc34284b0d47c1e8d) |
| .rsk domains partner registrar (`partnerRegistrarContractAddress`) | - | [`0x8104d97f6d82a7d3afbf45f72118fad51f190c42`](https://explorer.testnet.rsk.co/address/0x8104d97f6d82a7d3afbf45f72118fad51f190c42) |
| .rsk domains partner registrar (`partnerRegistrarContractAddress`) | - | [`0x8104d97f6d82a7d3afbf45f72118fad51f190c42`](https://explorer.testnet.rsk.co/address/0x8104d97f6d82a7d3afbf45f72118fad51f190c42) |

> See also RNS Resolver library [`@rsksmart/rns-resolver.js`](https://github.com/rsksmart/rns-resolver.js) to resolve domains following the standard protocol

### .rsk domain registrations

RNS validates and normalizes names in the following way:

1. **Validation:** RNS validates that a name is a valid domain name by checking that it conforms to the following rules:

  - The name contains only letters (a-z), digits (0-9), and hyphens (-).
  - The name starts and ends with a letter or digit.

2. **Normalization:** RNS normalizes a name by converting it to its canonical form, which includes:

  - Converting all letters to lowercase.
  - Expanding Punycode-encoded internationalized domain names (IDNs) to their Unicode representation.

#### 1. Using the RSKRegistrar
You can register .rsk domains paying with RIF Tokens. First, create the instance of `RSKRegistrar`

```typescript
import { Signer } from 'ethers'
import { RSKRegistrar } from '@rsksmart/rns-sdk'

let signer: Signer
const rskRegistrar = new RSKRegistrar(rskOwnerAddress, fifsAddrRegistrarAddress, rifTokenAddress, signer)
```

Query price and availability

```typescript
const label = 'taringa'

const available = await rskRegistrar.available(label)

const duration = BigNumber.from('1')

const price = await rskRegistrar.price(label, duration)
```

Register the domain

```typescript
const { makeCommitmentTransaction, secret, canReveal } = await rskRegistrar.commitToRegister(label, testAccountAddress)

await makeCommitmentTransaction.wait()

// you need to wait at least for one minute, you can build
// your own polling strategy checking canReveal to ensure
// it is the correct time to submit the register tx
const commitmentReady = await canReveal()

if (!commitmentReady) throw

const registerTx = await rskRegistrar.register(
  label,
  testAccountAddress,
  secret,
  duration,
  price
)

await registerTx.wait()
```

#### 2. Using the PartnerRegistrar

You can use the PartnerRegistrar to register domains using the partner registrar contracts. First, create the instance of `PartnerRegistrar`.
> The PartnerRegistrar supports one click register even where commitment is required.

```typescript
import { Signer } from 'ethers'
import { PartnerRegistrar } from '@rsksmart/rns-sdk'

let signer: Signer

const networkAddresses = {
  rskOwnerAddress: '0x0000000000000000000000000000000000000000',
  rifTokenAddress: '0x0000000000000000000000000000000000000000',
  partnerRegistrarAddress: '0x0000000000000000000000000000000000000000',
  partnerRenewerAddress: '0x0000000000000000000000000000000000000000',
  partnerAddress: '0x0000000000000000000000000000000000000000'
}

const partnerRegistrar = new PartnerRegistrar(signer, 'localhost', networkAddresses);
```
**Note:**

The `network` param is mandatory value, and it only valid values are `'mainnet'`, `'testnet'` or `'localhost'`.

Param `networkAddresses` is mandatory for `network='localhost'`.
For `'mainnnet'` and `'testnet'` will be optional and can be passed only in case of needing to modify one of the default
addresses for the given network, otherwise, default addresses will be used.

- Query price and availability

```typescript
const label = 'taringa'

const available = await partnerRegistrar.available(label)

const duration = BigNumber.from('1')

const price = await partnerRegistrar.price(label, duration)
```

- Register the domain

```typescript
const label = 'taringa'
const duration = BigNumber.from('1')
const ownerAddress = '0x...' //address of the owner of the domain

const price = await partnerRegistrar.price(label, duration)

const { commitHash, commitSecret, registerTxHash } = await partnerRegistrar.commitAndRegister(label, ownerAddress, duration, price)
```


- Transfer the domain

```typescript
const label = 'taringa'
const toAddress = '0x...' //address to transfer the domain ownership to

const transactionHash = await partnerRegistrar.transfer(label, toAddress)
```

- Renew the domain

```typescript
const label = 'taringa'
const duration = BigNumber.from('1')

const price = await partnerRegistrar.price(label, duration)

const transactionHash = await partnerRegistrar.renew(label, duration, price)
```

### Domain management

Create `RNS` instance

```typescript
import { Signer } from 'ethers'
import { RNS } from '@rsksmart/rns-sdk'

let signer: Signer
const rns = new RNS(registryAddress, signer)
```

#### Owner

Get and set the controller of a domain you own

```typescript
const domain = 'user1.taringa.rsk'
const newController = '0xb774...d771'

const tx = await rns.setOwner(domain, newController)
await tx.wait()

const controller = await rns.owner(domain)
```

#### Resolver

Get and set the resolver of a domain you own

```typescript
const domain = 'user1.taringa.rsk'
const resolverAddr = '0xb774...d771'

const tx = await rns.setResolver(domain, resolverAddr)
await tx.wait()

const controller = await rns.resolver(domain)
```

#### Subdomains

Set the owner of a subdomain of a domain you own

```typescript
const domain = 'taringa.rsk'
const subdomainLabel = 'user1'
const ownerAddress = '0x8c0f...1264'

const subDomainAvailable = await rns.getSubdomainAvailability(domain, subdomainLabel)

const tx = await rns.setSubdmoainOwner(domain, subdomainLabel, ownerAddress)
await tx.wait()
```

### Address resolution

Create `AddrResolver` instance

```typescript
import { Signer } from 'ethers'
import { AddrResolver } from '@rsksmart/rns-sdk'

let signer: Signer
const addrResolver = new AddrResolver(registryAddress, signer)
```

Get and set the address of a domain or subdomain you own

```typescript
const domain = 'user1.taringa.rsk'
const addr = '0xb774...d771'

const tx = await addrResolver.setAddr(domain, ownerAddress)
await tx.wait()

const addr = await addrResolver.addr(domain)
```
## Troubleshooting
1. Browser
> The library uses a dependency that requires Buffer to be available globally. If you are using in a browser environment, you need to the following:

- Install the dependency:
`npm install -D buffer`

- Add the following to your webpack config:
```javascript
const webpackConfig = {
    resolve: {
        fallback: {
            buffer: require.resolve('buffer/'),
        },
    },
    plugins: [
        new webpack.ProvidePlugin({
            Buffer: ['buffer', 'Buffer'],
        }),
    ],
};
```

OR

```javascript
window.Buffer = window.Buffer || require('buffer/').Buffer;
```

2. React Native
> You can use the `rn-nodeify` package

```
rn-nodeify --install buffer -hack --yarn && patch-package
```

### Partner Configuration
We have also provided a class for interacting with the partner configuration contract
    
```typescript
import { Signer } from 'ethers'
import { PartnerConfiguration } from '@rsksmart/rns-sdk'
  
let signer: Signer
const partnerConfigurationAddress = '0x...' //address of the partner configuration contract

const partnerConfiguration = new PartnerConfiguration(partnerConfigurationAddress, signer)
```

Available operations:
- getMinLength
```typescript
    const minLength = await partnerConfiguration.getMinLength()
```
- getMaxLength
```typescript
    const maxLength = await partnerConfiguration.getMaxLength()
```
- getMinDuration
```typescript
    const minDuration = await partnerConfiguration.getMinDuration()
```
- getMaxDuration
```typescript
    const maxDuration = await partnerConfiguration.getMaxDuration()
```
- getMinCommitmentAge
> This is the minimum time that needs to pass before a commitment can be revealed
```typescript
    const minCommitmentAge = await partnerConfiguration.getMinCommitmentAge()
```
- getFeePercentage
> This is the percentage of the domain price that will be charged as a fee
```typescript
    const feePercentage = await partnerConfiguration.getFeePercentage()
```
- getDiscount
> This is the percentage of the domain price that will be discounted for the partner
```typescript
    const discount = await partnerConfiguration.getDiscount()
```
- getPrice
```typescript
    const price = await partnerConfiguration.getPrice(label, duration)
```
- validateName
> This function will resolve if the name is valid
```typescript
    const valid = await partnerConfiguration.validateName(label, duration)
```

### List of Partners and addresses
Coming soon

## Run for development

Install dependencies:

```
npm i
```

### Run unit tests

```
npm test
```

Coverage report with:

```
npm run test:coverage
```

### Run linter

```
npm run lint
```

Auto-fix:

```
npm run lint:fix
```

### Build for production

```
npm run build
```

### Branching model

- `main` has latest release. Merge into `main` will deploy to npm. Do merge commits.
- `develop` has latest approved PR. PRs need to pass `ci` and `scan`. Do squash & merge.
- Use branches pointing to `develop` to add new PRs.
- Do external PRs against latest commit in `develop`.
