Skip to main content

Payment

Information about a transaction on an external chain that is classified as a native currency payment. Each supported blockchain specifies how a payment transaction should be formatted to be provable using this attestation type. These provable payments mimic traditional banking transactions where entity A sends a native currency to entity B, with an optional payment reference.

Supported chains

  • BTC (Bitcoin)
  • DOGE (Dogecoin)
  • XRP (XRP Ledger)
  • Test networks: testBTC (Bitcoin Testnet v3), testDOGE, testXRP

Request

FieldSolidity TypeDescription
transactionIdbytes32Unique ID of the payment transaction.
inUtxouint256For UTXO-based chains, this is the index of the transaction input associated with the source address. Set to 0 for non-UTXO chains.
utxouint256For UTXO-based chains, this is the index of the transaction output associated with the receiving address. Set to 0 for non-UTXO chains.

Response

FieldSolidity TypeDescription
blockNumberuint64The block number in which the transaction is included.
blockTimestampuint64The timestamp of the block containing the transaction.
sourceAddressHashbytes32Standardized address hash of the source address.
sourceAddressesRootbytes32The root of the Merkle tree of the source addresses.
receivingAddressHashbytes32Standardized address hash of the receiving address. Returns a zero 32-byte string if the transaction status is not successful.
intendedReceivingAddressHashbytes32Standardized address hash of the intended receiving address if the transaction failed.
spentAmountint256Amount (in minimal units) spent by the source address.
intendedSpentAmountint256Intended amount (in minimal units) to be spent by the source address, relevant if the transaction status is unsuccessful.
receivedAmountint256Amount (in minimal units) received by the receiving address.
intendedReceivedAmountint256Intended amount (in minimal units) to be received by the receiving address if the transaction failed.
standardPaymentReferencebytes32Standard payment reference.
oneToOneboolIndicates if the transaction involves only one source and one receiver.
statusuint8Transaction success status.
Standard Payment Reference

If a transaction has no standardPaymentReference, it is set to default value, thus, zero value reference should be used with caution.

Verification Process

  1. The transaction identified by transactionId is fetched from the relevant blockchain node or indexer.
  2. If the transaction cannot be retrieved or is in a block with insufficient confirmations, the attestation request is rejected.
  3. Once the transaction data is fetched, a payment summary is computed according to the chain-specific rules.
    • If the payment summary is successfully generated, the response is populated using this data.
    • If the summary cannot be computed, the attestation request is rejected.
  4. The fields blockNumber and blockTimestamp are extracted from the block data if they are not directly available in the transaction data.
    • For Bitcoin and Dogecoin, the blockTimestamp is derived from the mediantime of the block.
    • For XRPL, the blockTimestamp is derived from the close time of the ledger, converted to UNIX time.
Lowest used timestamp

For the lowestUsedTimestamp parameter, the blockTimestamp of the transaction is used.

Payment Summary

A payment summary consolidates all relevant data about a transaction that represents a payment. This is particularly focused on payments between one source account (address) and one target account.

  • UTXO Blockchains (e.g., BTC, DOGE): Payments can aggregate inputs from multiple addresses and distribute them to multiple outputs. The summary here is computed based on specified input and output indices that identify addresses of interest.
  • XRPL: Supports various transaction types, but a payment summary is only fully calculated for transactions of type Payment.

Structure

The summary includes the fields detailed in the table below. The interpretation of certain fields may vary based on the blockchain. Chain-specific explanations are provided in the sections that follow.

FieldDescription
transactionIdThe unique identifier of the transaction.
transactionStatusThe success status of the transaction.
standardPaymentReferenceA reference defined in the standard payment reference.
oneToOneIndicates if the transaction involves a single sender and a single receiver.
sourceAddressThe originating address involved in the transaction.
sourceAddressesRootThe root of the Merkle tree of the source addresses.
spentAmountThe total amount spent by the source address.
intendedSourceAmountThe expected amount intended to be sent from the source address.
receivingAddressThe target address receiving the payment.
intendedReceivingAddressThe expected target address intended to receive the payment.
receivedAmountThe actual amount received by the receiving address.
intendedReceivingAmountThe expected amount intended to be received.
note
  • Standard Address Hashes: Standard address hashes can be derived from addresses.
  • If transactionStatus is not SUCCESS, the receivingAddress is set to an empty string, and its hash defaults to a zeroed 32-byte string.
  • Standard Addresses Root is the root of the Merkle tree build on double keccak256 hashes of the all source addresses of the transaction.

UTXO chains (Bitcoin and Dogecoin)

The payment summary for Bitcoin and Dogecoin is derived using specified indices for a transaction input and output.

  • Conditions:
    • If the specified input or output does not exist, or lacks an address (e.g., outputs using OP_RETURN), no summary is generated.
    • Coinbase transactions are not summarized.
  • Data Sources:
    • For Bitcoin, all transaction details are retrieved using the getrawtransaction endpoint (verbosity 2) and getblock. This requires a Bitcoin node version ≥ 25.0.
    • For Dogecoin, since getrawtransaction with verbosity 2 is not supported, alternative methods must be used to access input transaction data.
