Transactions

Create and approve transactions in Taurus Protect

Creating a transfer in Taurus Protect for any currency or asset will follow the same few steps. Before you get started, make sure your Protect system has the appropriate transaction rules in place and signed by superadministrators for each currency or asset that you are transferring. Additionally, external transfers will require you to have the correct address whitelisted and approved in the system.

Once your governance rules and whitelisted addresses are in place, follow this document to go through the process:

  1. Authenticate with Protect
  2. Create a transaction request
  3. List pending requests for approval
  4. Sign & approve outstanding transaction requests

Creating a transaction request

The RequestCreator role is required to create a transaction request for any currency.

In this section, we will create a transfer to a whitelisted address . Taurus Protect abstracts away the use of currencies through wallets and addresses, so you can be sure that whenever you transact to and from a particular address, it will always be done in the currency you intended. Therefore, this endpoint consistently uses internal IDs to create the transaction request, and real blockchain addresses do not appear in the payload.

πŸ‘

Chain specific endpoints

Some blockchains will have additional parameters that apply to transaction requests. Please consult the relevant section in the API reference for more details on these.

Which endpoints to use for creating transfers will depend on your use case. If in doubt, reach out to our support team to get advice on the best tool for your problem at hand.

An example request may be created using the code 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/requests/outgoing" \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
  "fromAddressId": "441980",
  "amount": "10000000000000000",
  "toWhitelistedAddressId": "31350",
  "comment": "An optional comment can be included",
}'

πŸ‘

Call Result

A successful response for the POST call will include the details of the created transaction request in the result object. For full reference of response fields, please look at the API documentation .

