# On-going Monitoring

For a subset of attributes (ex: AML), the issuer is responsible to perform [on-going monitoring](/passport-issuer/integration-developers/on-going-monitoring.md) and update the attribute values directly in the passport.

{% hint style="info" %}
Only specific issuers are required to perform on-going monitoring.
{% endhint %}

### Example (change of AML score)

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

```javascript
import { ethers } from 'ethers';
import { BytesLike } from '@ethersproject/bytes';

const newAMLScore = hexZeroPad("0x05", 32); // new AML score
const accountToUpdate = "0x......."; // Wallet of the Passport holder to update
const issuer = new Wallet(ISSUER_PRIVATE_KEY); // Loading issuer account from their private key
const attrTypes = [utils.id("AML")];  // List of attributes types
const attrKeys = computeAttrKeys(accountAddress, attrTypes); // List of attributes Identifiers
const attrValues = [newAMLScore]; // List of attributes values
const verifiedAt = Math.floor(new Date().getTime() / 1000) - 60; // we substract 60 seconds to avoid errors during minting passport with long block time
const issuedAt = Math.floor(new Date().getTime() / 1000) - 60; // we substract 60 seconds to avoid errors during minting passport with long block time
const passportAddress = "0x2e779749c40CC4Ba1cAB4c57eF84d90755CC017d";
const chainId = 1; // 1 for Ethereum Mainnet
const fee = utils.parseEther("0"); // No fee to paid as this is an update performed by the issuer themselves
const did = ethers.constants.HashZero; // Leave empty
const signature = await signAttributes(
  accountAddress,
  issuer,
  attrKeys,
  attrValues,
  verifiedAt,
  issuedAt, 
  fee,
  passportAddress,
  chainId
)

const QUAD_PASSPORT_ABI = ''; // Can be retrieve at https://etherscan.io/address/0xebf9b4db6050517e402c578237326b793bf348ff#code

const passportContract = new ethers.Contract(
    passportAddress,
    QUAD_PASSPORT_ABI,
);

const tx = await passportContract.setAttributesIssuer(
    accountAddress,
    [
      attrKeys,
      attrValues,
      attrTypes,
      ethers.constants.HashZero, // Leave bytes32(0)
      0, // Leave 0
      verifiedAt,
      issuedAt,
      fee,
    ],
    sigIssuer,
    {
      value: fee,
    }
  )
)

await tx.wait();

export const signAttributes = async (
  accountAddress: string,
  issuer: typeof Signer,
  attrKeys: BytesLike[],
  attrValues: BytesLike[],
  verifiedAt: number,
  issuedAt: number,
  fee: any,
  passportAddress: string,
  chainId: number,
  did: BytesLike = utils.constants.HashZero,
): Promise<typeof DataHexString> => {
  const attrKeys: string[] = [];
  const attrValues: string[] = [];

  const hash = utils.keccak256(
    utils.defaultAbiCoder.encode(
      [
        "address",
        "bytes32[]",
        "bytes32[]",
        "bytes32",
        "uint256",
        "uint256",
        "uint256",
        "uint256",
        "address",
      ],
      [
        accountAddress,
        attrKeys,
        attrValues,
        did,
        verifiedAt,
        issuedAt,
        fee,
        chainId,
        passportAddress,
      ]
    )
  );

  const sig = await issuer.signMessage(utils.arrayify(hash));

  return sig;
};
`
```

{% endtab %}

{% tab title="Python" %}

```python
W3_TIMEOUT = 60
ABI_PATH = "" # DIRECTORY CONTAINING ABI FILE
PASSPORT_ABI_FILE_NAME = "" # ABI FILE NAME
NETWORK_URI = "" # INFURA_RPC_NODE
PASSPORT_CONTRACT_ADDRESS = "" # PASSPORT ADDRESS
CHAIN_ID = 1 # CHAIN ID WHERE ATTESTATION WILL BE POSTED
GAS_LIMIT = 300000
ISSUER_PRIVATE_KEY = "" # ISSUER__PRIVATE_KEY
HEX_PADDING = 66