FieldDescription
transactionIdThe transaction ID (txid). For SegWit transactions, this differs from hash.
oneToOnetrue if only sourceAddress is present in inputs, and outputs include only receivingAddress, sourceAddress (for change), or OP_RETURN.
sourceAddressAddress of the specified input.
spentAmountTotal value of all inputs with sourceAddress minus total value of all outputs to sourceAddress.
intendedSourceAmountSame as spentAmount.
receivingAddressAddress of the specified output.
intendedReceivingAddressAlways matches receivingAddress.
receivedAmountTotal value of outputs to receivingAddress minus total value of inputs from receivingAddress.
intendedReceivingAmountSame as receivedAmount.

Account-based chains (XRPL)

The payment summary on XRPL is applicable only for transactions of type Payment.

  • Conditions:
    • Only Payment transactions are summarized; other transaction types are ignored.
    • A successful payment has exactly one sender and at most one receiver. If unsuccessful, no receiver is recorded.
  • Data Sources:
    • Transaction details are obtained via the tx method.
    • Changes made by the transaction are recorded in the meta field (or metaData if fetched via the ledger method) under AffectedNodes. Balance changes are found within ModifiedNodes, by comparing FinalFields and PreviousFields.
FieldDescription
transactionIdTransaction hash found in the hash field.
oneToOneAlways true, as each Payment transaction has exactly one sender and at most one receiver.
sourceAddressAddress that sent the payment, reducing its balance.
spentAmountAmount by which sourceAddress's balance was reduced.
intendedSourceAmountCalculated as Amount + Fee. If transactionStatus is SUCCESS, it matches spentAmount.
receivingAddressAddress that received the payment. If unsuccessful, this is an empty string.
intendedReceivingAddressAddress specified in the Destination field.
receivedAmountAmount by which the receivingAddress's balance was increased. Can be zero if the transaction failed.
intendedReceivingAmountExpected increase in intendedReceivingAddress's balance if successful. Found in the Amount field. Matches spentAmount if successful.

Standard payment reference

A standard payment reference is defined as a 32-byte sequence that can be added to a payment transaction, in the same way that a payment reference is attached to a traditional banking transaction.

Bitcoin and Dogecoin

  • Uses OP_RETURN to store references.
  • A transaction is considered to have a standardPaymentReference defined if it has:
    • Exactly one output UTXO with OP_RETURN script, and
    • The script is of the form OP_RETURN <reference\> or 6a<lengthOfReferenceInHex\><reference\> in hex, where the length of the reference is 32 bytes.
  • Then 0x<reference\> is the standardPaymentReference.

XRPL

  • Uses the memoData field.
  • A transaction has a standardPaymentReference if it has:
    • Exactly one Memo, and
    • The memoData of this field is a hex string that represents a byte sequence of exactly 32 bytes.
  • This 32-byte sequence defines the standardPaymentReference.

Transaction success status

Transactions on different blockchains have various success statuses. Some blockchains may include transactions even if they failed to execute as intended.

StatusCode
SUCCESS0
SENDER_FAILURE1
RECEIVER_FAILURE2

Bitcoin and Dogecoin

It is not possible to include an unsuccessful transaction in a Bitcoin or Dogecoin block. Hence, if a transaction is included on a confirmed block, its status is "SUCCESS."

XRPL

On XRPL, some transactions that failed (based on the reason for failure) can be included in a confirmed block.

  • tesSUCCESS: Transaction successful.
  • tec-class codes: Indicate reasons for failure, such as:
    • tecDST_TAG_NEEDED: Missing required destination tag.
    • tecNO_DST: Nonexistent or unfunded destination address.
    • tecNO_PERMISSION: Source address lacks permission to send funds.

Standard address hash

The standard address hash is defined as the keccak256 hash of the standard address as a string:

keccak256(standardAddress)
standard address

If an address is case insensitive, the standard address is lowercase. If an address is case sensitive, there is always only one (correct) form of the address.

Examples:

ChainStandard AddressStandard Address Hash
Bitcoin (Base58)1FWQiwK27EnGXb6BiBMRLJvunJQZZPMcGd0x8f651b6990a4754c58fcb5c5a11f4d40f8ddfdeb0e4f67cdd06c27f8d7bcbe33
Bitcoin (Bech32)bc1qrmvxmwgqfr5q4fvtvnxczwxwm966n53c4lxh4v0xf75dc4b039ac72e037d67199bb92fa25db32b2210954df99637428473d47cedf
DogecoinDL2H9FuaXsxivSs1sRtuJ8uryosyAj62XX0x51064c88c6b8e9d58b2abeae37a773bf89c9b279f8a05fa0ac0e81ebe13d2f4f
XRPLrDsbeomae4FXwgQTJp9Rs64Qg9vDiTCdBv0xa491aed10a1920ca31a85ff29e4bc410705d37d4dc9e690d4d500bcedfd8078f

Finality

Blockchains have varying confirmation depths to consider blocks as final.