{
    "result": {
        "id": "7426452",
        "tenantId": "1",
        "currency": "ETH",
        "envelope": "Cgc3NDI2NDUyEInXp8z6nc6cGBgFKskHCAkSfhJ8ChJtLzQ0Jy82MCcvMCcvMCcvMCcYiKQBKioweDgxNDY0NWE3NWIyMGMxNmUyZDAzN2U5MjVlNDk1YTI2MDBhNjhmYTQyByOG8m/BAABCKzQ0MTk4MDo4NWIwYTdmZC0zZDZhLTRlODktYjBkZC0yNzExMTBkM2ZiODZQASIXCgpyZXF1ZXN0X2lkEAEaBzc0MjY0NTIiEgoJcnVsZXNfa2V5EAEaA0VUSCIRCghjdXJyZW5jeRABGgNFVEgiIQoNY3VycmVuY3lfbmFtZRABGg5FdGhlcmV1bSAtIEVUSCJRCgtjdXJyZW5jeV9pZBABGkAzM2E3ZGU2YWY1MDU4NTVkMzA2MjcxNWFjNDYwOTczNmE2MGNmMjc3OWYwM2NkMDMzZTc0NTkwNzA4YzRmMGQ0InQKBnNvdXJjZRAEGmAIARJcCPz8GhIqMHgxMTAxYzQ3NjMwMjc1MDM0YTlhMTFlZTg4Yzk3MjcxODE3NDcwNDRmGhZub3N0cm8gYWRkcmVzcyBmb3IgRVRIIhJtLzQ0Jy82MCcvMCcvMCcvMCciBnNvdXJjZSJjCgtkZXN0aW5hdGlvbhAFGkUIARJBCPb0ARIqMHg4MTQ2NDVhNzViMjBjMTZlMmQwMzdlOTI1ZTQ5NWEyNjAwYTY4ZmE0Gg9TUUwgU2VydmVyIGFkZHIiC2Rlc3RpbmF0aW9uIqgBCgZhbW91bnQQChqTAQoHI4byb8EAABIHMTQuMzcyMBoQMTQzNy4yMDQ5MjUwODk1NiASKgNFVEgyA0NIRjphCh12YWxpZGF0b3ItY29yZUB0YXVydXNncm91cC5jaBJAqgMqxIZNsS0Rk/x8qDvtYp1c4+n+OYTafSIco3vVhK0evKo/lckcXTAPlPQQsRmGiHVG5EhpZLlBD+pkgQ9YayIGYW1vdW50Ih4KEXRvdGFsX2ZpYXRfYW1vdW50EAEaBzE0LjM3MjAiGgoPZ2FzX3ByaWNlX2xpbWl0EAIaBQG4zrRoIhQKCmdhc19saW1pdHMQAxoECgJSCCISCgdwYXlsb2FkIgdwYXlsb2FkIh8KFGZlZV9wYWlkX2J5X3JlY2VpdmVyEAEaBWZhbHNlIiAKFXVzZV91bmNvbmZpcm1lZF9mdW5kcxABGgVmYWxzZSIcChFpc19jYW5jZWxfcmVxdWVzdBABGgVmYWxzZSJCCg90cmFuc2FjdGlvbl9pZHMQBhotCis0NDE5ODA6ODViMGE3ZmQtM2Q2YS00ZTg5LWIwZGQtMjcxMTEwZDNmYjg2OgdtYWlubmV0",
        "status": "CREATED",
        "trails": [
            {
                "userId": "11",
                "externalUserId": "[email protected]",
                "action": "created",
                "requestStatus": "CREATED"
            },
            {
                "userId": "1",
                "externalUserId": "[email protected]",
                "action": "approvers_assigned",
                "comment": "rule = [source: m/44'/60'/0', destination: <binary>, amount: <binary>, payload: <binary>], approvals = ['team1': 1 sig]",
                "requestStatus": "CREATED"
            }
        ],
        "metadata": {
            "hash": "f1cca186eb53015be7774b6b5b20ec8e9aacbc0be3b8bde8084e997e8c5987aa",
            "payload": [
                {
                    "column": "",
                    "key": "request_id",
                    "type": "String",
                    "value": "7426452"
                },
                {
                    "column": "",
                    "key": "rules_key",
                    "type": "String",
                    "value": "ETH"
                },
                {
                    "column": "",
                    "key": "currency",
                    "type": "String",
                    "value": "ETH"
                },
                {
                    "column": "",
                    "key": "currency_name",
                    "type": "String",
                    "value": "Ethereum - ETH"
                },
                {
                    "column": "",
                    "key": "currency_id",
                    "type": "String",
                    "value": "33a7de6af505855d3062715ac4609736a60cf2779f03cd033e74590708c4f0d4"
                },
                {
                    "column": "source",
                    "key": "source",
                    "type": "Source",
                    "value": {
                        "payload": {
                            "address": "0x1101c47630275034a9a11ee88c9727181747044f",
                            "id": "441980",
                            "label": "nostro address for ETH",
                            "path": "m/44'/60'/0'/0'/0'"
                        },
                        "type": "SourceInternalAddress"
                    }
                },
                {
                    "column": "destination",
                    "key": "destination",
                    "type": "Destination",
                    "value": {
                        "payload": {
                            "address": "0x814645a75b20c16e2d037e925e495a2600a68fa4",
                            "id": "31350",
                            "label": "SQL Server addr",
                            "memo": ""
                        },
                        "type": "DestinationExternalAddress"
                    }
                },
                {
                    "column": "amount",
                    "key": "amount",
                    "type": "Amount",
                    "value": {
                        "currencyFrom": "ETH",
                        "currencyTo": "CHF",
                        "decimals": "18",
                        "rate": "1437.20492508956",
                        "valueFrom": "10000000000000000",
                        "valueTo": "14.3720"
                    }
                },
                {
                    "column": "",
                    "key": "total_fiat_amount",
                    "type": "String",
                    "value": "14.3720"
                },
                {
                    "column": "",
                    "key": "gas_price_limit",
                    "type": "BigInt",
                    "value": "7395521640"
                },
                {
                    "column": "",
                    "key": "gas_limits",
                    "type": "BigIntArray",
                    "value": [
                        "21000"
                    ]
                },
                {
                    "column": "payload",
                    "key": "payload",
                    "type": "Bytes",
                    "value": ""
                },
                {
                    "column": "",
                    "key": "fee_paid_by_receiver",
                    "type": "String",
                    "value": "false"
                },
                {
                    "column": "",
                    "key": "use_unconfirmed_funds",
                    "type": "String",
                    "value": "false"
                },
                {
                    "column": "",
                    "key": "is_cancel_request",
                    "type": "String",
                    "value": "false"
                },
                {
                    "column": "",
                    "key": "transaction_ids",
                    "type": "StringArray",
                    "value": [
                        "441980:85b0a7fd-3d6a-4e89-b0dd-271110d3fb86"
                    ]
                }
            ],
            "payloadAsString": "[{\"key\":\"request_id\",\"type\":\"String\",\"value\":\"7426452\",\"column\":\"\"},{\"key\":\"rules_key\",\"type\":\"String\",\"value\":\"ETH\",\"column\":\"\"},{\"key\":\"currency\",\"type\":\"String\",\"value\":\"ETH\",\"column\":\"\"},{\"key\":\"currency_name\",\"type\":\"String\",\"value\":\"Ethereum - ETH\",\"column\":\"\"},{\"key\":\"currency_id\",\"type\":\"String\",\"value\":\"33a7de6af505855d3062715ac4609736a60cf2779f03cd033e74590708c4f0d4\",\"column\":\"\"},{\"key\":\"source\",\"type\":\"Source\",\"value\":{\"type\":\"SourceInternalAddress\",\"payload\":{\"id\":\"441980\",\"address\":\"0x1101c47630275034a9a11ee88c9727181747044f\",\"label\":\"nostro address for ETH\",\"path\":\"m/44'/60'/0'/0'/0'\"}},\"column\":\"source\"},{\"key\":\"destination\",\"type\":\"Destination\",\"value\":{\"type\":\"DestinationExternalAddress\",\"payload\":{\"id\":\"31350\",\"address\":\"0x814645a75b20c16e2d037e925e495a2600a68fa4\",\"label\":\"SQL Server addr\",\"memo\":\"\"}},\"column\":\"destination\"},{\"key\":\"amount\",\"type\":\"Amount\",\"value\":{\"valueFrom\":\"10000000000000000\",\"valueTo\":\"14.3720\",\"rate\":\"1437.20492508956\",\"decimals\":\"18\",\"currencyFrom\":\"ETH\",\"currencyTo\":\"CHF\"},\"column\":\"amount\"},{\"key\":\"total_fiat_amount\",\"type\":\"String\",\"value\":\"14.3720\",\"column\":\"\"},{\"key\":\"gas_price_limit\",\"type\":\"BigInt\",\"value\":\"7395521640\",\"column\":\"\"},{\"key\":\"gas_limits\",\"type\":\"BigIntArray\",\"value\":[\"21000\"],\"column\":\"\"},{\"key\":\"payload\",\"type\":\"Bytes\",\"value\":\"\",\"column\":\"payload\"},{\"key\":\"fee_paid_by_receiver\",\"type\":\"String\",\"value\":\"false\",\"column\":\"\"},{\"key\":\"use_unconfirmed_funds\",\"type\":\"String\",\"value\":\"false\",\"column\":\"\"},{\"key\":\"is_cancel_request\",\"type\":\"String\",\"value\":\"false\",\"column\":\"\"},{\"key\":\"transaction_ids\",\"type\":\"StringArray\",\"value\":[\"441980:85b0a7fd-3d6a-4e89-b0dd-271110d3fb86\"],\"column\":\"\"}]"
        },
        "rule": "rule = [source: m/44'/60'/0', destination: <binary>, amount: <binary>, payload: <binary>], approvals = ['team1': 1 sig]",
        "approvers": {
            "parallel": [
                {
                    "sequential": [
                        {
                            "externalGroupID": "team1",
                            "minimumSignatures": 1
                        }
                    ]
                }
            ]
        },
        "type": "payment",
        "currencyInfo": {
            "name": "Ethereum",
            "symbol": "ETH",
            "coinTypeIndex": "60",
            "blockchain": "ETH",
            "decimals": "18",
            "isAccountBased": true,
            "enabled": true,
            "id": "33a7de6af505855d3062715ac4609736a60cf2779f03cd033e74590708c4f0d4",
            "displayName": "Ethereum - ETH",
            "type": "native",
            "network": "mainnet"
        }
    }
}

