# Whitelist smart contract

## 🌊 Intro

#### This repository provides an implementation of Whitelist interface proposed in the [TZIP-15](https://gitlab.com/tzip/tzip/-/blob/tzip-15-whitelist/proposals/tzip-15/tzip-15.md) written in [SmartPy](https://smartpy.io/): Python library for constructing Tezos SC and compiled to Michelson code. The contract is migrated from the [Lorentz whitelisting contract](https://github.com/tqtezos/lorentz-contract-whitelist).

## 💡 Why a whitelist contract?

The purpose of the Whitelist contract is to validate transfers so the token's contract holder can control which users can perform such operations. The Whitelist contract contains a list of `users` and `entities` that have satisfied the euroTz KYC/AML compliance procedures and thus are eligible to hold euroTz Tokens.

## 🔔 Specifications

#### `UserID`

As userID, we use the user's public key hash, e.g. a `tz1 address`.

### Storage

• `whitelists`:

```
big_map (
         key = whitelistID: Nat,
         value = {
         unrestricted: Bool,
         allowedWhitelists: set(whitelistID)
        })
```

• `users`:

```
big_map (
         key = userID: address,
         value = whitelistID : Nat
        )
```

• `admin`: address

• `issuer`: address

### 🚀 We use the interface as an on-chain wrapper:

1. The whitelist contract is deployed separately from our euroTz contract.
2. The assertion entrypoints are called from euroTz contract, without requiring callbacks since they call `FAILWITH` when they fail.

### How does our wrapper whitelist SC work?

🎥 ***Main scenarios:***

1. `Issuer` may transfer to `bob` **ONLY** if `bob`'s is an asserted receiver, in other words, bob must be added to the `users` big\_map and his `whitelistID` must be set and `unrestricted` in the `whitelists` big\_map.
2. `Alice` may transfer to `Bob` **ONLY** if `bob` and `Alice` are asserted users AND `Bob`'s whitelistID is in `Alice`'s `allowedWhitelists`.

## ⬇️ Get the Project:

1. Clone the project & cd to the directory;
2. Install the packages with `yarn install`.

## 🔍 EntryPoints  (SC methods):

| EntryPoint           | Params (type / desc.)                                                            | Permission | Type        |
| -------------------- | -------------------------------------------------------------------------------- | ---------- | ----------- |
| addUser              | `address (address) / whitelistID (option(nat))`                                  | Admin      | Management  |
| setWhitelistOutbound | `whitelistID (nat) / option(allowed_whitelists (set) / unrestricted (bool))`     | Admin      | Management  |
| setAdmin             | `address (address)`                                                              | Admin      | Management  |
| setIssuer            | `address (address)`                                                              | Admin      | Management  |
| getAdmin             | `address (address / KT1 view contractAddress)`                                   | Any        | Informative |
| getIssuer            | `address (address / KT1 view contractAddress)`                                   | Any        | Informative |
| getWhitelist         | `address (address / KT1 view contractAddress) / whitelistID (nat)`               | Any        | Informative |
| getUser              | `address (address / KT1 view contractAddress) / address (address / userAddress)` | Any        | Informative |
| assertReceiver       | `address (address)`                                                              | Any        | Assertion   |
| assertReceivers      | `set (set(address))`                                                             | Any        | Assertion   |
| assertTransfer       | `from_ (address) / to_ (address)`                                                | Any        | Assertion   |
| assertTransfers      | `set (set({from_: address, to_: address}))`                                      | Any        | Assertion   |

