# 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)