@dataclass(frozen=True)
class UpdateCreditScoreInputs:
    target_address: str
    token_id: int
    new_value: str
    verified_at: int
    quad_did: str
    issued_at: int = blockchain_timestamp_seconds()
    # Update fee should be 0 so issuer does not have to pay for update
    update_fee: int = 0

    def attr_keys(self) -> List[bytes]:
        # Need to modify CREDIT_SCORE to attribute type 
        return [Web3.solidityKeccak(['bytes32', 'bytes32'], [self.target_address, keccak('CREDIT_SCORE')])]

    def attr_values(self) -> List[bytes]:
        return [self.hexlified_value()]

    def attr_types(self) -> List[str]:
        return [keccak('CREDIT_SCORE')]

    def hexlified_value(self) -> bytes:
        return bytes.fromhex(f'{self.new_value:#0{HEX_PADDING}x}'[2:])

    def attribute_setter_config(self) -> tuple:
        return (
            [key.hex() for key in self.attr_keys()],
            [f'0x{val.hex()}' for val in self.attr_values()],
            self.attr_types(),
            self.quad_did,
            self.token_id,
            self.verified_at,
            self.issued_at,
            self.update_fee,
        )

    def update_message(self, chain_id) -> str:
        abiEncoded = eth_abi.encode_abi(
            [
                'bytes32',
                'bytes32[]',
                'bytes32[]',
                'bytes32',
                'uint256',
                'uint256',
                'uint256',
                'uint256',
                'bytes32',
            ],
            [
                self.target_address,
                self.attr_keys(),
                self.attr_values(),
                bytes.fromhex(self.quad_did[2:]),
                self.verified_at,
                self.issued_at,
                self.update_fee,
                chain_id,
                bytes.fromhex(Web3.toChecksumAddress(PASSPORT_CONTRACT_ADDRESS)[2:]).rjust(
                    32, b'\0'
                ),
            ],
        )
        return Web3.solidityKeccak(['bytes32'], [('0x' + abiEncoded.hex())]).hex()

def keccak(value: str) -> str:
    if not value:
        raise ValueError('Attempted to keccak hash an empty string')
    return Web3.solidityKeccak(['bytes32'], [value.encode()]).hex()


def _load_passport_abi(self) -> str:
    with open(os.path.join(ABI_PATH, PASSPORT_ABI_FILE_NAME)) as f:
        return json.load(f)

update_inputs = UpdateCreditScoreInputs(
    target_address = '', # target wallet address for credit score update
    token_id = 1, # configurable passport skin
    new_value = '',# new value
    verified_at = int(datetime.now(timezone.utc).timestamp()) - 60,
    quad_did = web3.constants.HASH_ZERO,
    issued_at = int(datetime.now(timezone.utc).timestamp()) - 60,
    update_fee = 0 # Update fee should be 0 so issuer does not have to pay for update
)

w3 = Web3(Web3.HTTPProvider(NETWORK_URI, request_kwargs={'timeout': W3_TIMEOUT}))
contract = w3.eth.contract(
    address=Web3.toChecksumAddress(PASSPORT_CONTRACT_ADDRESS),
    abi=self._load_passport_abi(),
)

# Assumes no pending transaction
nonce = w3.eth.getTransactionCount(
    Web3.toChecksumAddress(
        w3.eth.account.privateKeyToAccount(ISSUER_PRIVATE_KEY).address))


transaction = contract.functions.setAttributesIssuer(
    Web3.toChecksumAddress(update_inputs.target_address),
    update_inputs.attribute_setter_config(),
    update_inputs.update_message(CHAIN_ID),
).buildTransaction(
    {
        'gasPrice': w3.eth.gas_price,
        'gas': GAS_LIMIT, 
        'nonce': nonce,
        'chainId': CHAIN_ID,
    }
)
decoder = codecs.getdecoder('hex_codec')
signed_tx = self.w3.eth.account.signTransaction(
    transaction, private_key=decoder(str.encode(ISSUER_PRIVATE_KEY))[0]
)
tx_hash = self.w3.eth.sendRawTransaction(signed_tx.rawTransaction)
```

{% endtab %}
{% endtabs %}


---

# 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.quadrata.com/passport-issuer/integration-developers/on-going-monitoring.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.
