Whitelisting

Enable addresses, smart contracts, and tokenized assets for transactions in Taurus Protect

Whitelisting ensures that only approved addresses and contracts can interact with the system. Each whitelisting request goes through 4-eyes approval, which means the complete workflow consists of a number of steps:

  1. Authenticate with Protect
  2. Create an address of contract whitelisting request
  3. List pending requests for approval
  4. Sign & approve outstanding whitelisting requests

In the following sections, we walk you through this step by step.

Creating a whitelisted address or asset request

The WhitelistedAddressCreator role is required to create a whitelisting request of any kind.

For the complete documentation on working with addresses and contracts, please consult the respective sections in the API documentation page:

An example request may be created using the example below. These samples assume you have already configured your Protect instance with the appropriate setup for HMAC authentication, and are running these queries against one of the example proxy implementations. Alternatively, you can acquire a Bearer token using a username and password.

export BASEURL=https://localhost:9000
curl --location "$BASEURL/api/rest/v1/whitelists/addresses" \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
  "blockchain": "ETH",
  "address": "0xb794f5ea0ba39494ce839613fffba74279579267",
  "label": "API-Documentation - Test 8"
}'

πŸ‘

Call Result

A successful response for the POST call to create a simple whitelisting request might look like this:

{
    "result": {
        "id": "3711"
    }
}

In this example, Taurus-PROTECT has created a new whitelisting request with a unique ID of 3711. Taurus-PROTECT responds with a JSON object containing the newly assigned request id.

In the next steps we are going to look at how to approve this request.

Listing outstanding requests

Before approving the requests through the API, even if we already know their IDs, we must learn their metadata hash. This property isn't automatically returned by the API on successful creation of a request, and therefore we must enumerate the outstanding requests using a separate API call.

We will work with the following 2 endpoints:

The response from these queries will be similar, and detail everything about the request that is necessary to verify and approve.