*Visit* [*TZIP-15*](https://gitlab.com/tzip/tzip/-/blob/tzip-15-whitelist/proposals/tzip-15/tzip-15.md#entrypoints) *to get a more developed description of the whitelist contract entrypoints.*

## 👀 Tests Cases

#### The smartContract tests are written in smartPy and in JS (with Taquito, Mocha and Chai)

> To launch tests, you have to:

1. Originate the whitelist contract with:

   ```
    yarn originate-whitelist
   ```
2. Paste the address of the originated whitelist SC in the conf file under `/conf` as following:

```
whitelistContractAddress: KT1-whitelist-address
```

1. Originate the euroTz contract with:

   ```
    yarn originate-euroTz
   ```
2. Paste the address of the originated euroTz SC in the same conf file as follows:

```
euroTzContractAddress: KT1-euroTz-address
```

🎉 **You're ready to launch tests now\...**

> *The following tests use as*

*• whitelist contract address:* [*KT1PFj9vshZKrHYLCswxUXKtY89SDYkXThDC*](https://you.better-call.dev/carthagenet/KT1PFj9vshZKrHYLCswxUXKtY89SDYkXThDC/operations)

*• euroTz contract address:* [*KT1WcxYBrh9WwRo1vkbSJxAqr6ZVAUqDEiFg*](https://you.better-call.dev/carthagenet/KT1WcxYBrh9WwRo1vkbSJxAqr6ZVAUqDEiFg/operations)

#### Management entrypoints

```
    yarn test-setAdmin # Test setAdmin entryPoint
```

#### Test Output:

```javascript
Whitelist Smart Contract: SetAdmin Entrypoint tests
initialAdmin:  tz1SVqTz7entj982jDSKcTQNgT7f2cg7C8dk
------------------------------------------------------------
MESSAGE STATEMENT:  only admin may update
✓ Update admin as non-admin / Should fail (4409ms)

OpHash :  oo5ofnco8QxcMtjm4Xo3Px93knyssQBLhL2bCTeKHUSbpWW7fX3
Admin from storage:  tz1XrCvviH8CqoHMSKpKuznLArEa1yR9U7ep
✓ Update admin as admin / Should succeed (27855ms)

OpHash :  oom91CsjUhFs9ThaSP2S2NgtYK4UT5HnceEHwr27i6bn3kvxeMX
Admin from storage:  tz1SVqTz7entj982jDSKcTQNgT7f2cg7C8dk
✓ Reset old admin as admin / Should succeed (46817ms)
3 passing (1m)
✨  Done in 82.75s.
```

```
    yarn test-setIssuer # Test setIssuer entryPoint
```

#### Test Output:

```javascript
Whitelist Smart Contract: SetIssuer Entrypoint tests
initialIssuer:  tz1SVqTz7entj982jDSKcTQNgT7f2cg7C8dk
------------------------------------------------------------
MESSAGE STATEMENT:  only admin may update
✓ Update issuer as non-admin / Should fail (5824ms)

OpHash :  ooERfBcJPaByiUdrhvidyHZEkZ2ki68LbVMz2mbPWrSJMfDwvzU
Issuer from storage:  tz1XrCvviH8CqoHMSKpKuznLArEa1yR9U7ep
✓ Update issuer as admin / Should succeed (38408ms)

OpHash :  oooiEXQCbmtAFYZpqvDKbJzeyXvYGYqWatTfGxCYdxxZona1gZz
Issuer from storage:  tz1SVqTz7entj982jDSKcTQNgT7f2cg7C8dk
✓ Reset old issuer as admin / Should succeed (52151ms)
3 passing (2m)
✨  Done in 101.61s.
```

```
    yarn test-addUser # Test addUser entryPoint
```

#### Test Output:

```javascript
Whitelist Smart Contract: AddUser Entrypoint tests

MESSAGE STATEMENT:  only admin may update
✓ Add user as non-admin / Should fail (2832ms)

MESSAGE STATEMENT:  issuer is not a user
✓ Add Issuer as a standard user as admin / Should fail (3734ms)

OpHash :  opVy3boRogtgpU2fz4wjgMwNEYxogymqGRqsUFzPdc7WChEzviq
✓ Add User with None as admin / Should succeed (16585ms)

OpHash :  ooZvAefYT4Q7bqi8K2AJQY1QWi3neMZVxEVqXGeAzsN52pHVKtz
userWhitelistID: 10
✓ Add user with Some as admin / Should succeed (116256ms)

4 passing (2m)
✨  Done in 142.39s.
```

```
    yarn test-setWhitelistOutbound # Test setWhitelistOutbound entryPoint
```

#### Test Output:

```javascript
Whitelist Smart Contract: SetWhitelistOutbound Entrypoint tests

MESSAGE STATEMENT:  only admin may update
✓ Set Whitelist Outbound as non-admin / Should fail (3120ms)

OpHash:  ooczsae6WA6xDTGYVhUuk4SS1WC55CFeqcBtPkdhGkVQjfqnrR6
whitelistDetails:  { allowed_whitelists: [], unrestricted: true }
✓ Set Whitelist Outbound as admin with Some / Should succeed (177318ms)

OpHash: onjtUV2b4KWD67hNawDmKkndh1Hwa4JWDYmg83iXCrhn4ZDrGMB
whitelistDetails:  {allowed_whitelists: [ 555 ], unrestricted: true}
✓ Update existing outbound whitelists as admin / Should succeed (36756ms)

OpHash:  oooTfhZYiiMQUjPpsTMXXdH6KUUQu6a5a4KDqV2k9BPaha798o6
✓ Remove outbound whitelist as admin / Should succeed (25887ms)

4 passing (4m)
✨  Done in 247.35s.
```

#### Informative entrypoints

```
    yarn test-viewEntryPoints # Test getters entryPoints
```

#### Test Output:

```javascript
Whitelist Smart Contract: Informative Entrypoints tests

viewOpHash :  ooZZfDBazuXBiUsE2Vc3NAfg6f9eWPFdA6RooXqDtguajvpFGiY
Admin from view contract storage:  tz1SVqTz7entj982jDSKcTQNgT7f2cg7C8dk
✓ Get Admin Address / Should succeed (84910ms)

viewOpHash :  oofULds4jJv6DyWw6v2nnh3FCogfgZhNvPwPTyica45zZzQAvzD
Issuer from view contract storage:  tz1SVqTz7entj982jDSKcTQNgT7f2cg7C8dk
✓ Get Issuer Address / Should succeed (16055ms)

viewOpHash :  op2rGJ3L2oczuUnkfshRetWgAkGSj2Zr6nahuGyzbAHfSJ3wNpw
Whitelist details from view contract storage:  { allowed_whitelists: [], unrestricted: true }
✓ Get WhitelistDetails / Should succeed (111324ms)

viewOpHash :  onq4VEiZ6q3SVKp8p1k7fya7auSL2m8WNaaUiphGi6XdJ2hv1NP
whiteListID from view contract storage: 444
✓ Get User whitelist ID / Should succeed (75185ms)

MESSAGE STATEMENT:  whitelist not found
✓ Get inexistent WhitelistDetails / Should fail (2839ms)

MESSAGE STATEMENT:  user not found
✓ Get inexistent User / Should fail (4059ms)

6 passing (5m)
✨  Done in 302.18s.
```

#### Assertion entrypoints

:warning: ***In order to mint some euroTz tokens to the\*\*\*\* ****`Issuer`****, you must launch the\*\*\*\* ****`assertReceiver`**** ****test before the**** ****`assertTrasnfer`**** \*\*\*\*one.***

```
    yarn test-assertReceiver # Test assertReceiver entryPoint
```

#### Test Output:

```javascript
Whitelist Smart Contract: AssertReceiver Entrypoint tests

opHash :  onsWLrfWngr7PGLjYTJWUE5Fn1N2pGcd8uWPXQb1b2G9TCRqLKn
✓ Assert receiver Issuer - Admin mints 50 euroTz to the Issuer / Should succeed (66577ms)

MESSAGE STATEMENT:  user not on a whitelist
✓ Assert receiver inexistent User - Admin tries to mint 40 euroTz to an inexistent user / Should fail (4432ms)

OpHash :  op5bsabpVa8FNszZJWmXi5TpDyYjz3AViovU1J3dMafo3Vha6m7
✓ Admin adds Ouss in users big_map (26387ms)

MESSAGE STATEMENT:  whitelist does not exist
✓ Assert receiver existing User, his associated whitelistID don t refer to an existing whitelist - Admin tries to mint 40 euroTz / Should fail (5517ms)

OpHash :  oodR5jieVi7enfGoVkUZcJdVnep5fyya7DFGQ576rJZzbtSEoAs
✓ Admin sets Ouss s whitelist outbound initally restricted (26856ms)

MESSAGE STATEMENT:  outbound restricted
✓ Assert receiver existing User, his associated whitelist is restricted - Admin tries to mint 40 euroTz to whitelisted restricted user (Ouss) / Should fail (2814ms)

OpHash :  oneyu5hcCPQopgvuQ2KsV4AjJLrNjkh2iKCWi96tuB9VceChcJX
✓ Admin sets Ouss s whitelist outbound unrestricted (106327ms)

opHash :  ooRxZ5HtiEfwtSrMpYgzJ6qWyt6qap87ThpRFP2mNpfjyughTH9
✓ Assert receiver existing User, the associated whitelistID refers to an existing whitelist and unrestricted - Admin mints 50 euroTz to Ouss: whitelisted unrestricted user / Should succeed (26285ms)

8 passing (4m)
✨  Done in 270.13s.
```

```
    yarn test-assertReceivers # Test assertReceivers entryPoint
```

#### Test Output:

```javascript
Whitelist Smart Contract: AssertReceivers Entrypoint tests

MESSAGE STATEMENT:  user not on a whitelist
✓ Assert receivers with one inexistent user / Should fail (3127ms)

OpHash :  ooHWW4VusZTuiDREUuXefX7rLbAQw1PZe38Vz8qJXJDdMTTeCDc
✓ Admin adds Khaled in whitelst contract (75338ms)

MESSAGE STATEMENT:  whitelist does not exist
✓ Assert receivers with one user s whitelistID don t refer to an existing whitelist / Should fail (3524ms)

OpHash:  oo5W1bhrJmmSrv97mc5mS3Ee7Kqgbgu71vxJTYh1JW4jpM7QgZx
✓ Admin sets Khaled s whitelist outbound restricted (26590ms)

MESSAGE STATEMENT:  outbound restricted
✓ Assert receivers with one user s whitelist restricted / Should fail (6387ms)

OpHash :  opZcnwmQvBPiHReqad11vSB3iFZbto4i5aBsUAH1Ck8k23gxCoQ
✓ Admin sets Khaled s whitelist outbound unrestricted (55717ms)

OpHash:  opCXZZmrC4esizqy5trQgTPv3gKv4yE7SHmXMpXRzfgvWcgufV9
✓ Assert receivers: all users are existing and unrestricted / Sould succeed (35641ms)

7 passing (5m)
✨  Done in 324.29s.
```

```
    yarn test-assertTrasnfer # Test assertTrasnfer entryPoint
```

#### Test Output:

```javascript
Whitelist Smart Contract: AssertTransfer Entrypoint tests

MESSAGE STATEMENT:  user not on a whitelist
✓ Assert Transfer from Issuer to an inexistent user - Issuer tries to transfer 9 euroTz to Fred / Should fail (3379ms)

OpHash :  oozZLubPscjt5ed9YaQi61Mfq94xNLAYC6bruTy3u2zHvcZSDcR
✓ Admin adds Fred in whitelst contract (91502ms)

MESSAGE STATEMENT:  whitelist does not exist
✓ Assert Transfer from Issuer to an existing user with whitelistID don t refer to an existing whitelist - Issuer tries to transfer 9 euroTz to Fred / Should fail (5575ms)

OpHash:  ooykfX3jCuioSabk69PV9BpLdxT5BCT7sbJMZB2sRH4sAogk6sW
✓ Admin sets Fred s whitelist outbound initally restricted (18412ms)

MESSAGE STATEMENT:  outbound restricted
✓ Assert Transfer from Issuer to existing, restricted user - Issuer tries to transfer 9 euroTz to Fred / Should fail (7536ms)

OpHash:  opMY1NtPZC9tmJAP2PCZqXQ6jfzKL6S5XDVvJpu6sYNjPHf8qvD
✓ Admin sets Fred s whitelist outbound unrestricted (36973ms)

euroTzTrasnferOpHash:  onxrnAwMs63ZvQKvG5T2GVhohfTuWdVcbKFLq9qQwzPgzTww5tS
✓ Assert Transfer from Issuer to an existing, unrestricted user - Issuer transfers 9 euroTz to Fred / Should succeed (27318ms)

MESSAGE STATEMENT:  user not on a whitelist
✓ Assert Transfer from existing to an inexistent user - Fred tries to transfer 3 euroTz to Safwen / Should fail (4771ms)

OpHash :  ooeXt7h7WrTsWNhFvwM4MJngD4pWb6eoCiLyDw3GmztBG1WkQU3
✓ Admin adds Safwen in whitelst contract (77621ms)

MESSAGE STATEMENT:  whitelist does not exist
✓ Assert Transfer between: Two existing users with sender s whitelistID don t refer to an existing whitelist - Fred tries to transfer 3 euroTz to Safwen / Should fail (3318ms)

OpHash:  op6oNdbhSAKixx795dQrogrDC9hzKYJissrJVBZbVeZ4Ldij8Kf
✓ Admin sets Safwen s whitelist outbound unrestricted (106412ms)

OpHash:  oobQx7dVeYVtTBs9WSSiBBD4onMQPcHcXwCSwc1nJgDqL7a9hU6
✓ Admin sets Fred s whitelist outbound restricted (61054ms)

MESSAGE STATEMENT:  outbound restricted
✓ Assert Transfer between: Two existing users while sender is restricted -  Fred tries to transfer 3 euroTz to Safwen / Should fail (3590ms)

OpHash :  ooYvVeAkfasz18E7F935WYeG1B5Ur9RgjHWhJr4ohqtAoxPtDHD
✓ Admin sets Fred s whitelist outbound unrestricted and contains Safwen (31729ms)

euroTzTrasnferOpHash :  oorXEA6UpHcQhaFYp51sT11SYf6mpDMbvYDXKfwGawbUDEe5fKP
✓ Assert Transfer between: Two existing users, sender and receiver are unrestricted, receiver s whitelistID is in the sender s whitelist - Fred transfers 3 euroTz to Safwen / Should succeed (22084ms)

15 passing (8m)
✨  Done in 507.14s.
```

```
    yarn test-assertTrasnfers # Test assertTrasnfers entryPoint
```

#### Test Output:

```javascript
Whitelist Smart Contract: AssertTrasnfers Operation Testing


MESSAGE STATEMENT:  user not on a whitelist
✓ Assert list of transfers with one inexistent sender / Should fail (4333ms)

MESSAGE STATEMENT:  user not on a whitelist
✓ Assert list of transfers with one inexistent receiver / Should fail (2927ms)

OpHash :  opTmp7RmzQS3BypxzahgsjtzemypbLVXiepgsNGgXHqzJGLwecg
✓ Admin adds Daly to users big_map in whitelist contract (16347ms)

MESSAGE STATEMENT:  whitelist does not exist
✓ Assert list of transfers with one sender s whitelistID don t refer to an existing whitelist / Should fail (3160ms)

OpHash:  oo9HkTbzfvffz8Zi6Avj4rAXqDysC4TtnpcDZt3zEde4vGKRQxU
✓ Admin sets Daly s whitelist outbound restricted (25520ms)

MESSAGE STATEMENT:  outbound restricted
✓ Assert list of transfers with one restricted sender & restricted receiver / Should fail (2629ms)

OpHash:  op5s6LJTAPrBZ5FvDvNz5KC1V3mpHquef2N5BYP5deFhi5c6FLG
✓ Admin sets Daly s whitelist outbound unrestricted (74910ms)

MESSAGE STATEMENT:  outbound not whitelisted
✓ Assert list of transfers with one transfer where the receiver s whitelistID is not in the sender s whitelist / Should fail (2550ms)

OpHash:  opEixE7d7tZF9XUcBP7ob6fSj91ifiYm4g4CBuFe43sszKK1TdP
✓ Admin adds Thib to Daly s whitelist (34924ms)

OpHash:  oo7ss2kbjivZvchJKRUWdyDGa5LTCJ6Ditb5jFUX6h7wj6pCWVS
✓ Assert list of transfers: all senders & receivers are existing and unrestricted, all receivers are whitelisted in the senders' whitelists / Should succeed (2662ms)

10 passing (8m) 
✨  Done in 490.57s.
```

## 👮 Roles:

1. **Admin**: the contract's owner and manager, he can:

   • Set a new contract's `admin`;

   • Set a new contract's `issuer`;

   • Add / Update / Delete any user in the `users` big\_map;

   • Add / Update / Delete any whitelist in the `whitelists` big\_map.
2. **Issuer**:

   • Can't be explicitly added to `users`;

   • Is always `unrestricted`;

   • Whose `allowedWhitelists` is the set of ALL `whitelistId`'s

## 🔗 Implementations

* An implementation of wrapping and non-wrapping forms in Lorentz may be found [here](https://github.com/tqtezos/lorentz-contract-whitelist)
* A partial implementation of the compile-time wrapping form in LIGO may be found [here](https://github.com/tqtezos/ligo-contract-whitelist)


---

# 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.nomadic-labs.com/nomadic-labs-knowledge-center/projects/whitelist-smart-contract.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.