ChainchainIdConfirmations requiredConfirmation time
Bitcoin06≈60 mins
Dogecoin260≈60 mins
XRPL33≈12 seconds

Contract interface

Sourced from IPayment.sol on GitHub.

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.6 <0.9;

/**
* @custom:name IPayment
* @custom:id 0x01
* @custom:supported BTC, DOGE, XRP
* @author Flare
* @notice A relay of a transaction on an external chain that is considered a payment in a native currency.
* Various blockchains support different types of native payments. For each blockchain, it is specified how a payment
* transaction should be formed to be provable by this attestation type.
* The provable payments emulate traditional banking payments from entity A to entity B in native currency with an
* optional payment reference.
* @custom:verification The transaction with `transactionId` is fetched from the API of the blockchain node or
* relevant indexer.
* If the transaction cannot be fetched or the transaction is in a block that does not have a sufficient
* [number of confirmations](/specs/attestations/configs.md#finalityconfirmation), the attestation request is rejected.
*
* Once the transaction is received, the payment summary is computed according to the rules for the source chain.
* If the summary is successfully calculated, the response is assembled from the summary.
* `blockNumber` and `blockTimestamp` are retrieved from the block if they are not included in the transaction data.
* For Bitcoin and Dogecoin, `blockTimestamp` is mediantime of the block.
* For XRPL, `blockTimestamp` is close time of the ledger converted to UNIX time.
*
* If the summary is not successfully calculated, the attestation request is rejected.
* @custom:lut `blockTimestamp`
* @custom:lutlimit `0x127500`, `0x127500`, `0x127500`
*/
interface IPayment {
/**
* @notice Toplevel request
* @param attestationType ID of the attestation type.
* @param sourceId ID of the data source.
* @param messageIntegrityCode `MessageIntegrityCode` that is derived from the expected response.
* @param requestBody Data defining the request. Type (struct) and interpretation is determined
* by the `attestationType`.
*/
struct Request {
bytes32 attestationType;
bytes32 sourceId;
bytes32 messageIntegrityCode;
RequestBody requestBody;
}

/**
* @notice Toplevel response
* @param attestationType Extracted from the request.
* @param sourceId Extracted from the request.
* @param votingRound The ID of the State Connector round in which the request was considered.
* @param lowestUsedTimestamp The lowest timestamp used to generate the response.
* @param requestBody Extracted from the request.
* @param responseBody Data defining the response. The verification rules for the construction
* of the response body and the type are defined per specific `attestationType`.
*/
struct Response {
bytes32 attestationType;
bytes32 sourceId;
uint64 votingRound;
uint64 lowestUsedTimestamp;
RequestBody requestBody;
ResponseBody responseBody;
}

/**
* @notice Toplevel proof
* @param merkleProof Merkle proof corresponding to the attestation response.
* @param data Attestation response.
*/
struct Proof {
bytes32[] merkleProof;
Response data;
}

/**
* @notice Request body for Payment attestation type
* @param transactionId ID of the payment transaction.
* @param inUtxo For UTXO chains, this is the index of the transaction input with source address.
* Always 0 for the non-utxo chains.
* @param utxo For UTXO chains, this is the index of the transaction output with receiving address.
* Always 0 for the non-utxo chains.
*/
struct RequestBody {
bytes32 transactionId;
uint256 inUtxo;
uint256 utxo;
}

/**
* @notice Response body for Payment attestation type
* @param blockNumber Number of the block in which the transaction is included.
* @param blockTimestamp The timestamp of the block in which the transaction is included.
* @param sourceAddressHash Standard address hash of the source address.
* @param sourceAddressesRoot The root of the Merkle tree of the source addresses.
* @param receivingAddressHash Standard address hash of the receiving address.
* The zero 32-byte string if there is no receivingAddress (if `status` is not success).
* @param intendedReceivingAddressHash Standard address hash of the intended receiving address.
* Relevant if the transaction is unsuccessful.
* @param spentAmount Amount in minimal units spent by the source address.
* @param intendedSpentAmount Amount in minimal units to be spent by the source address.
* Relevant if the transaction status is unsuccessful.
* @param receivedAmount Amount in minimal units received by the receiving address.
* @param intendedReceivedAmount Amount in minimal units intended to be received by the receiving address.
* Relevant if the transaction is unsuccessful.
* @param standardPaymentReference Standard payment reference of the transaction.
* @param oneToOne Indicator whether only one source and one receiver are involved in the transaction.
* @param status Success status of the transaction: 0 - success, 1 - failed by sender's fault,
* 2 - failed by receiver's fault.
*/
struct ResponseBody {
uint64 blockNumber;
uint64 blockTimestamp;
bytes32 sourceAddressHash;
bytes32 sourceAddressesRoot;
bytes32 receivingAddressHash;
bytes32 intendedReceivingAddressHash;
int256 spentAmount;
int256 intendedSpentAmount;
int256 receivedAmount;
int256 intendedReceivedAmount;
bytes32 standardPaymentReference;
bool oneToOne;
uint8 status;
}
}