{
    "result": [
        {
            "id": "31391",
            "tenantId": "1",
            "signedAddress": {
                "payload": "CgRET0dFGiJEUkJiM1B1M1FSaEZFTmtRR1ZLNHYxWWVreWZUNDQzOEVzKhdPdXRzaWRlIGFkZHJlc3MgTGF1cmVudA=="
            },
            "metadata": {
                "hash": "c7123e130e11ade04dd2913f20f7b75ffb71bf63f2b3f5ca4401388a90ab796b",
                "payload": {
                    "address": "DRBb3Pu3QRhFENkQGVK4v1YekyfT4438Es",
                    "addressType": "individual",
                    "contractType": "",
                    "currency": "DOGE",
                    "customerId": "",
                    "exchangeAccountId": "",
                    "label": "Outside address Laurent",
                    "linkedInternalAddresses": [],
                    "memo": ""
                },
                "payloadAsString": "{\"currency\":\"DOGE\",\"addressType\":\"individual\",\"address\":\"DRBb3Pu3QRhFENkQGVK4v1YekyfT4438Es\",\"memo\":\"\",\"label\":\"Outside address Laurent\",\"customerId\":\"\",\"exchangeAccountId\":\"\",\"linkedInternalAddresses\":[],\"contractType\":\"\"}"
            },
            "action": "create",
            "trails": [
                {
                    "id": "48279",
                    "userId": "11",
                    "externalUserId": "[email protected]",
                    "action": "created",
                    "date": "2025-04-22T07:57:42.689266Z"
                }
            ],
            "rulesContainer": "...",
            "rule": ", approvals = ['team1': 1 sig]",
            "approvers": {
                "parallel": [
                    {
                        "sequential": [
                            {
                                "externalGroupID": "team1",
                                "minimumSignatures": 1
                            }
                        ]
                    },
                    {
                        "sequential": [
                            {
                                "externalGroupID": "Traded",
                                "minimumSignatures": 1
                            }
                        ]
                    }
                ]
            },
            "blockchain": "DOGE",
            "status": "APPROVING",
            "network": "mainnet"
        }
    ],
    "totalItems": "1"
}
{
    "result": [
        {
            "id": "800",
            "tenantId": "1",
            "signedContractAddress": {
                "payload": "CgNFVEgSBFRFU1QaBFRFU1QgBioqMHgxNWNkMTcwMDFmZWZkYjMyYzczN2I0YzVhNzU2ODY1MGRjNmE4ZjEw"
            },
            "metadata": {
                "hash": "fa401332179c30fa52ffd5c352be4fa1e12b3333db9541ef7bd44914b435603f",
                "payload": {
                    "blockchain": "ETH",
                    "contractAddress": "0x15cd17001fefdb32c737b4c5a7568650dc6a8f10",
                    "decimals": "6",
                    "name": "TEST",
                    "symbol": "TEST"
                },
                "payloadAsString": "{\"blockchain\":\"ETH\",\"name\":\"TEST\",\"symbol\":\"TEST\",\"decimals\":\"6\",\"contractAddress\":\"0x15cd17001fefdb32c737b4c5a7568650dc6a8f10\"}"
            },
            "action": "create",
            "trails": [
                {
                    "id": "1961",
                    "userId": "11",
                    "externalUserId": "[email protected]",
                    "action": "created",
                    "date": "2025-04-24T07:40:32.506113Z"
                }
            ],
            "rulesContainer": "...",
            "rule": ", approvals = ['team1': 1 sig|'team1': 1 sig, 'team2': 1 sig]",
            "approvers": {
                "parallel": [
                    {
                        "sequential": [
                            {
                                "externalGroupID": "team1",
                                "minimumSignatures": 1
                            }
                        ]
                    },
                    {
                        "sequential": [
                            {
                                "externalGroupID": "team1",
                                "minimumSignatures": 1
                            },
                            {
                                "externalGroupID": "team2",
                                "minimumSignatures": 1
                            }
                        ]
                    }
                ]
            },
            "status": "APPROVING",
            "blockchain": "ETH",
            "network": "mainnet"
        },
        {
            "id": "799",
            "tenantId": "1",
            "signedContractAddress": {
                "payload": "CgNTT0wSBkthbWlubxoES01OTyAGKitLTU5vM25Kc0JYZmNwSlRWaFpjWExXN1JtVHdUdDRHVkZFN3N1VUJvOXNTOglTT0xfVE9LRU4="
            },
            "metadata": {
                "hash": "b3f8e1b38b7a8871a34d8a5151f394d292e1bd5cbbaddf838d48981bbaf43211",
                "payload": {
                    "blockchain": "SOL",
                    "contractAddress": "KMNo3nJsBXfcpJTVhZcXLW7RmTwTt4GVFE7suUBo9sS",
                    "decimals": "6",
                    "kind": "SOL_TOKEN",
                    "name": "Kamino",
                    "symbol": "KMNO"
                },
                "payloadAsString": "{\"blockchain\":\"SOL\",\"name\":\"Kamino\",\"symbol\":\"KMNO\",\"decimals\":\"6\",\"contractAddress\":\"KMNo3nJsBXfcpJTVhZcXLW7RmTwTt4GVFE7suUBo9sS\",\"kind\":\"SOL_TOKEN\"}"
            },
            "action": "create",
            "trails": [
                {
                    "id": "1960",
                    "userId": "11",
                    "externalUserId": "[email protected]",
                    "action": "created",
                    "date": "2025-04-22T14:25:37.647573Z"
                }
            ],
            "rulesContainer": "...",
            "rule": ", approvals = ['team1': 1 sig]",
            "approvers": {
                "parallel": [
                    {
                        "sequential": [
                            {
                                "externalGroupID": "team1",
                                "minimumSignatures": 1
                            }
                        ]
                    }
                ]
            },
            "status": "APPROVING",
            "blockchain": "SOL",
            "network": "mainnet"
        }
    ],
    "totalItems": "2"
}

Signing the request

Signing the request involves using the ECDSA-SHA256 signature scheme. Please refer to the Signing for approval page in the documentation for a tutorial on producing a valid signature.

The signature looks something like this: I0xWyXI3L33DMZAKvvKXnlWD3GGXWh0AuL5zHD12DGp2/pR2h2MbgiQZrNGB27s/iB938UgMK1xUhbm1fNx8nw==. You will also need the list of IDs of the requests that you are approving.

Approving through the API

β˜‘οΈ

Approvals require a different role from creation

For submitting the approval, the WhitelistedAddressApprover role will be necessary.

To approve whitelisting requests, you will need to use 2 separate endpoints depending on whether you are looking to whitelist an asset (smart contract), or an address for transactions:

Approvals all use the same JSON schema for the POST request:

{
  "comment": "mandatory comment to describe the action",
  "ids": ["1234", "2345"],
  "signature": "I0xWyXI3L33DMZAKvvKXnlWD3GGXWh0AuL5zHD12DGp2/pR2h2MbgiQZrNGB27s/iB938UgMK1xUhbm1fNx8nw=="
}

At this point, all data should be available for creating the API call:

  • Comment is a free-form field, and typically you will want to use some internal reference or verifiable information that corresponds to the approval
  • We prepared the ids sorted list in the "Prepare the Payload" section above.
  • The signature field is the Base64 encoded signature created in the previous section.

Congratulations! You have successfully approved some whitelisting requests in Taurus Protect!




  Β© 2025 Taurus SA. All rights reserved.