# 3-Multi Users with Permission Sharing

In this scenario, Alice and Bob are traders who possess sensitive transaction volume information for various wallets. They need to securely store this data and share it with Charlie, who is responsible for providing settlement services. To ensure data privacy and security, Alice and Bob choose to encrypt the transaction data before storing it in their respective tables.

{% hint style="info" %}
This use case will use multiple wallet address. During testing period, please make you have all wallet address are registered in into [whitelist](https://sites.google.com/mindnetwork.xyz/mindnetwork/alpha-test).

Be more specific, please ensure your Alice, Bob and Charlie are registered and approved.&#x20;
{% endhint %}

## 3.1. Task

1. Alice and Bob are two traders who possess transaction volume information for several wallets. They will encrypt this data and insert it into their respective tables.
2. Charlie is responsible for providing settlement services. To facilitate this, Alice and Bob grant Charlie permission to access their tables.
3. Charlie then executes a query, aggregates the data, and calculates the total volume for each wallet.

## 3.2. Data

### Alice

<table><thead><tr><th width="461">Wallet Address(text)</th><th width="136">Token (text)</th><th width="152">Volume (float4)</th></tr></thead><tbody><tr><td>(secret data)</td><td>(secret data)</td><td>(secret data)</td></tr><tr><td>0x8CFB38b2cba74757431B205612E349B8b9a9E661</td><td>USDT</td><td>5.6</td></tr><tr><td>0xD862D48f36ce6298eFD00474eC852b8838a54F66</td><td>BUSD</td><td>6.3</td></tr><tr><td>0x8CFB38b2cba74757431B205612E349B8b9a9E661</td><td>BUSD</td><td>10.3</td></tr></tbody></table>

### Bob

<table><thead><tr><th width="470">Wallet Address(text)</th><th width="131">Token (text)</th><th width="152">Volume (float4)</th></tr></thead><tbody><tr><td>(secret data)</td><td>(secret data)</td><td>(secret data)</td></tr><tr><td>0xD862D48f36ce6298eFD00474eC852b8838a54F66</td><td>USDT</td><td>3.3</td></tr><tr><td>0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770</td><td>BUSD</td><td>9.8</td></tr><tr><td>0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770</td><td>USDT</td><td>7.7</td></tr></tbody></table>

## 3.3. Example code to Insert cipher and grant permission

{% tabs %}
{% tab title="Typescript" %}

```typescript
// ATTENTION: 
// This use case will use multiple wallet address. 
// During testing period, please make you have all wallet address are registered in into whitelist: https://sites.google.com/mindnetwork.xyz/mindnetwork/alpha-test
// Be more specific, please ensure your Alice, Bob and Charlie are registered and approved. 

import {MindLake} from 'mind-lake-sdk';

const DataType = MindLake.DataType;

const dataAlice = [
  { WalletAddress: "0x8CFB38b2cba74757431B205612E349B8b9a9E661", Token: 'USDT', Volume: "5.6" },
  { WalletAddress: "0xD862D48f36ce6298eFD00474eC852b8838a54F66", Token: 'BUSD', Volume: "6.3" },
  { WalletAddress: "0x8CFB38b2cba74757431B205612E349B8b9a9E661", Token: 'BUSD', Volume: "10.3" },
];

const dataBob = [
  {'wallet': '0xD862D48f36ce6298eFD00474eC852b8838a54F66', 'token': 'USDT', 'volume': 3.3},
  {'wallet': '0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770', 'token': 'BUSD', 'volume': 9.8},
  {'wallet': '0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770', 'token': 'USDT', 'volume': 7.7}
];

const insertData = async (data: Array<any>) => {
const mindLake = await MindLake.getInstance("YOU OWN APP KEY");

// connect to MindLake."5" is example of Goerli Testnet chainId
const chainId = "5"
const res1 = await mindLake.connect(chainId);
if(res1.code !== 0) {
  console.error(res1.message);
  return
}

// create a table
const dataLake = mindLake.dataLake;
await dataLake.dropTable("transaction");
const res2 = await dataLake.createTable("transaction", [{columnName: 'WalletAddress', type: DataType.text, encrypt: false}, {columnName: 'Token', type: DataType.text, encrypt: true}, {columnName: 'Volume', type: DataType.float4, encrypt: true}],);
if(res2.code !==0) {
  console.error(res2.message);
  return
}

// encrypt data
const cryptor = mindLake.cryptor;
for (const row of data) {
  const walletAddress = row.WalletAddress;
  const encryptToken = await cryptor.encrypt(row.Token, "transaction.Token");
  const encryptVolume = await cryptor.encrypt(row.Volume, "transaction.Volume");
  const sql = `insert into transaction ("WalletAddress", "Token", "Volume") values ('${walletAddress}', '${encryptToken.result}', '${encryptVolume.result}')`;
  const sqlRes = await dataLake.query(sql);
  if(sqlRes.code !== 0) {
    return console.error(sqlRes.message)
  }
}
const permission = mindLake.permission;
const result = await permission.grant(chainId,"charlieWalletAddress", ['transaction.Token', 'transaction.Volume'])
if(result.code !==0 ) {
  console.error(result.message);
  return
}
};

const excute = async () => {
  await insertData(dataAlice);
  await insertData(dataBob);
}
```

{% endtab %}

{% tab title="Python" %}

```python
import env
import mindlakesdk
import logging

# the policy grant target should be an existing user in MindLake, so first register Charlie
# '5' is example of Goerli Testnet chainID
chainID = '5'
mindlake = mindlakesdk.connect(env.walletPrivateKeyCharlie, env.appKey, chainID)
assert mindlake, mindlake.message

def writeDataGrantToCharlie(walletPrivateKey, appKey, data) -> str:
    # connect to MindLake
    mindlake = mindlakesdk.connect(walletPrivateKey, appKey, chainID)
    assert mindlake, mindlake.message

    # create a table
    result = mindlake.datalake.createTable('transaction_temp',
            [
                mindlake.datalake.Column('WalletAddress', mindlake.DataType.text, True),
                mindlake.datalake.Column('Token', mindlake.DataType.text, True),
                mindlake.datalake.Column('Volume', mindlake.DataType.float4, True)
            ])
    assert result, result.message

    # encrypt and insert
    for row in data:
        result = mindlake.datalake.query(f"""
        INSERT INTO "transaction_temp" ("WalletAddress", "Token", "Volume")
        VALUES (
            '{mindlake.cryptor.encrypt(row["wallet"],'transaction_temp.WalletAddress').data}',
            '{mindlake.cryptor.encrypt(row["token"],'transaction_temp.Token').data}',
            '{mindlake.cryptor.encrypt(row["volume"],'transaction_temp.Volume').data}')
        """)
        assert result, result.message

    result = mindlake.permission.grant(env.walletAddressCharlie,
        ['transaction_temp.WalletAddress', 'transaction_temp.Token', 'transaction_temp.Volume'])
    assert result, result.message
    policyID = result.data
    return policyID

# Alice write data to table and grant permission to Charlie    
policyIDAlice = writeDataGrantToCharlie(env.walletPrivateKeyAlice, env.appKey, [
    {'wallet': '0x8CFB38b2cba74757431B205612E349B8b9a9E661', 'token': 'USDT', 'volume': 5.6},
    {'wallet': '0xD862D48f36ce6298eFD00474eC852b8838a54F66', 'token': 'BUSD', 'volume': 6.3},
    {'wallet': '0x8CFB38b2cba74757431B205612E349B8b9a9E661', 'token': 'BUSD', 'volume': 10.3}
])

# Bob write data to table and grant permission to Charlie
policyIDBob = writeDataGrantToCharlie(env.walletPrivateKeyBob, env.appKey, [
    {'wallet': '0xD862D48f36ce6298eFD00474eC852b8838a54F66', 'token': 'USDT', 'volume': 3.3},
    {'wallet': '0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770', 'token': 'BUSD', 'volume': 9.8},
    {'wallet': '0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770', 'token': 'USDT', 'volume': 7.7}
])
```

{% endtab %}
{% endtabs %}

## 3.4. Result Table in MindLake

### Alice

<table><thead><tr><th width="464">Wallet Address</th><th width="145">Token</th><th width="142">Volume</th></tr></thead><tbody><tr><td>(text, encrypt=False)</td><td>(text, encrypt=True)</td><td>(float4, encrypt=True)</td></tr><tr><td>0x8CFB38b2cba74757431B205612E349B8b9a9E661</td><td>0x1111</td><td>0x2211</td></tr><tr><td>0xD862D48f36ce6298eFD00474eC852b8838a54F66</td><td>0x1122</td><td>0x2222</td></tr><tr><td>0x8CFB38b2cba74757431B205612E349B8b9a9E661</td><td>0x1133</td><td>0x2233</td></tr></tbody></table>

### Bob

<table><thead><tr><th width="469">Wallet Address</th><th width="145">Token</th><th width="142">Volume</th></tr></thead><tbody><tr><td>(text, encrypt=False)</td><td>(text, encrypt=True)</td><td>(float4, encrypt=True)</td></tr><tr><td>0xD862D48f36ce6298eFD00474eC852b8838a54F66</td><td>0x1144</td><td>0x2244</td></tr><tr><td>0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770</td><td>0x1155</td><td>0x2255</td></tr><tr><td>0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770</td><td>0x1166</td><td>0x2266</td></tr></tbody></table>

## 3.5. Example code to confirm permission and calculate on cipher

{% tabs %}
{% tab title="TypeScript" %}

```typescript
const mindLake = await MindLake.getInstance("YOU OWN APP KEY");
// Charlie connect to MindLake. "5" is example of Goerli Testnet chainId
const chainId = "5"
let res = await mindLake.connect(chainId);
if(res.code !== 0) {
  return console.error(res.message);
}
const permission =  mindLake.permission;
const dataLake =  mindLake.dataLake;
const cryptor = mindLake.cryptor;
res = await permission.confirm("policyAliceID");
if(res.code !== 0) {
  return console.error(res.message);
}
res = await permission.confirm("policyBobID");
if(res.code !== 0) {
  return console.error(res.message);
}
const sql = `SELECT combine."WalletAddress", SUM(combine."Volume") FROM(SELECT "WalletAddress","Volume" FROM "${aliceWalletAddress.toLocaleLowerCase()}"."transaction"UNION ALLSELECT "WalletAddress","Volume" FROM "${bobWalletAddress.toLocaleLowerCase()}"."transaction") as combineGROUP BY "WalletAddress"`;
res = await dataLake.query(sql);
if(res.code !== 0) {
  return console.error(res.message);
}
for (const row of res.result.data) {
  const walletAddress = row[0];
  res = await cryptor.decrypt(row[1]);
  if(res.code !== 0) {
    return console.error(res.message);
  }
  console.log(`${walletAddress} >>> `, res.result)
}
```

{% endtab %}

{% tab title="Python" %}

```python
# Charlie confirm the permission
# '5' is example of Goerli Testnet chainID
chainID = '5'
mindlake = mindlakesdk.connect(env.walletPrivateKeyCharlie, env.appKey, chainID)
assert mindlake, mindlake.message
result = mindlake.permission.confirm(policyIDAlice)
assert result, result.message
result = mindlake.permission.confirm(policyIDBob)
assert result, result.message

# Charlie query and calculate the total volume of each wallet
result = mindlake.datalake.query(f'''
SELECT combine."WalletAddress", SUM(combine."Volume") FROM
(SELECT "WalletAddress","Volume" FROM "{env.walletAddressAlice[2:].lower()}"."transaction_temp"
UNION ALL
SELECT "WalletAddress","Volume" FROM "{env.walletAddressBob[2:].lower()}"."transaction_temp") as combine
GROUP BY "WalletAddress"
''')
assert result, result.message

print('-'*57)
print('|', result.data["columnList"][0], " "*28, '|', result.data["columnList"][1], '\t|')
print('-'*57)
for row in result.data['data']:
    result = mindlake.cryptor.decrypt(row[0])
    assert result, result.message
    walletAddress = result.data
    result = mindlake.cryptor.decrypt(row[1])
    assert result, result.message
    sumVolume = result.data
    print(f'| {walletAddress} | {sumVolume:.1f}\t|')
print('-'*57)
```

{% endtab %}
{% endtabs %}

## 3.6. Output

```
---------------------------------------------------------
| WalletAddress                              | sum      |
---------------------------------------------------------
| 0xD862D48f36ce6298eFD00474eC852b8838a54F66 | 9.6      |
| 0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770 | 17.5     |
| 0x8CFB38b2cba74757431B205612E349B8b9a9E661 | 15.9     |
---------------------------------------------------------
```
