use r402_protocol::extension::{AdvertiseContext, Extension};
use r402_protocol::payment::ExtensionEntry;
use serde_json::{Value, json};
pub const ERC20_APPROVAL_GAS_SPONSORING_KEY: &str = "erc20ApprovalGasSponsoring";
pub const ERC20_APPROVAL_GAS_SPONSORING_VERSION: &str = "1";
fn client_info_schema() -> Value {
json!({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"from": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
"asset": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
"spender": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
"amount": { "type": "string", "pattern": "^[0-9]+$" },
"signedTransaction": { "type": "string", "pattern": "^0x[a-fA-F0-9]+$" },
"version": { "type": "string", "pattern": "^[0-9]+(\\.[0-9]+)*$" }
},
"required": ["from", "asset", "spender", "amount", "signedTransaction", "version"]
})
}
#[derive(Debug, Clone)]
pub struct Erc20ApprovalGasSponsoringExtension {
pub description: String,
pub version: String,
}
impl Default for Erc20ApprovalGasSponsoringExtension {
fn default() -> Self {
Self::new()
}
}
impl Erc20ApprovalGasSponsoringExtension {
#[must_use]
pub fn new() -> Self {
Self {
description:
"The facilitator accepts a raw signed approval transaction and will sponsor the gas fees."
.into(),
version: ERC20_APPROVAL_GAS_SPONSORING_VERSION.into(),
}
}
}
impl Extension for Erc20ApprovalGasSponsoringExtension {
fn id(&self) -> &'static str {
ERC20_APPROVAL_GAS_SPONSORING_KEY
}
fn advertise(&self, _ctx: &AdvertiseContext<'_>) -> Option<ExtensionEntry> {
Some(ExtensionEntry::with_schema(
json!({
"description": self.description,
"version": self.version,
}),
client_info_schema(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn advertise_key_and_version() {
let ext = Erc20ApprovalGasSponsoringExtension::new();
assert_eq!(ext.id(), "erc20ApprovalGasSponsoring");
let Some(entry) = ext.advertise(&AdvertiseContext::new(None)) else {
panic!("erc20 advertise must emit an entry");
};
let v = entry.to_value();
assert_eq!(v["info"]["version"], "1");
assert!(
v.get("schema").is_some(),
"client schema must be advertised"
);
}
}