Skip to content

Latest commit

 

History

History
431 lines (311 loc) · 21.4 KB

sep-0008.md

File metadata and controls

431 lines (311 loc) · 21.4 KB

Preamble

SEP: 0008
Title: Regulated Assets
Author: Tomer Weller <@tomerweller>, Howard Chen <@howardtw>, Christian Peters (DSTOQ) <@shredding>, Leigh McCulloch <@leighmcculloch>
Status: Active
Created: 2018-08-22
Updated: 2022-02-21
Version 1.7.4

Simple Summary

Regulated Assets are assets that require an issuer’s approval (or a delegated third party’s approval, such as a licensed securities exchange) on a per-transaction basis. It standardizes the identification of such assets as well as defines the protocol for performing compliance checks and requesting issuer approval.

Target Audience

Asset issuers, issuance platforms, wallet developers, and exchanges

Motivation

Stellar is an ideal platform for issuing securities. Regulation sometimes requires asset issuers to monitor and approve each transaction involving issued assets and to enforce constraints. This is possible on the Stellar network today, however there exists no standard interface between wallets and issuers to negotiate approval.

Overview

Implementing a regulated asset requires these parts:

Regulated Assets Transaction Flow

  1. Wallet creates and signs a transaction.
  2. Wallet resolves asset information and detects that it's an asset that requires authorization.
    1. Wallet can detect whether an asset requires authorization by checking the authorization flags of the asset issuer's account
  3. Wallet detects that it's a regulated asset.
    1. Wallet can detect whether an asset is a regulated asset by checking for an approval server via SEP-1 stellar.toml set up by the issuer
  4. Wallet sends the transaction to the approval server.
  5. Approval server determines whether the transaction should be approved.
  6. Wallet handles approval response:
    1. Success? Transaction has been approved and signed by the issuer.
    2. Revised? Transaction has been revised to be made compliant, and signed by the issuer. Wallet will show the changes to the user and ask them to sign the new transaction. It is important to understand that an approval service could respond with a different transaction.
    3. Pending? The issuer could not determine whether to approve this transaction at the moment. Wallet can re-send the same transaction to the approval server later (Return to 3).
    4. Action Required? Transaction requires a user action to be completed. Wallet will present the attached action url as a clickable link, along with the attached message and an option to resubmit once the action has been taken.
    5. Rejected? Wallet should display the associated error message to the user.

Authorization Flags

Regulated asset issuers must have both Authorization Required and Authorization Revocable flags set on their account. This allows the issuer to grant and revoke authorization to transact the asset at will.

SEP-1 stellar.toml

Issuers advertise the existence of an Approval Service through their SEP-1 stellar.toml file. This is done in the [[CURRENCIES]] section as different assets can have different requirements.

Fields:

These fields are listed in the [[CURRENCIES]] section:

  • regulated is a boolean indicating whether or not this is a regulated asset. If missing, false is assumed.
  • approval_server is the URL of an approval service that signs validated transactions.
  • approval_criteria is a human readable string that explains the issuer's requirements for approving transactions.

Example

[[CURRENCIES]]
code="GOAT"
issuer="GD5T6IPRNCKFOHQWT264YPKOZAWUMMZOLZBJ6BNQMUGPWGRLBK3U7ZNP"
regulated=true
approval_server="https://goat.io/tx_approve"
approval_criteria="The goat approval server will ensure that transactions are compliant with NFO regulation"

Transaction Composition

A transaction that an approval server will approve consists of operations that authorize the clients' accounts, transact the regulated asset, and deauthorize the clients' accounts. If a transaction is built this way, there will be no point where the clients' accounts have open, authorized trustlines to run unapproved operations because of transaction atomocity. A transaction as described above can be built by wallets preemptively or by approval servers in order to make it compliant when the revised status is applicable.

Depending on whether issuers want to allow the clients' accounts to maintain offers, issuers can leave the accounts in the AUTHORIZE_TO_MAINTAIN_LIABILITIES_FLAG state or completely deauthorize the accounts when completing the operation. See [CAP-18] for the fine-grained control of authorization.

An example of the transaction where the issuer allow the client's account to maintain offers:

Operation 1: AllowTrust op where issuer fully authorizes account A, asset X
Operation 2: Account A manages offer to buy or sell X
Operation 3: AllowTrust op where issuer sets account A, asset X to AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG state

