Use Case 2: Sell-Side - Distributing Assets
Overview
The following is a sell-side walkthrough for integrating into your Ownera router via the FinP2P API for the purpose of issuing assets to distribute to your partnered financial organizations. The steps involved are as follows:
- Onboarding the asset on the Router - registering the asset so it can be distributed to other buy side routers, including:
- Defining the asset and its issuing entity
- Attaching certifications and other trade and marketing supporting documentation
- Defining a primary sale intent in order to launch the asset
- Sharing it with other buy side routers
- Asset tokenization request and trade processing requests - receive and process primary and secondary intent trade requests orchestrated by a comprehensive execution plan.
- Facilitate other operational requests - implement various operational functionality including:
- the ability to send fund withdrawal requests from payment providers on behalf of the issuer, accrued via investment proceeds.
- The ability to send fund withdrawal requests from payment providers on behalf of the issuer, accrued via investment proceeds.
An adapter needs to be developed in order to connect between the Router and your tokenization platform which needs to include the asset tokenization tokenization request and trade processing requests. It can also include other flows, including asset onboarding on the router for automation purposes, and other operational functionalities.
Onboarding the asset
The following outlines the walkthrough of the integration requires in order to onboard an asset on the Router. There are two different asset onboarding approaches that can be implemented:
- The Asset will be tokenized firstly on the tokenization platform (or has already been tokenized) and then be onboarded on to the Router. In this case an association will need to be defined to link between the Asset ID on the Router and on the Tokenization Platform (for example, add to the asset data on the Router the token ID or address).
- The Asset is on-boarded on the to the Ownera platform which in turn will request the tokenization platform to tokenize it. Linking is done as part of the same transaction of defining the asset on the Router and then creating the underlying associated record on the Tokenization Platform.
Asset onboarding is available either by using the Ownera Admin App, or via the Router’s own API. If you chose to utilize the Owenra Admin app, there is no need to integrate this section.
The diagram below illustrates the steps involved in this integration flow for onboarding the asset. This includes other Routers involved to illustrate the overall flow integration with other organizations:
Below it the walkthrough of using the API to onboard the asset, which you can use to integrate to in order to automate this process from your tokenization or asset management platform.
# | API Reference | Description | |||
---|---|---|---|---|---|
Onboard the Issuer | |||||
1 | Create Issuer | Registers the issuer on your router. | |||
2 | Bind Investor to a Custodian | Send a request to the associated custodian to onboard the investor on the custody system, which in turn will provide the public key (finID) that is associated with the investor. | |||
3 | Attach Certificates | [Optional] Attach certificates to the investor, such as KYA. For FinP2P recommended certificate specifications refer to https://github.com/owneraio/finp2p-certificates-spec | |||
4 | Attach Documentation | [Optional] Add additional documentation to provide additional information about the issuer. | |||
5 | Share Issuer with Payments Service | Link the issuer to the applicable payments organization responsible for handling the payments leg for the issuer. Note: If the your router provides also payment services to the issuer, this step can be omitted | |||
Setup the Account | |||||
6 | n/a | Note settlement account will be auto-created (if does not exist already) on-behalf of the issuer during trade execution, therefore no initial setup required. | |||
Onboarding an Asset | |||||
7 | Create the Asset | Define the asset you are onboarding, including values such as asset denomination, basic name, etc. If the asset has been tokenized already, you can provide a link id here (such as token id or address) that will will be sent to the ledger to validate and confirm. You can also preconfigure the asset to support only certain type of intents, as listed out in the API documentation (e.g., you can assign only primary sale intent type to the asset which will not allow any other intents to be created against the given asset). | |||
8 | Process Request Asynchronously | The asset creation is a asynchronous process, and therefore you should use the operation polling process to check on the progress of the asset registration (e.g., while it is being routed for the tokenization platform to validate and confirm). | |||
9 | Attach Certificates | Attach certificates, such as “KYA” to the given asset. | |||
10 | Attach Documentation | Add additional documentation to support and facilitate asset trading. This is done by attaching documents to the given asset certificate, such as PDFs or images of the asset. | |||
11 | Select Organizations to Distribute To | Share an asset with the given organization(s) that you allow to have access to the asset to invest in. | |||
12 | Create a Primary Sale | Allow for investors in which you shared the asset with to investment in the primary sale (issuance). This is an intent of type “Primary Sales”. |
Asset Tokenization and Trade Request Processing
The following is a walkthrough on what needs to be developed inside the adapter which will be used to connect between the Router and the Tokenization (asset management) platform, which includes all end points required to handle incoming Router requests.
Asset Onboarding Request
This request is sent to the adapter once the asset is onboarded on the given Router, in which you will handle the request based on the asset onboarding approach you elected to implement. You do need to integrate the following even if you chose to use the Ownera Admin tool since onboarding the asset there will also require interaction with the blockchain (e.g., tokenize the asset upon this call, or bind the asset setup on Ownera to the Tokenization side).
# | API Reference | Description |
---|---|---|
13 | Handle Asset Creation Notification | When an asset is onboarded on the given sell-side router, you will receive this notification to the ledger/tokenization adapter that a new asset has been registered on the router. It will provide you with the resource id which is the unique identifier for the asset on the router, and optionally if entered in the asset creation process a tokenId which you can use to understand which token the asset resource id is referring to on the blockchain side. So based on the implementation approach:. 1. If tokenization of the asset on the blockchain is done prior to onboarding, you should be provided with the tokenId that refers to the asset that has been tokenized already, and you can just confirm the mapping/refers to that token is correct. If not, reject the request. 2. Or invoke the process to tokenize the asset on the adapter and respond back with the tokenId that has been tokenized. |
Process Primary Sale Request
The diagram below illustrates the steps involved in this integration flow around the trade initiation from the buy side and its flow to execution plan approval. This includes other Routers involved to illustrate the overall flow integration with other organizations:
In addition the diagram below illustrates the issuance step in the overall trade processing that is orchestrated between the Routers by the execution plan, in this case including the trading platform request and the payment leg processing by the payment service.
The following outlines how to handle primary sale request, including the endpoint you will need to develop in the adapter to handle the flow:
# | API Reference | Description |
---|---|---|
14 | Receive Notification and Approve New Execution Plan | You will receive an execution plan notification which will be an indication that a new request for primary sale investment (issuance) in the given asset by an investor was submitted. You can chose to immediately approve the execution plan, or response with acknowledgement and approve it in a subsequent asynchronous manner after you review the plan. |
15 | Review Execution Plan | (Optional) Poll the plan to review and asset your participation in the execution plan, and in turn will allow you to process your approval. This step can include onboarding the investor, qualifying and approving their KYC, performing compliance and accreditation checks, etc. Refer to the execution plan explanation. Execution Plan You can also elect to verify the signatures provided in the execution plan details, in this case signature for the investor requesting the issuance. Refer to the section titled "Cryptographic Keys and Signature Verifications" below. The template for the signature is outlined in the custody section of the API specification Signature Templates . |
16 | Query Investor | (Optional) Query for the investor details, such as their certificates, if you wish to qualify the investor(s) involved in the trade. |
17 | Process Status Polling for Execution Plan Approval | (Optional) If you chose to process approval in asynchronous manner, the router will polling your side for status on approval. |
18 | Process Issuance Request | Develop the logic to process the primary sale intent execution request which is sent to your adapter, including: 1. Receiving the issuance request from the Router and processing it against the tokenization platform. 2. Could include logic for handling regulations and other order execution acceptance parameters. 3. Note by default the Router performs these validations on your behalf. However, additional signature verification can be performed to validate the transaction. Refer to the section titled "Cryptographic Keys and Signature Verifications" below for additional information. The template for the signature is outlined in the custody section of the API specification Signature Templates. Note based on the blockchain, signature hash can be generated using different algorithms, such as SHA-256 or Keccak-256. Note Router should be configured accordingly to the algorithm being used.The request can either be processed synchronously or asynchronously (isCompleted set to True or false accordingly)Respond with a CID which will be used as a method to poll your side for completion. |
19 | Process Request Asynchronously | Implement the get operation ability in order to allow to query for the status of the execution request sent to you, for the case you chose the process the issuance request asynchronously. |
Process Secondary Trade Request
The diagram below illustrates the steps involved in this integration flow around the trade initiation from the buy side of the buyer and seller for secondary intent, and the flow for the execution plan approval. Diagram includes other Routers involved to illustrate the overall flow integration with other organizations:
And below is the illustration of the trade execution, which includes the illustration of the payment leg processing in this trade.
The following outlines how to handle secondary trade request, including the endpoint you will need to develop in the adapter to handle the flow:
# | API Reference | Description |
---|---|---|
20 | Receive Notification and Approve New Execution Plan | You will receive an execution plan notification which will be an indication that a new request for a trade in which the given asset is requested to be transferred from one investor to another. You can chose to immediately approve the execution plan, or response with acknowledgement and approve it in a subsequent asynchronous manner after you review the plan. |
21 | Review Execution Plan | (Optional) Poll the plan to review and asset your participation in the execution plan, and in turn will allow you to process your approval. This step can include onboarding the investor, qualifying and approving their KYC, performing compliance and accreditation checks, etc. Refer to the execution plan explanation. Execution Plan You can also elect to verify the signatures provided in the execution plan details, in this case signature for both the buying and selling investors involved in this secondary trade execution request. Refer to the section titled "Cryptographic Keys and Signature Verifications" below. The template for the signature is outlined in the custody section of the API specification Signature Templates. |
22 | Query Investor | (Optional) Query for the investor details, such as their certificates, if you wish to qualify the investor(s) involved in the trade, in this case including the buyer and seller. |
23 | Process Status Polling for Execution Plan Approval | (Optional) If you chose to process approval in asynchronous manner, the router will polling your side for status on approval. |
23 | Process Secondary Trade Request | Develop the logic to process the secondary intent execution request which is sent to your adapter, including: 1. Receiving the transfer request from the Router and processing it against the tokenization platform. 2. Could include logic for handling regulations and other order execution acceptance parameters. 3. Note by default the Router performs these validations on your behalf. However, additional signature verification can be performed to validate the transaction. Refer to the section titled "Cryptographic Keys and Signature Verifications" below for additional information. The template for the signature is outlined in the custody section of the API specification Signature Templates . Note based on the blockchain, signature hash can be generated using different algorithms, such as SHA-256 or Keccak-256. Note Router should be configured accordingly to the algorithm being used.The request can either be processed synchronously or asynchronously (isCompleted set to True or false accordingly)Respond with a CID which will be used as a method to poll your side for completion. |
24 | Process Request Asynchronously | Implement the get operation ability in order to allow to query for the status of the execution request sent to you. |
Additional Operational Functionality
The following outlines additional operational functionality that can be implemented, such as manual balance adjustments, providing asset balance and receipts, and providing functionality to request withdrawal of sale proceeds for issuers.
API Reference | Description | ||||
---|---|---|---|---|---|
25 | Query Receipts | Uses GraphQL to either subscribe or query for the receipts that will serve as confirmation that the investment was executed and payment was processed. | |||
26 | Monitor Execution Plan Progression | And/or use the get execution plan details and continue to pool to see progress of the plan | |||
27 | Provide Receipts | Allow to request receipt details from your ledger. | |||
28 | Provide Ledger Asset Balance | Allow to request the token balance for the given owner on the asset. | |||
29 | Perform Balance Adjustments | This will allow you to make balance adjustments that were the result of activities performed outside of the Ownera scope of trading. | |||
Request Fund Withdrawal | |||||
30 | Request Funds Withdrawal | The issuer has the option to withdraw the funds that are in the given account by putting in a request for withdrawal. This request is routed to the given payment service provider to process. | |||
29 | Poll for Transaction Completion | If the request is processed asynchronously, use the get operations to receive the response. |
Allow for Asset Redeem
TBD.
Testing Your Integration
You can test your sell-side integration by requesting from Ownera a buy-side sandbox which will provide you with a test Investor App.
Sell Side Integration Example
The following is an example walkthrough for the sell side integration. The below are the assumed values that will be used through out the example:
Parameter | Description | Value |
---|---|---|
Issuer Fin ID | The public key used in creating the user profile, which in turn will be used as the finID for the user when transacting. | 024c9f4cda13349113e312bfe6e8860a5186ab90b3d0987cfd43bcdc285303d93a |
Issuer User ID | The finP2P resource ID for the given user created. | bank-us:101:f09fa8ac-2b83-49de-8779-6ff954c5b181 |
Issuer Payments Service Org ID | The payments service organization that will be used to transact the investment | payments-router |
Asset ID | The asset ID assigned to the created asset. | bank-us:102:631dd696-302b-4f6e-b91f-79cc7bf84f54 |
Primary Sale Intent ID | The Id assigned to the primary sale intent that is created. | bank-us:105:3a360d7a-df60-40b2-9574-eccaa1ed1f08 |
Onboarding the Asset [Optional]
The following section is option, as the asset onboarding can be facilitated using the Ownera Admin application which would not require integration to this capability.
Create issuer user profile
curl --location --request POST 'http://<router url>/finapi/profiles/owner' \
--header 'Content-Type: application/json' \
--data-raw '{}'
{
"isCompleted": true,
"response": {
"id": "bank-us:101:f09fa8ac-2b83-49de-8779-6ff954c5b181"
},
"type": "profile"
}
Bind the user to the given custodian:
Send a request to the custodian to onboard the investor on the custody platform, which will return the public key that will be used to identify the investor. The process is an asynchronous process, and therefore you will need to
curl --location --request POST '<router url>/finapi/profiles/owner/bank-us:101:f09fa8ac-2b83-49de-8779-6ff954c5b181/account' \
--header 'Content-Type: application/json' \
--data-raw '{
"orgId": "custody-service"
}'
{
"cid":"e3c59abb-f296-4556-a4c6-9c0e397ad6f9",
"isCompleted":false,
"type":"addAccount"
}
Poll the request until it is completed:
Poll for the completion of the custodian investor onboarding.
curl --location --request POST '<router url>/operations/status/e3c59abb-f296-4556-a4c6-9c0e397ad6f9' \
--header 'Content-Type: application/json' \
--data-raw '{}'
{
"cid": "e3c59abb-f296-4556-a4c6-9c0e397ad6f9",
"isCompleted": true,
"response": {
"custodyOrgId": "custody-service",
"finId": "024c9f4cda13349113e312bfe6e8860a5186ab90b3d0987cfd43bcdc285303d93a"
},
"type": "account"
}
Attach certificate to the given user
curl --location --request POST 'http://<router url>/finapi/profiles/bank-us:101:f09fa8ac-2b83-49de-8779-6ff954c5b181/certificates' \
--header 'Content-Type: application/json' \
--data-raw '{
"type": "ownerInfo",
"issuanceDate": 1668598695,
"expirationDate": 1700134695,
"data": "{\"email\":\"[email protected]\",\"name\":\"Issuer1\",\"type\":\"issuer\"}"
}'
{
"id": "ec634170-8c62-4d55-b324-0391eb30fac1"
}
Upload a document to the given certificate, pointing to a location of the file
curl --location --request POST 'http://<router url>/finapi/profiles/bank-us:101:f09fa8ac-2b83-49de-8779-6ff954c5b181/certificates/ec634170-8c62-4d55-b324-0391eb30fac1' \
--form 'kya doc=@"/kyc.pdf"'
Associate the user with the given payments service
curl --location --request POST 'http://<router url>/finapi/profiles/bank-us:101:f09fa8ac-2b83-49de-8779-6ff954c5b181\share' \
--header 'Content-Type: application/json' \
--data-raw '{
"organizations": [
"payements-service"
]
}'
n/a
Onboard the asset
The asset onboarding onto the router should includes the following:
- In the case the asset has been pre-tokenized, and it just being registered on the router, a valid tokenId should be provided that allows to associate the asset being registered to a record on the asset/tokenization side. The Id could designate a token address on a blockchain, a unique asset id, etc., which is all based on what asset platform the integration is against. For example, in the case below we include the Ethereum address of the asset given we are transacting against a blockchain.). Do not include this if you are tokenizing the asset upon the asset creation on the router.
- The denomination of the asset, using the ISO currency code.
- The asset policy, which can designate if a ledger proof (signing) is required for the given integration. In this case a public key assigned by the ledger platform is provided to allow to verify the signature of the given operation, and the type of signature that is going to be used (in the case below EIP712 signature template is used. For more information refer to: https://finp2p-docs.ownera.io/reference/ledger-proofs
- The type of intents that the asset should be configured to accept.
- Additional configuration, such as what signature template is going to be used when requesting signature by the Custodian on behalf of the investor, when transacting against the given asset. For more information refer to: https://finp2p-docs.ownera.io/reference/signature-schemes
curl -curl --location --request POST 'http://<router url>/finapi/profiles/asset
--header 'Content-Type: application/json' \
--data-raw '
{
"denomination": {
"type": "fiat",
"code": "USD"
},
"ledgerAssetBinding": {
"type": "tokenId",
"tokenId": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606EB48"
},
"assetPolicies": {
"proof": {
"policy": {
"publicKey": "02587dc63b3468c84a11dc1ed15c18b2e2a863b89fa70987887b0788f34a2865c2",
"signatureTemplate": "EIP712"
},
"type": "signatureProofPolicy"
}
},
"intentTypes": [
"primarySale",
"buyingIntent",
"sellingIntent"
],
"config": "{\"signatureTemplate\":{\"hashFunction\":\"keccak-256\",\"templateType\":\"eip712\"}}",
"type": "Fund",
"name": "Asset 001",
"issuerId": "bank-us:101:a431ad7b-b963-43fc-8d8d-48a9fba7ff6d"
}'
{
"cid": "fe1a9f51-9c5c-4bbf-bddc-a44cebb3a631",
"isCompleted": false,
"response": {
"id": "bank-us:102:4b2fc630-bc23-4337-be44-97f12875f835"
},
"type": "profile"
}
Poll for the status of the asset onboarding
Request for the status of the asset onboarding, and continue polling until the request is completed.
curl -curl --location --request POST 'http://<router url>/finapi/operations/status/fe1a9f51-9c5c-4bbf-bddc-a44cebb3a631
--header 'Content-Type: application/json' \
--data-raw ''
{
"cid": "fe1a9f51-9c5c-4bbf-bddc-a44cebb3a631",
"isCompleted": true,
"response": {
"id": "bank-us:102:4b2fc630-bc23-4337-be44-97f12875f835"
},
"type": "profile"
}
{
"cid": "fe1a9f51-9c5c-4bbf-bddc-a44cebb3a631",
"error": {
"code": 999,
"message": "failed to create asset: &{5 asset with tokenId 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606EB48 does not exists}"
},
"isCompleted": true,
"type": "profile"
}
Process the asset onboarding request on the ledger side
This endpoint that is required on the ledger adapter will process the notification that an asset has been onboarded on Ownera at your organization (e.g., will be invoked as a result of the asset profile creation).
If your process has the asset tokenized prior to onboarding on Ownera, just confirm the message, and/or you can use this call to perform your mapping between the asset id of Ownera provided in this message and your asset id (e.g., blockchain contract address).
If not, invoke your logic for tokenizing the assets and linking it to the Ownera asset id.
curl --location --request POST 'http://<adapter url>/assets/create' \
--header 'Content-Type: application/json' \
--data-raw '
{
"asset": {
"resourceId": "bank-us:102:4b2fc630-bc23-4337-be44-97f12875f835",
"type": "finp2p"
},
"ledgerAssetBinding": {
"tokenId": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606EB48",
"type": "tokenId"
}
}'
{
"cid": "c24ac7f0-f923-436c-befe-259cc6cc8d18",
"isCompleted": false
}
Provide status on the asset onboarding on the ledger side
curl --location --request POST 'http://<adapter url>/operations/status/c24ac7f0-f923-436c-befe-259cc6cc8d18' \
--header 'Content-Type: application/json' \
--data-raw ''
{
"cid": "c24ac7f0-f923-436c-befe-259cc6cc8d18",
"isCompleted": true,
"response": {
"ledgerAssetInfo": {
"ledgerTokenId": {
"tokenId": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606EB48",
"type": "token"
}
}
}
}
{
"cid": "c24ac7f0-f923-436c-befe-259cc6cc8d18",
"isCompleted": true,
"error": {
"code": 5,
"message": "asset with tokenId 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606EB48 does not exists"
}
}
Attached a Know Your Asset (KYA) certificate to the asset
curl --location --request POST 'http://<router url>/finapi/profiles/bank-us:102:873e82a0-274a-4762-8535-7b9540cf680b/certificates' \
--header 'Content-Type: application/json' \
--data-raw '{
"type": "KYA",
"issuanceDate": 1668598695,
"expirationDate": 1672399561,
"data": "{\"publicName\":\"Public Asset Building 2\",\"officialName\":\"Official Asset Building 2\",\"description\":\"Description of the Asset Building 2.\",\"info\":[{\"type\":\"text\",\"name\":\"Location\",\"value\":\"NYC\"},{\"type\":\"link\",\"name\":\"Website\",\"value\":\"https://www.assetpage.com\"}]}"
}'
Optional: Attached images to the asset (examples below illustrate the setting of images in case you are using the Ownera App)
Note the naming convention for the images
curl --location --request POST 'http://<router url>/finapi/profiles/bank-us:102:873e82a0-274a-4762-8535-7b9540cf680b/certificates/b57732e7-a477-4769-8753-f761a1412a60/docs' \
--header 'Content-Type: application/json' \
--form 'asset icon=@"/icon-asset profle.png"'
curl --location --request POST 'http://localhost:62532/profiles/bank-us:102:873e82a0-274a-4762-8535-7b9540cf680b/certificates/b57732e7-a477-4769-8753-f761a1412a60/docs' \
--header 'Content-Type: application/json' \
--form 'asset logo=@"/logo-asset profle.png"'
Create a Primary Sale Intent
The intent below is an example of making 50 tokens available each at the price of 1 USD. The intent indicates the finId and the payments service organization that is going to be used for managing the issuance, and the destination for the payment for the investment.
curl --location --request POST 'http://<router url>/finapi/profiles/asset/bank-us:102:4b2fc630-bc23-4337-be44-97f12875f835/intent' \
--header 'Content-Type: application/json' \
--data-raw '{
"assetTerm": {
"asset": {
"type": "finp2p",
"resourceId": "bank-us:102:4b2fc630-bc23-4337-be44-97f12875f835"
},
"amount": "50"
},
"assetInstruction": {
"account": {
"account": {
"type": "finId",
"finId": "024c9f4cda13349113e312bfe6e8860a5186ab90b3d0987cfd43bcdc285303d93a",
"orgId": "payements-service"
},
"asset": {
"type": "finp2p",
"resourceId": "bank-us:102:4b2fc630-bc23-4337-be44-97f12875f835"
}
}
},
"settlementTerm": {
"asset": {
"type": "fiat",
"code": "USD"
},
"unitValue": "1"
},
"intent": {
"type": "primarySale",
"settlementInstruction": {
"type": "escrow",
"destinationAccounts": [
{
"account": {
"type": "finId",
"finId": "024c9f4cda13349113e312bfe6e8860a5186ab90b3d0987cfd43bcdc285303d93a",
"orgId": "payements-service"
},
"asset": {
"type": "fiat",
"code": "USD"
}
}
]
},
"issuer": "bank-us:101:f09fa8ac-2b83-49de-8779-6ff954c5b181"
},
"start": 1671108000,
"end": 1672399561
}'
{
"id": "bank-us:105:cd3a32e0-da32-4161-a00b-d138a56712d6"
}
Example of Asset Tokenization and Trade Request Processing
Once a primary sale trade is requested, you will receive a notification message which indicates of a new execution plan submission for an asset in which is managed by your organization for issuance in which you will need to approve. If no decision making is required, you can approve immediately, otherwise acknowledge and process it asynchronously.
curl --location --request POST 'http://<adapter url>/plan/approve' \
--header 'Content-Type: application/json' \
--data-raw '
{
"executionPlanId": "bank-uk:106:9909e355-df0e-4708-8121-cec192089418"
}'
{
"cid": "12321321",
"isCompleted": false,
}
{
"cid": "12321321",
"isCompleted": true,
"approval": {
"status": "approved"
}
}
You can query the router for details on the execution plan outlining the full workflow for the execution of the given transaction.
curl --location 'http://<router url>/finapi/execution/bank-uk:106:9909e355-df0e-4708-8121-cec192089418' \
--header 'Content-Type: application/json' \
--data ''
Refer to the Execution Plan details at the bottom of this section.
Process request for status of execution plan approval
curl --location 'http://<router url>/operations/status/12321321' \
--header 'Content-Type: application/json' \
--data ''
{
"cid": "12321321",
"isCompleted": true,
"approval": {
"status": "approved"
}
}
{
"cid": "12321321",
"isCompleted": true,
"approval": {
"status": "rejected",
"failure": {
"failureType": "validationFailure",
"code": 1001,
"message": "Invalid input data provided"
}
}
}
Receive and respond back to primary sale investment token issuance request
The message contains a reference to the execution plan you received. Respond to the process as indicated in order to process this request in an asynchronous manner. The cid you will return will be used next in the operational status polling to receive the progress and completion on processing of the issuance request.
Please note that "hasgroups" section is subject to change, and for up to date specification please refer to https://finp2p-docs.ownera.io/reference/signature-schemes. The signature can be used to verify in this case the signing of the investors on the issuance request commercials.
The executionPlanId provides you with the association of this request to the execution plan provided previously.
curl --location --request POST 'http://<adapter url>/assets/issue' \
--header 'Content-Type: application/json' \
--data-raw '
{
"nonce": "7282551a6ffc938e58cdb9a32c7e7c2f60e2b0466b47800e0000000063e0c5bd",
"asset": {
"resourceId": "bank-us:102:4b2fc630-bc23-4337-be44-97f12875f835",
"type": "finp2p"
},
"destination": {
"finId": "03c7c9d6b96a678f549c4dd5407eabffc779cf665846846f1a8bb5d5e63cedc8e3",
"type": "finId"
},
"quantity": "10000",
"settlementRef": "",
"executionContext": {
"executionPlanId": "bank-uk:106:9909e355-df0e-4708-8121-cec192089418",
"instructionSequenceNumber": 2
},
"signature": {
"signature": "db96110667579fd876c6b74d8fd848d29d0fb22f114c912202661010a27ef5087b3c9e33fc9af83e1f025e7d180612c7fc0c5f847a61e348dd35309855b29a44",
"template": {
"hash": "319581118dc6e2af7a5d92b5e149bed108bd03f2c214371ff28d9ca81206ad60",
"hashGroups": [
{
"fields": [
{
"name": "nonce",
"type": "bytes",
"value": "16f56a399856b1a6243f5b9afb3e523ea73294be4c95ef0a0000000066d97261"
},
{
"name": "operation",
"type": "string",
"value": "issue"
},
{
"name": "assetType",
"type": "string",
"value": "finp2p"
},
{
"name": "assetId",
"type": "string",
"value": "citi:102:d0c3eb56-0fff-4670-adfd-ad291a4314c3"
},
{
"name": "dstAccountType",
"type": "string",
"value": "finId"
},
{
"name": "dstAccount",
"type": "string",
"value": "02fd7923740a775c95ce17e9bb7239ff9096689f70db9263a7efb9a9ad08e9fed7"
},
{
"name": "amount",
"type": "string",
"value": "1"
}
],
"hash": "faf3fd67e908cd840298a6f3523631716d4c8df06d69c9a32415f59eaa56825d"
},
{
"fields": [
{
"name": "assetType",
"type": "string",
"value": "fiat"
},
{
"name": "assetId",
"type": "string",
"value": "USD"
},
{
"name": "srcAccountType",
"type": "string",
"value": "finId"
},
{
"name": "srcAccount",
"type": "string",
"value": "02fd7923740a775c95ce17e9bb7239ff9096689f70db9263a7efb9a9ad08e9fed7"
},
{
"name": "dstAccountType",
"type": "string",
"value": "finId"
},
{
"name": "dstAccount",
"type": "string",
"value": "03c48631f1d9ca0c89d8da8c7268e4d44f4223737829a9316d940352da3b25c40d"
},
{
"name": "amount",
"type": "string",
"value": "10"
}
],
"hash": "e8a6bc590ed71a70e2507ad842a237cf55bbb751abb38fca61c0c83c4b07bd5f"
}
],
"type": "hashList"
}
}
}
}'
{
"cid": "6661b36bbc7ee21b182bd909f1e0cb14c40a8d9dd80badee0000000063db7cf9",
"isCompleted": false,
"response": {
}
}
Process request for status of execution plan approval if you chose to handle approval asynchronously
Note: The error code and text should designate an exception that is meaningful for investigation of the processing failure.
curl --location --request POST 'http://<adapter_url>/operations/6661b36bbc7ee21b182bd909f1e0cb14c40a8d9dd80badee0000000063db7cf9' \
--header 'Content-Type: application/json' \
--data-raw '
{
}'
{
"cid": "6661b36bbc7ee21b182bd909f1e0cb14c40a8d9dd80badee0000000063db7cf9",
"isCompleted": false,
"response": {
}
}
{
"type": "receipt",
"operation": {
"cid": "6661b36bbc7ee21b182bd909f1e0cb14c40a8d9dd80badee0000000063db7cf9",
"isCompleted": true,
"response": {
"id": "1031C909828C9BB3ED572A0066B1565357A993409DE4330E8319D2D0B7553D64",
"asset": {
"type": "finp2p",
"resourceId": "bank-us:102:4b2fc630-bc23-4337-be44-97f12875f835"
},
"quantity": "10000",
"destination": {
"finId": "03c7c9d6b96a678f549c4dd5407eabffc779cf665846846f1a8bb5d5e63cedc8e3",
"account": {
"type": "finId",
"finId": "03c7c9d6b96a678f549c4dd5407eabffc779cf665846846f1a8bb5d5e63cedc8e3"
}
},
"transactionDetails": {
"transactionId": "1031C909828C9BB3ED572A0066B1565357A993409DE4330E8319D2D0B7553D64"
},
"timestamp": 1702203549,
"operationType": "issue"
}
}
}
{
"type": "receipt",
"operation": {
"cid": "6661b36bbc7ee21b182bd909f1e0cb14c40a8d9dd80badee0000000063db7cf9",
"isCompleted": true,
"error": {
"Code": 18,
"Message": "Transfer operation failed"
}
}
}
You can query the execution plan endpoint to track on status of progression, including operations performed by other routers involved in the trade.
Refer to the Execution Plan API section at the bottom of this section.
Example: Processing Secondary Intent Execution Request
Receive a notification message which indicates of a new execution plan submission for an asset in which is managed by your organization for transfer of ownership between investors in which you will need to approve. If no decision making is required, you can approve immediately, otherwise acknowledge and process it asynchronously.
curl --location --request POST 'http://<adapter url>/plan/approve' \
--header 'Content-Type: application/json' \
--data-raw '
{
"executionPlanId": "bank-uk:106:aa0ab7c6-3193-44c5-9760-c9e1e048e2a0"
}'
{
"cid": "12321321",
"isCompleted": false,
}
{
"cid": "12321321",
"isCompleted": true,
"approval": {
"status": "approved"
}
}
You can request from the router for details on the execution plan outlining the full workflow for the execution of the given transaction
curl --location 'http://http://<router url>/finapi/execution/bank-uk:106:aa0ab7c6-3193-44c5-9760-c9e1e048e2a0' \
--header 'Content-Type: application/json' \
--data ''
Refer to Execution Plan details at the bottom of this section.
Process request for status of execution plan approval if you chose to handle approval asynchronously
curl --location 'http://<router url>/operations/status/12321321' \
--header 'Content-Type: application/json' \
--data ''
{
"cid": "12321321",
"isCompleted": true,
"approval": {
"status": "approved"
}
}
{
"cid": "12321321",
"isCompleted": true,
"approval": {
"status": "rejected",
"failure": {
"failureType": "validationFailure",
"code": 1001,
"message": "Invalid input data provided"
}
}
}
Receive a message and respond back to secondary execution request for a transfer of tokens
The message contains a reference to the execution plan you received. Respond to the process as indicated in order to process this request in an asynchronous manner. The cid you will return will be used next in the operational status polling to receive the progress and completion on processing of the transfer request.
curl --location --request POST 'http://<adapter_url>/assets/transfer' \
--header 'Content-Type: application/json' \
--data-raw '{
"asset": {
"resourceId": "bank-uk:106:9909e355-df0e-4708-8121-cec192089418",
"type": "finp2p"
},
"source": {
"account": {
"finId": "03c7c9d6b96a678f549c4dd5407eabffc779cf665846846f1a8bb5d5e63cedc8e3",
"type": "finId"
},
"finId": "03c7c9d6b96a678f549c4dd5407eabffc779cf665846846f1a8bb5d5e63cedc8e3"
},
"destination": {
"account": {
"finId": "02ae39c9adaff2485deb65ac3749261089d268874c55ca73c5bd83815154b6ea9c",
"type": "finId"
},
"finId": "02ae39c9adaff2485deb65ac3749261089d268874c55ca73c5bd83815154b6ea9c"
},
"executionContext": {
"executionPlanId": "execution/bank-uk:106:aa0ab7c6-3193-44c5-9760-c9e1e048e2a0",
"instructionSequenceNumber": 2
},
"nonce": "39fc89491a957fc72e12d7eb703c0bd23cd5c54ff612b97f0000000066d97bb6",
"quantity": "10000",
"settlementRef": "",
"signature": {
"signature": "b1444d7981bd543a24510a178a21a917ee261bcb531d4ef4dbee318e90a4992b30c995f232f87a3d993d8c7e4814cfe7552b6f9825de6dfe8b19b61ab7ffba0b",
"template": {
"hash": "5413f45cc5b03b0036f418813c96cbe469efe3f13395c19ff330955858636349",
"hashGroups": [
{
"fields": [
{
"name": "nonce",
"type": "bytes",
"value": "39fc89491a957fc72e12d7eb703c0bd23cd5c54ff612b97f0000000066d97bb6"
},
{
"name": "operation",
"type": "string",
"value": "transfer"
},
{
"name": "assetType",
"type": "string",
"value": "finp2p"
},
{
"name": "assetId",
"type": "string",
"value": "citi:102:d0c3eb56-0fff-4670-adfd-ad291a4314c3"
},
{
"name": "srcAccountType",
"type": "string",
"value": "finId"
},
{
"name": "srcAccount",
"type": "string",
"value": "02fd7923740a775c95ce17e9bb7239ff9096689f70db9263a7efb9a9ad08e9fed7"
},
{
"name": "dstAccountType",
"type": "string",
"value": "finId"
},
{
"name": "dstAccount",
"type": "string",
"value": "0354916de33408d1e30f638fc5920a57166867ded48c0f9098baf5f1a6c1c76676"
},
{
"name": "amount",
"type": "string",
"value": "1"
}
],
"hash": "1d36e25dcd84e29597d9e923446cb13930a8e59bf6cbbca02ee5909e290443a2"
},
{
"fields": [
{
"name": "assetType",
"type": "string",
"value": "fiat"
},
{
"name": "assetId",
"type": "string",
"value": "USD"
},
{
"name": "srcAccountType",
"type": "string",
"value": "finId"
},
{
"name": "srcAccount",
"type": "string",
"value": "0354916de33408d1e30f638fc5920a57166867ded48c0f9098baf5f1a6c1c76676"
},
{
"name": "dstAccountType",
"type": "string",
"value": "finId"
},
{
"name": "dstAccount",
"type": "string",
"value": "02fd7923740a775c95ce17e9bb7239ff9096689f70db9263a7efb9a9ad08e9fed7"
},
{
"name": "amount",
"type": "string",
"value": "1"
}
],
"hash": "e18bc18f23990e4a155bdce1afa9b15ef118624133b9af6401e84814a9d9bcf3"
}
],
"type": "hashList"
}
}
}'
{
"cid": "08d53ff8f3aa9ec71476002facf319b567ddfbd80ab7c1d50000000063d7e00d",
"isCompleted": false,
"response": {
}
}
Process polling request to check on status of execution
curl --location --request POST 'http://<adapter_url>/operations/08d53ff8f3aa9ec71476002facf319b567ddfbd80ab7c1d50000000063d7e00d' \
--header 'Content-Type: application/json' \
--data-raw '
{
}'
{
"cid": "08d53ff8f3aa9ec71476002facf319b567ddfbd80ab7c1d50000000063d7e00d",
"isCompleted": false,
"response": {
}
}
{
"type": "receipt",
"operation": {
"cid": "08d53ff8f3aa9ec71476002facf319b567ddfbd80ab7c1d50000000063d7e00d",
"isCompleted": true,
"response": {
"asset": {
"resourceId": "bank-uk:102:f96240dc-8365-475b-84e2-9611bb5e5bc0",
"type": "finp2p"
},
"id": "f86ea694-2638-4b8d-a407-bfe03217e07a",
"quantity": "100000",
"source": {
"account": {
"finId": "037fcf0db48dceb2195bd0fc24a4dab68c5731b5b8c141f63d017969a55570c4e9",
"type": "finId"
},
"finId": "037fcf0db48dceb2195bd0fc24a4dab68c5731b5b8c141f63d017969a55570c4e9"
},
"destination": {
"account": {
"finId": "02af7088141665f96ce1eb3db1bb2b45af58a8b3a09ee0dd64ee921e1b197fe2d5",
"type": "finId"
},
"finId": "02af7088141665f96ce1eb3db1bb2b45af58a8b3a09ee0dd64ee921e1b197fe2d5"
},
"transactionDetails": {
"transactionId": "1031C909828C9BB3ED572A0066B1565357A993409DE4330E8319D2D0B7553D64"
},
"timestamp": 1702203549,
"operationType": "transfer"
}
}
}
{
"type": "receipt",
"operation": {
"cid": "08d53ff8f3aa9ec71476002facf319b567ddfbd80ab7c1d50000000063d7e00d",
"isCompleted": true,
"error": {
"Code": 15,
"Message": "Transfer failed"
}
}
}
Process request for the balance of a given asset
curl --location --request GET 'http://<adapter_url>/assets/getBalance' \
--header 'Content-Type: application/json' \
--data-raw '
{
"owner": {
"account": {
"type": "finId",
"finId": "02d6fea8153a9a64d13e198379dfd4ea3945d03198770e0e5b94f556c4e69f6835"
}
},
"asset": {
"type": "finp2p",
"resourceId": "bank-uk:102:f96240dc-8365-475b-84e2-9611bb5e5bc0"
}
}'
{
"asset": {
"type": "finp2p",
"resourceId": "bank-uk:102:f96240dc-8365-475b-84e2-9611bb5e5bc0"
},
"balance": "100000"
}
Process request for a receipt of a previously performed transaction
Firstly The get receipt will be sent, in which you can response with with acknowledgement and process the request asynchronously.
curl --location --request GET 'http://<adapter_url>/assets/getReceipt/73358f79-3b3a-4080-90c3-cbb7edd26668' \
--header 'Content-Type: application/json' \
--data-raw ''
{
"cid": "08d53ff8f3aa9ec71476002facf319b567ddfbd80ab7c1d50000000063d7e00d",
"isCompleted": false,
"response": {
}
}
And the response to the get status operation to provide receipt.
curl --location --request POST 'http://<adapter_url>/operations/08d53ff8f3aa9ec71476002facf319b567ddfbd80ab7c1d50000000063d7e00d' \
--header 'Content-Type: application/json' \
--data-raw '
{
}'
{
"cid": "08d53ff8f3aa9ec71476002facf319b567ddfbd80ab7c1d50000000063d7e00d",
"isCompleted": true,
"response": {
<Insert the receipt here the same as previously provided>
}
}
Example: Importing Transactions Manually
The following allows you to send manual updates to Ownera for the case adjustments need to be performed on the user token holdings for the given asset.
The "source" section should be used to take out of the given account the specified amount. The "destination" section should be used to put into the given account the specified amount. Meaning, it incrementally increase or decreases the balance of the quantity specified.
transactionId should be the Id generated on the transacting system, e.g. blockchain ID. OperationID could be used to tie the operation of this import to a previous request that it might be related to, e.g., a deposit request that is related to this.
curl --location --request POST 'http://<router_url>finp2p/ledger/transaction/import' \
--header 'Content-Type: application/json' \
--data-raw '{
"transactions": [
{
"id": "fa5e7391-fce9-4cf6-98e4-b53cdf38d79d",
"asset": {
"type": "finp2p",
"resourceId": "bank-us:102:4b2fc630-bc23-4337-be44-97f12875f835"
},
"quantity": "10000",
"timestamp": 1673863664,
"destination": {
"finId": "02ae39c9adaff2485deb65ac3749261089d268874c55ca73c5bd83815154b6ea9c",
"account": {
"type": "finId",
"finId": "02ae39c9adaff2485deb65ac3749261089d268874c55ca73c5bd83815154b6ea9c",
"orgId": "bank-uk"
}
},
"transactionDetails": {
"transactionId": "73358f79-3b3a-4080-90c3-cbb7edd26668",
"operationId": "cbb7edd26668-3b3a-4080-90c3-cbb7edd234668"
}
}
]
}'
curl --location --request POST 'http://<router_url>finp2p/ledger/transaction/import' \
--header 'Content-Type: application/json' \
--data-raw '{
"transactions": [
{
"id": "fa5e7391-fce9-4cf6-98e4-b53cdf38d79d",
"asset": {
"type": "finp2p",
"resourceId": "bank-us:102:4b2fc630-bc23-4337-be44-97f12875f835"
},
"quantity": "10000",
"timestamp": 1673863664,
"source": {
"finId": "02ae39c9adaff2485deb65ac3749261089d268874c55ca73c5bd83815154b6ea9c",
"account": {
"type": "finId",
"finId": "02ae39c9adaff2485deb65ac3749261089d268874c55ca73c5bd83815154b6ea9c",
"orgId": "bank-uk"
}
},
"transactionDetails": {
"transactionId": "73358f79-3b3a-4080-90c3-cbb7edd26668",
"operationId": "cbb7edd26668-3b3a-4080-90c3-cbb7edd234668"
}
}
]
}'
Execution Plan Details
**Execution Plan**
Field | Sub-Field | Description |
---|---|---|
id | Execution plan unique ID | |
intentID | The intent ID in which was created by the trading platform processing the intent request. | |
instructions | This section will have repeating sections with each describing the step and the details to perform the execution. | |
sequence | The step in the execution plan, starting from 1. | |
type | The operation being performed at the given instruction sequence number, as follows: | |
1 | The step in the execution plan which requests the hold of the funds for the transaction. | |
2 | The step in the execution plan which requests the issuance or transfer of the asset tokens. | |
3 | The step in the execution plan which requests the release of the funds to the issuer or seller. | |
Organizations | Specifies the organization(s) ID that are involved in performing the step in the execution plan. | |
Participants | This part of the message will outline all the participants that are involved in the execution plan split into two roles: Contributor - the participants respond to requests and perform actions. Observers - the participants “listens” to execution plan progression (and can display progress to the end-user). | |
creationTimeStamp | The time the execution plan was submitted, in epoch format. | |
currentInstructionSequence | Provides you with the current sequence number being waited on or executed. | |
executionPlanStatus | Provides you with the overall status of the execution plan, which can be one of the following values: Pending - pending start of execution plan or pending the specific sequence number in which the plan is at. Approved - Plan is approved Completed - Plan is fully successfully executed Failed - Plan has failed in execution Rejected - Plan was rejected by a given participant |
Cryptographic Keys and Signature Verifications
FinP2P employs a standardized cryptographic identity (FinID) based on secp256k1 keys. However, integrating this identity across different DLTs, each with varying cryptographic capabilities, requires tailored strategies. Ownera has identified two major approaches, that can be implemented based on the blockchain in which the adapter will be developed against. These strategies prioritize the cryptographic security of both user and organizational operations, and can be combined to create custom solutions that best fit the specific DLT environment.
1. Ledger Cryptographic Strategy:
Description: This approach relies on the DLT built in support for secp256k1 keys. The user's FinP2P identity directly maps to their underlying identity on the DLT, allowing for direct verification and eliminating the need for additional key management by the organization.
When to use: When the DLT natively supports secp256k1 cryptography.
Requires: A FinP2P Operator smart contract deployment that will provide permissioning by the blockchain ledger to receive validate and transact for a given investor key. Including for the FinP2P off chain signature validation and verification that is sent in the payload by the router for the given transaction.
FinP2P Operator Contract Management:
- User Interaction: FinP2P Sell Side router interacts with the operator contract, which acts as the DLT interface.
- Forwarding and Verification: The operator contract receives the user's transaction, verifies their FinP2P signature, and transaction with the corresponding token contract.
Allowance in Operator Contracts: In the context of interacting with token contracts, such as ERC20, via the FinP2P operator contract, an essential step involves the establishment of an allowance. This procedure is fundamental for enabling the FinP2P operator contract, which acts as an intermediary, to manage token transactions on behalf of the investor. For more Details.
Examples of Blockchains: EVM type blockchains such as Etherum, Polygon, Tezos, Besu, Quorum and other networks.
2. Account Mapping Strategy:
Description: When the DLT does not support secp256k1 or connection to tokenization platform is via APIs, a local account needs to be created that will be used to map to the user FinP2P identity. The Adapter maintains the mapping between the FinP2P Identity and the tokenization unique identity, In addition optionally enforcing signature validation on chain, based on the FinP2P signature of the investor, Transactions are performed with the local account and validated on chain against the FinP2P Identity.
When to use: When the DLT does not support secp256k1 cryptography.
Requires: A Sell Side Organization to manage local account keys and to optionally add an on chain validation procedure.
FinP2P OnChain Signature Verification:
This strategy leverages smart contracts on DLTs that support them.
The token contract validates the off chain FinP2P cryptographic signature for the underlying action.
Examples of Blockchains: Corda, Canton and other blockchains.
Updated about 1 month ago