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
Field | Solidity Type | Description |
---|---|---|
transactionId | bytes32 | Unique ID of the payment transaction. |
inUtxo | uint256 | For UTXO-based chains, this is the index of the transaction input associated with the source address. Set to 0 for non-UTXO chains. |
utxo | uint256 | For UTXO-based chains, this is the index of the transaction output associated with the receiving address. Set to 0 for non-UTXO chains. |
Response
Field | Solidity Type | Description |
---|---|---|
blockNumber | uint64 | The block number in which the transaction is included. |
blockTimestamp | uint64 | The timestamp of the block containing the transaction. |
sourceAddressHash | bytes32 | Standardized address hash of the source address. |
sourceAddressesRoot | bytes32 | The root of the Merkle tree of the source addresses. |
receivingAddressHash | bytes32 | Standardized address hash of the receiving address. Returns a zero 32-byte string if the transaction status is not successful. |
intendedReceivingAddressHash | bytes32 | Standardized address hash of the intended receiving address if the transaction failed. |
spentAmount | int256 | Amount (in minimal units) spent by the source address. |
intendedSpentAmount | int256 | Intended amount (in minimal units) to be spent by the source address, relevant if the transaction status is unsuccessful. |
receivedAmount | int256 | Amount (in minimal units) received by the receiving address. |
intendedReceivedAmount | int256 | Intended amount (in minimal units) to be received by the receiving address if the transaction failed. |
standardPaymentReference | bytes32 | Standard payment reference. |
oneToOne | bool | Indicates if the transaction involves only one source and one receiver. |
status | uint8 | Transaction success status. |
If a transaction has no standardPaymentReference
, it is set to default value, thus, zero value reference should be used with caution.
Verification Process
- The transaction identified by
transactionId
is fetched from the relevant blockchain node or indexer. - If the transaction cannot be retrieved or is in a block with insufficient confirmations, the attestation request is rejected.
- 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.
- The fields
blockNumber
andblockTimestamp
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.
- For Bitcoin and Dogecoin, the
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.
Field | Description |
---|---|
transactionId | The unique identifier of the transaction. |
transactionStatus | The success status of the transaction. |
standardPaymentReference | A reference defined in the standard payment reference. |
oneToOne | Indicates if the transaction involves a single sender and a single receiver. |
sourceAddress | The originating address involved in the transaction. |
sourceAddressesRoot | The root of the Merkle tree of the source addresses. |
spentAmount | The total amount spent by the source address. |
intendedSourceAmount | The expected amount intended to be sent from the source address. |
receivingAddress | The target address receiving the payment. |
intendedReceivingAddress | The expected target address intended to receive the payment. |
receivedAmount | The actual amount received by the receiving address. |
intendedReceivingAmount | The expected amount intended to be received. |
- Standard Address Hashes: Standard address hashes can be derived from addresses.
- If
transactionStatus
is notSUCCESS
, thereceivingAddress
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.
- If the specified input or output does not exist, or lacks an address (e.g., outputs using
- Data Sources:
- For Bitcoin, all transaction details are retrieved using the
getrawtransaction
endpoint (verbosity 2) andgetblock
. 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.
- For Bitcoin, all transaction details are retrieved using the
Field | Description |
---|---|
transactionId | The transaction ID (txid ). For SegWit transactions, this differs from hash . |
oneToOne | true if only sourceAddress is present in inputs, and outputs include only receivingAddress , sourceAddress (for change), or OP_RETURN . |
sourceAddress | Address of the specified input. |
spentAmount | Total value of all inputs with sourceAddress minus total value of all outputs to sourceAddress . |
intendedSourceAmount | Same as spentAmount . |
receivingAddress | Address of the specified output. |
intendedReceivingAddress | Always matches receivingAddress . |
receivedAmount | Total value of outputs to receivingAddress minus total value of inputs from receivingAddress . |
intendedReceivingAmount | Same 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.
- Only
- Data Sources:
- Transaction details are obtained via the
tx
method. - Changes made by the transaction are recorded in the
meta
field (ormetaData
if fetched via theledger
method) underAffectedNodes
. Balance changes are found withinModifiedNodes
, by comparingFinalFields
andPreviousFields
.
- Transaction details are obtained via the
Field | Description |
---|---|
transactionId | Transaction hash found in the hash field. |
oneToOne | Always true , as each Payment transaction has exactly one sender and at most one receiver. |
sourceAddress | Address that sent the payment, reducing its balance. |
spentAmount | Amount by which sourceAddress 's balance was reduced. |
intendedSourceAmount | Calculated as Amount + Fee . If transactionStatus is SUCCESS , it matches spentAmount . |
receivingAddress | Address that received the payment. If unsuccessful, this is an empty string. |
intendedReceivingAddress | Address specified in the Destination field. |
receivedAmount | Amount by which the receivingAddress 's balance was increased. Can be zero if the transaction failed. |
intendedReceivingAmount | Expected 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\>
or6a<lengthOfReferenceInHex\><reference\>
in hex, where the length of the reference is 32 bytes.
- Exactly one output UTXO with
- Then
0x<reference\>
is thestandardPaymentReference
.
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.
Status | Code |
---|---|
SUCCESS | 0 |
SENDER_FAILURE | 1 |
RECEIVER_FAILURE | 2 |
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)
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:
Chain | Standard Address | Standard Address Hash |
---|---|---|
Bitcoin (Base58 ) | 1FWQiwK27EnGXb6BiBMRLJvunJQZZPMcGd | 0x8f651b6990a4754c58fcb5c5a11f4d40f8ddfdeb0e4f67cdd06c27f8d7bcbe33 |
Bitcoin (Bech32 ) | bc1qrmvxmwgqfr5q4fvtvnxczwxwm966n53c4lxh4v | 0xf75dc4b039ac72e037d67199bb92fa25db32b2210954df99637428473d47cedf |
Dogecoin | DL2H9FuaXsxivSs1sRtuJ8uryosyAj62XX | 0x51064c88c6b8e9d58b2abeae37a773bf89c9b279f8a05fa0ac0e81ebe13d2f4f |
XRPL | rDsbeomae4FXwgQTJp9Rs64Qg9vDiTCdBv | 0xa491aed10a1920ca31a85ff29e4bc410705d37d4dc9e690d4d500bcedfd8078f |
Finality
Blockchains have varying confirmation depths to consider blocks as final.
Chain | chainId | Confirmations required | Confirmation time |
---|---|---|---|
Bitcoin | 0 | 6 | ≈60 mins |
Dogecoin | 2 | 60 | ≈60 mins |
XRPL | 3 | 3 | ≈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;
}
}