In this example, Taurus-PROTECT has created a new transaction request with a unique ID of 7426452.

In the next steps we are going to look at how to approve this request. One thing to note however, is that this response body already has everything you need to approve it:

  • the result.id of the newly created request
  • the result.metadata.hash property of the response.

In the following section you will see how to retrieve all pending requests in case you want to approve multiple transactions using the same API call.

Listing outstanding requests

Before approving the requests through the API, even if we already know their IDs, we must learn their metadata hash. To approve all outstanding transactions requests, it is easiest to ask Taurus Protect to list these and use the API response to produce the necessary approvals.

We will work with the following endpoint:

The response from this query will be familiar, and detail everything about all the outstanding request that is necessary to verify and approve. Note that this endpoint will return all requests, including transfers, pledges, staking, or any other transaction that happens on-chain or on the Taurus Network.

{
    "result": [
      {
        "id": "7426452",
        "tenantId": "1",
        "currency": "ETH",
        "envelope": "Cgc3NDI2NDUyEInXp8z6nc6cGBgFKskHCAkSfhJ8ChJtLzQ0Jy82MCcvMCcvMCcvMCcYiKQBKioweDgxNDY0NWE3NWIyMGMxNmUyZDAzN2U5MjVlNDk1YTI2MDBhNjhmYTQyByOG8m/BAABCKzQ0MTk4MDo4NWIwYTdmZC0zZDZhLTRlODktYjBkZC0yNzExMTBkM2ZiODZQASIXCgpyZXF1ZXN0X2lkEAEaBzc0MjY0NTIiEgoJcnVsZXNfa2V5EAEaA0VUSCIRCghjdXJyZW5jeRABGgNFVEgiIQoNY3VycmVuY3lfbmFtZRABGg5FdGhlcmV1bSAtIEVUSCJRCgtjdXJyZW5jeV9pZBABGkAzM2E3ZGU2YWY1MDU4NTVkMzA2MjcxNWFjNDYwOTczNmE2MGNmMjc3OWYwM2NkMDMzZTc0NTkwNzA4YzRmMGQ0InQKBnNvdXJjZRAEGmAIARJcCPz8GhIqMHgxMTAxYzQ3NjMwMjc1MDM0YTlhMTFlZTg4Yzk3MjcxODE3NDcwNDRmGhZub3N0cm8gYWRkcmVzcyBmb3IgRVRIIhJtLzQ0Jy82MCcvMCcvMCcvMCciBnNvdXJjZSJjCgtkZXN0aW5hdGlvbhAFGkUIARJBCPb0ARIqMHg4MTQ2NDVhNzViMjBjMTZlMmQwMzdlOTI1ZTQ5NWEyNjAwYTY4ZmE0Gg9TUUwgU2VydmVyIGFkZHIiC2Rlc3RpbmF0aW9uIqgBCgZhbW91bnQQChqTAQoHI4byb8EAABIHMTQuMzcyMBoQMTQzNy4yMDQ5MjUwODk1NiASKgNFVEgyA0NIRjphCh12YWxpZGF0b3ItY29yZUB0YXVydXNncm91cC5jaBJAqgMqxIZNsS0Rk/x8qDvtYp1c4+n+OYTafSIco3vVhK0evKo/lckcXTAPlPQQsRmGiHVG5EhpZLlBD+pkgQ9YayIGYW1vdW50Ih4KEXRvdGFsX2ZpYXRfYW1vdW50EAEaBzE0LjM3MjAiGgoPZ2FzX3ByaWNlX2xpbWl0EAIaBQG4zrRoIhQKCmdhc19saW1pdHMQAxoECgJSCCISCgdwYXlsb2FkIgdwYXlsb2FkIh8KFGZlZV9wYWlkX2J5X3JlY2VpdmVyEAEaBWZhbHNlIiAKFXVzZV91bmNvbmZpcm1lZF9mdW5kcxABGgVmYWxzZSIcChFpc19jYW5jZWxfcmVxdWVzdBABGgVmYWxzZSJCCg90cmFuc2FjdGlvbl9pZHMQBhotCis0NDE5ODA6ODViMGE3ZmQtM2Q2YS00ZTg5LWIwZGQtMjcxMTEwZDNmYjg2OgdtYWlubmV0",
        "status": "CREATED",
        "trails": [
            {
                "userId": "11",
                "externalUserId": "[email protected]",
                "action": "created",
                "requestStatus": "CREATED"
            },
            {
                "userId": "1",
                "externalUserId": "[email protected]",
                "action": "approvers_assigned",
                "comment": "rule = [source: m/44'/60'/0', destination: <binary>, amount: <binary>, payload: <binary>], approvals = ['team1': 1 sig]",
                "requestStatus": "CREATED"
            }
        ],
        "metadata": {
            "hash": "f1cca186eb53015be7774b6b5b20ec8e9aacbc0be3b8bde8084e997e8c5987aa",
            "payload": [
                {
                    "column": "",
                    "key": "request_id",
                    "type": "String",
                    "value": "7426452"
                },
                {
                    "column": "",
                    "key": "rules_key",
                    "type": "String",
                    "value": "ETH"
                },
                {
                    "column": "",
                    "key": "currency",
                    "type": "String",
                    "value": "ETH"
                },
                {
                    "column": "",
                    "key": "currency_name",
                    "type": "String",
                    "value": "Ethereum - ETH"
                },
                {
                    "column": "",
                    "key": "currency_id",
                    "type": "String",
                    "value": "33a7de6af505855d3062715ac4609736a60cf2779f03cd033e74590708c4f0d4"
                },
                {
                    "column": "source",
                    "key": "source",
                    "type": "Source",
                    "value": {
                        "payload": {
                            "address": "0x1101c47630275034a9a11ee88c9727181747044f",
                            "id": "441980",
                            "label": "nostro address for ETH",
                            "path": "m/44'/60'/0'/0'/0'"
                        },
                        "type": "SourceInternalAddress"
                    }
                },
                {
                    "column": "destination",
                    "key": "destination",
                    "type": "Destination",
                    "value": {
                        "payload": {
                            "address": "0x814645a75b20c16e2d037e925e495a2600a68fa4",
                            "id": "31350",
                            "label": "SQL Server addr",
                            "memo": ""
                        },
                        "type": "DestinationExternalAddress"
                    }
                },
                {
                    "column": "amount",
                    "key": "amount",
                    "type": "Amount",
                    "value": {
                        "currencyFrom": "ETH",
                        "currencyTo": "CHF",
                        "decimals": "18",
                        "rate": "1437.20492508956",
                        "valueFrom": "10000000000000000",
                        "valueTo": "14.3720"
                    }
                },
                {
                    "column": "",
                    "key": "total_fiat_amount",
                    "type": "String",
                    "value": "14.3720"
                },
                {
                    "column": "",
                    "key": "gas_price_limit",
                    "type": "BigInt",
                    "value": "7395521640"
                },
                {
                    "column": "",
                    "key": "gas_limits",
                    "type": "BigIntArray",
                    "value": [
                        "21000"
                    ]
                },
                {
                    "column": "payload",
                    "key": "payload",
                    "type": "Bytes",
                    "value": ""
                },
                {
                    "column": "",
                    "key": "fee_paid_by_receiver",
                    "type": "String",
                    "value": "false"
                },
                {
                    "column": "",
                    "key": "use_unconfirmed_funds",
                    "type": "String",
                    "value": "false"
                },
                {
                    "column": "",
                    "key": "is_cancel_request",
                    "type": "String",
                    "value": "false"
                },
                {
                    "column": "",
                    "key": "transaction_ids",
                    "type": "StringArray",
                    "value": [
                        "441980:85b0a7fd-3d6a-4e89-b0dd-271110d3fb86"
                    ]
                }
            ],
            "payloadAsString": "[{\"key\":\"request_id\",\"type\":\"String\",\"value\":\"7426452\",\"column\":\"\"},{\"key\":\"rules_key\",\"type\":\"String\",\"value\":\"ETH\",\"column\":\"\"},{\"key\":\"currency\",\"type\":\"String\",\"value\":\"ETH\",\"column\":\"\"},{\"key\":\"currency_name\",\"type\":\"String\",\"value\":\"Ethereum - ETH\",\"column\":\"\"},{\"key\":\"currency_id\",\"type\":\"String\",\"value\":\"33a7de6af505855d3062715ac4609736a60cf2779f03cd033e74590708c4f0d4\",\"column\":\"\"},{\"key\":\"source\",\"type\":\"Source\",\"value\":{\"type\":\"SourceInternalAddress\",\"payload\":{\"id\":\"441980\",\"address\":\"0x1101c47630275034a9a11ee88c9727181747044f\",\"label\":\"nostro address for ETH\",\"path\":\"m/44'/60'/0'/0'/0'\"}},\"column\":\"source\"},{\"key\":\"destination\",\"type\":\"Destination\",\"value\":{\"type\":\"DestinationExternalAddress\",\"payload\":{\"id\":\"31350\",\"address\":\"0x814645a75b20c16e2d037e925e495a2600a68fa4\",\"label\":\"SQL Server addr\",\"memo\":\"\"}},\"column\":\"destination\"},{\"key\":\"amount\",\"type\":\"Amount\",\"value\":{\"valueFrom\":\"10000000000000000\",\"valueTo\":\"14.3720\",\"rate\":\"1437.20492508956\",\"decimals\":\"18\",\"currencyFrom\":\"ETH\",\"currencyTo\":\"CHF\"},\"column\":\"amount\"},{\"key\":\"total_fiat_amount\",\"type\":\"String\",\"value\":\"14.3720\",\"column\":\"\"},{\"key\":\"gas_price_limit\",\"type\":\"BigInt\",\"value\":\"7395521640\",\"column\":\"\"},{\"key\":\"gas_limits\",\"type\":\"BigIntArray\",\"value\":[\"21000\"],\"column\":\"\"},{\"key\":\"payload\",\"type\":\"Bytes\",\"value\":\"\",\"column\":\"payload\"},{\"key\":\"fee_paid_by_receiver\",\"type\":\"String\",\"value\":\"false\",\"column\":\"\"},{\"key\":\"use_unconfirmed_funds\",\"type\":\"String\",\"value\":\"false\",\"column\":\"\"},{\"key\":\"is_cancel_request\",\"type\":\"String\",\"value\":\"false\",\"column\":\"\"},{\"key\":\"transaction_ids\",\"type\":\"StringArray\",\"value\":[\"441980:85b0a7fd-3d6a-4e89-b0dd-271110d3fb86\"],\"column\":\"\"}]"
        },
        "rule": "rule = [source: m/44'/60'/0', destination: <binary>, amount: <binary>, payload: <binary>], approvals = ['team1': 1 sig]",
        "approvers": {
            "parallel": [
                {
                    "sequential": [
                        {
                            "externalGroupID": "team1",
                            "minimumSignatures": 1
                        }
                    ]
                }
            ]
        },
        "type": "payment",
        "currencyInfo": {
            "name": "Ethereum",
            "symbol": "ETH",
            "coinTypeIndex": "60",
            "blockchain": "ETH",
            "decimals": "18",
            "isAccountBased": true,
            "enabled": true,
            "id": "33a7de6af505855d3062715ac4609736a60cf2779f03cd033e74590708c4f0d4",
            "displayName": "Ethereum - ETH",
            "type": "native",
            "network": "mainnet"
        }
    }
    ],
    "cursor": {
        "currentPage": "gaRjdXJypUZJUlNU"
    }
}

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 RequestApprover role will be necessary.

To approve a transaction requests, you will need to use the following endpoint:

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 created and approved a transaction in Taurus Protect!




  Β© 2025 Taurus SA. All rights reserved.