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:
- Authenticate with Protect
- Create a transaction request
- List pending requests for approval
- 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!
Updated 8 days ago