An example of the transaction where the issuer do not allow the clients' accounts to maintain offers:

Operation 1: AllowTrust op where issuer fully authorizes account A, asset X
Operation 2: AllowTrust op where issuer fully authorizes account B, asset X
Operation 3: Payment from A to B
Operation 4: AllowTrust op where issuer fully deauthorizes account B, asset X
Operation 5: AllowTrust op where issuer fully deauthorizes account A, asset X

Approval Server

Approval server is a single endpoint that receives a signed transaction, checks for compliance, and signs it on success.

The specification of an approval server is defined as follows:

Cross-Origin Headers

Cross-origin requests to the endpoints in this specification must be supported by the server so that web clients from other sites can use the endpoint(s). Valid CORS headers and requests that allow any domain origin to access the endpoints in this specification are necessary. The following HTTP header and support of preflight OPTIONS requests are the smallest change necessary. More complex headers configurations can be used.

Access-Control-Allow-Origin: *

Content Type

Approval server accepts requests in the following Content-Types:

  • application/x-www-form-urlencoded
  • application/json

Approval server responds with the Content-Type:

  • application/json

POST Endpoint

Request

This endpoint accepts HTTP POST requests containing a single tx parameter.

Name Type Description
tx string A base64 encoded transaction envelope XDR signed by the user. This is the transaction that will be tested for compliance and signed on success.
Example (JSON)
{
  "tx": "AAAAAHAHhQtYBh5F2zA6..."
}
Example (Form)
tx=AAAAAHAHhQtYBh5F2zA6...

Responses

All responses will contain a top level status parameter that indicates the type of the result.

Success

This response means that the transaction was found compliant and signed without being revised.

A success response will have a 200 HTTP status code and success as the status value.

Parameters:

Name Type Description
status string "success"
tx string Transaction envelope XDR, base64 encoded. This transaction will have both the original signature(s) from the request as well as one or multiple additional signatures from the issuer.
message string (optional) A human readable string containing information to pass on to the user.
Example
{
  "status": "success",
  "tx": "AAAAAHAHhQtYBh5F2zA6..."
}
Revised

This response means that the transaction was revised to be made compliant.

A revised transaction should only add operations to the transaction to make it compliant and should not change the intent of the operations that the user has included. If an operation the user has included cannot be made compliant by adding another operation, the rejected status should be returned. For example, if a user includes a payment operation to an account not authorized, the server can revise the transaction including allow trust operations to authorize the account. But, for example, if a user includes a payment operation for $1000, but amounts over $100 are not compliant, the transaction should not be revised lowering the amount and instead be rejected, because altering the payment amount would change the intent of the transaction.

A revised response will have a 200 HTTP status code and revised as the status value.

It is important for the user to examine the revised transaction before re-signing it. More specifically, the user should make sure that the revised transaction does not contain any additional operations whose source account matches the source account of the original operation(s).

Parameters:

Name Type Description
status string "revised"
tx string Transaction envelope XDR, base64 encoded. This transaction is a revised compliant version of the original request transation, signed by the issuer.
message string A human readable string explaining the modifications made to the transaction to make it compliant.
Example
{
  "status": "revised",
  "tx": "AAAAAHAHhQtYBh5F2zA6...",
  "message": "Authorization and deauthorization operations were added."
}
Pending

This response means that the issuer could not determine whether to approve the transaction at the time of receiving it. Wallet can re-submit the same transaction at a later point in time.

A pending response will have a 200 HTTP status code and pending as the status value.

Parameters:

Name Type Description
status string "pending"
timeout integer Number of milliseconds to wait before submitting the same transaction again. Use 0 if the wait time cannot be determined.
message string (optional) A human readable string containing information to pass on to the user.
Example
{
  "status": "pending",
  "message": "Futher verifications are required due to..."
}
Action Required

This response means that the user must complete an action before this transaction can be approved. The approval service will provide a URL that facilitates the action. Upon completion, the user will resubmit the transaction.

The action the user must complete is intended to be displayed in a browser. The client may be able to skip displaying the URL in the browser if the server supports the POST action_method, the client can supply the requested details, and the server's final response to the POST indicates no further action is required.

An action_required response will have a 200 HTTP status code and action_required as the status value.

Parameters:

