IPayment
An interface to relay and verify payment transactions in native currencies across multiple external blockchains.
Sourced from IPayment.sol
on GitHub.
Overview
The IPayment interface enables smart contracts on Flare networks to access and verify native currency payment transactions from external blockchains. It provides a standardized way to prove that payments have occurred between entities, similar to traditional banking transfers, with support for payment references. This attestation type handles the different transaction models across various blockchains, offering a unified interface for payment verification.
Supported Chains
Network Type | Supported Chains |
---|---|
Mainnet | BTC (Bitcoin), DOGE (Dogecoin), XRP (XRP Ledger) |
Testnet | testBTC (Bitcoin Testnet v3), testDOGE , testXRP |
Chain-Specific Implementation Details
UTXO Chains (Bitcoin and Dogecoin)
- Transaction Model: Uses inputs and outputs, where multiple addresses can contribute inputs and receive outputs
- inUtxo: Index of the transaction input with the source address
- utxo: Index of the transaction output with the receiving address
- blockTimestamp: Derived from the mediantime of the block
Account-based Chains (XRPL)
- Transaction Model: Direct transfers between accounts
- inUtxo and utxo: Always set to 0 (not used)
- blockTimestamp: Derived from the close_time of the ledger converted to UNIX time
Interface Definition
// 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;
}
}
Structs
Request
Toplevel request structure.
Parameters
attestationType
: ID of the attestation type (0x01 for Payment)sourceId
: ID of the data source (e.g., BTC, DOGE, XRP)messageIntegrityCode
: MessageIntegrityCode derived from the expected responserequestBody
: Data defining the request
Response
Toplevel response structure.
Parameters
attestationType
: Extracted from the requestsourceId
: Extracted from the requestvotingRound
: The ID of the State Connector round in which the request was consideredlowestUsedTimestamp
: The lowest timestamp used to generate the responserequestBody
: Extracted from the requestresponseBody
: Data defining the response
Proof
Toplevel proof structure for verification.
Parameters
merkleProof
: Merkle proof corresponding to the attestation responsedata
: Attestation response
RequestBody
Request body specific to payment verification.
Parameters
transactionId
: The unique identifier of the transaction to verifyinUtxo
: Index of the transaction input with source address (UTXO chains only)utxo
: Index of the transaction output with receiving address (UTXO chains only)
ResponseBody
Response body containing payment transaction details.
Parameters
blockNumber
: Block number containing the transactionblockTimestamp
: Timestamp of the block containing the transactionsourceAddressHash
: Standard hash of the source addresssourceAddressesRoot
: Root of the Merkle tree of all source addressesreceivingAddressHash
: Standard hash of the receiving address (zero if transaction failed)intendedReceivingAddressHash
: Standard hash of the intended receiving address (for failed transactions)spentAmount
: Amount spent by the source address in minimal unitsintendedSpentAmount
: Amount intended to be spent (for failed transactions)receivedAmount
: Amount received by the receiving addressintendedReceivedAmount
: Amount intended to be received (for failed transactions)standardPaymentReference
: Standard payment reference included in the transactiononeToOne
: True if the transaction involves only one source and one receiverstatus
: Transaction status (0=success, 1=sender failure, 2=receiver failure)
Transaction Status Codes
Status Code | Description | Explanation |
---|---|---|
0 | SUCCESS | Transaction completed successfully |
1 | SENDER_FAILURE | Transaction failed due to issues on the sender's side |
2 | RECEIVER_FAILURE | Transaction failed due to issues on the receiver's side |
Implementation Notes
- Attestation ID:
0x01
- The
lowestUsedTimestamp
parameter uses the value ofblockTimestamp
- The
lutlimit
(Lowest Used Timestamp limit) is0x127500
(1,209,600 seconds = 14 days) for all supported chains - Standard payment references can be included for cross-chain payment identification
- For account-based chains like XRPL,
inUtxo
andutxo
parameters should be set to 0 - The
sourceAddressesRoot
is a Merkle tree root of all transaction input addresses, useful for multi-input transactions - The
oneToOne
flag helps identify simple peer-to-peer transfers versus more complex multi-party transactions
Standard Payment Reference
A standardized payment reference follows a specific format to ensure consistency across different blockchains:
- For UTXO chains (BTC, DOGE): Derived from OP_RETURN outputs or specific patterns in transaction data
- For XRPL: Derived from the MemoData field in the transaction
Payment references allow for correlation of payments across chains and facilitate payment reconciliation in cross-chain applications.
Usage Example
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@flare-foundation/flare-smart-contracts-v2/contracts/userInterfaces/IFdcHub.sol";
import "@flare-foundation/flare-smart-contracts-v2/contracts/userInterfaces/IFdcVerification.sol";
import "@flare-foundation/flare-smart-contracts-v2/contracts/userInterfaces/fdc/IPayment.sol";
contract PaymentVerifier {
IFdcHub private fdcHub;
IFdcVerification private fdcVerification;
bytes32 private constant ATTESTATION_TYPE_PAYMENT = 0x0100000000000000000000000000000000000000000000000000000000000000;
bytes32 private constant SOURCE_ID_BTC = 0x4254430000000000000000000000000000000000000000000000000000000000;
// Store verified payments
mapping(bytes32 => bool) public verifiedPayments; // transactionId => verified
constructor(address _fdcHubAddress, address _fdcVerificationAddress) {
fdcHub = IFdcHub(_fdcHubAddress);
fdcVerification = IFdcVerification(_fdcVerificationAddress);
}
// Request verification of a payment transaction
function requestPaymentVerification(
bytes32 transactionId,
uint256 inUtxo,
uint256 utxo
) external payable {
// Create request body
IPayment.RequestBody memory requestBody = IPayment.RequestBody({
transactionId: transactionId,
inUtxo: inUtxo,
utxo: utxo
});
// Encode the full request
bytes memory encodedRequest = abi.encode(
ATTESTATION_TYPE_PAYMENT,
SOURCE_ID_BTC,
bytes32(0), // messageIntegrityCode (would need to be calculated properly)
requestBody
);
// Submit the request with payment
fdcHub.requestAttestation{value: msg.value}(encodedRequest);
}
// Verify a provided payment proof
function verifyPayment(IPayment.Proof calldata _proof)
external
returns (
bool success,
bytes32 sourceAddressHash,
bytes32 receivingAddressHash,
int256 amount,
bytes32 paymentReference
)
{
// Verify the proof using FdcVerification
bool proofVerified = fdcVerification.verifyPayment(_proof);
if (proofVerified) {
// Extract the payment details from the proof
bytes32 transactionId = _proof.data.requestBody.transactionId;
IPayment.ResponseBody memory response = _proof.data.responseBody;
// Check if this is a successful payment
if (response.status == 0) {
// Store that this transaction has been verified
verifiedPayments[transactionId] = true;
return (
true,
response.sourceAddressHash,
response.receivingAddressHash,
response.receivedAmount,
response.standardPaymentReference
);
}
}
return (false, bytes32(0), bytes32(0), 0, bytes32(0));
}
}