Name Type Description
status string "action_required"
message string A human readable string containing information regarding the action required.
action_url string A URL that allows the user to complete the actions required to have the transaction approved.
action_method string (optional) GET or POST, indicating the type of request that should be made to the action_url. If not provided, GET is assumed.
action_fields string[] (optional) An array of additional fields defined by SEP-9 Standard KYC / AML fields that the client may optionally provide to the approval service when sending the request to the action_url so as to circumvent the need for the user to enter the information manually.
Example
{
  "status": "action_required",
  "message": "Please provide an email address and mobile phone number.",
  "action_url": "https://...",
  "action_method": "POST",
  "action_fields": ["email_address", "mobile_number"]
}
Following the Action URL

If the action_method field is provided, the client uses the method specified when making the request to the action_url.

If the action_fields field is provided, the client may optionally supply the fields if they already have the information so as to reduce the user's need to re-enter the information. If the requested fields contain sensitive or personal information, the server should use action_method POST so that sensitive information is not transmitted in a URL.

When no further action is to be taken by the user, the client can re-submit the transaction to the approval server. However, there is no guarantee to receive a "success" status in the following call. Clients should expect to receive a response with any of the status.

There are two possible values for action_method.

  • In the case that action_method is GET, or not defined:

    Fields requested may be passed as query parameters in the action_url. The URL should be opened in a browser. Any fields passed should allow the server to proceed without collecting user information, or at the least pre-fill the provided information.

  • In the case that action_method is POST:

    Fields requested may be passed as JSON in the request body, using content type application/json. The server should respond to the POST with a HTTP response with status code 200, content-type application/json. The body should contain either an acknowledgement that the POST was sufficient and no further action is required, or the next URL if action is still required. If a next URL is provided, it should be loaded in a browser for the user to complete any action required.

    Request Parameters:

    Any fields defined by SEP-9 Standard KYC / AML fields.

    Request Example:

    {
      "email_address": "[email protected]"
    }

    Response Parameters when no further action required:

    Name Type Description
    result string no_further_action_required.

    Response Example:

    {
      "result": "no_further_action_required"
    }

    Response Parameters when further action required:

    Name Type Description
    result string follow_next_url.
    next_url string A URL where the user can complete the required actions with all the parameters included in the original POST pre-filled or already accepted.
    message string (optional) A human readable string containing information regarding the further action required.

    Response Example:

    {
      "result": "follow_next_url",
      "next_url": "https://...",
      "message": "Please sign the terms of service."
    }
Rejected

This response means that the transaction is not compliant and could not be revised to be made compliant.

A rejected response will have a 400 HTTP status code and rejected as the status value.

Parameters:

Name Type Description
status string "rejected"
error string A human readable string explaining why the transaction is not compliant and could not be made compliant.
Example
{
  "status": "rejected",
  "error": "The destination account is blocked."
}

Best practices

  • Issuers are encouraged to use the revised status to make a transaction complaint when possible, since Wallets may not know how to form a transaction that is compliant with the server's criteria.
  • If a transaction was revised, ALWAYS explain the changes that were made to a transaction through the message parameter.
  • Wallet should inspect revised transactions and alert the user if any of the original operations are changed.
  • Core operations shouldn't be modified as that can be confusing and misleading. For example, if the user wishes to put an offer for 1000 GOATS but due to velocity limits they can only put an offer for 500 GOATS, it is better to error with a message than to change the amount.
  • Adding an upper timebound to a transaction can help the issuer ensure that their view of the world does not get out of sync.
  • Issuers can enforce additional fees by adding additional operations. For example, any transaction involving GOATs, will also send 0.1 GOAT to the issuer's account.

Discussion

Should my asset be a regulated asset?

Ideally, no. Implementing Regulated Assets should only be used when absolutely necessary, such as for regulatory reasons. It comes with a substantial operational overhead, added complexity, and burden for users. Issuers should only go down this route if it is absolutely required.

Why doesn’t the approval service submit transactions to the network?

  • Separation of concerns between signing transactions and submitting them.
  • Transactions might require more signatures from further regulated asset issuers.
  • Wallets can handle the failure from transaction submission based on the type of errors and reconstruct the transaction.
  • Scaling concerns as having the approval server submit transactions would require more infrastructure.

Implementations

Changelog

  • v1.7.4: Add link to the Approval Server reference implementation in Go. (#1135)