Skip to content

Commit 4818b6f

Browse files
committed
fix(monitor): implement v2 escrow accounts
1 parent 6c21ec2 commit 4818b6f

File tree

3 files changed

+142
-7
lines changed

3 files changed

+142
-7
lines changed

contrib/indexer-service/start.sh

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ ESCROW_DEPLOYMENT=$(curl -s --max-time 10 "http://graph-node:8000/subgraphs/name
3030
-d '{"query": "{ _meta { deployment } }"}' | jq -r '.data._meta.deployment' 2>/dev/null)
3131
stdbuf -oL echo "🔍 DEBUG: Escrow deployment result: $ESCROW_DEPLOYMENT"
3232

33+
# Handle null deployment IDs by removing the lines entirely
34+
if [ "$NETWORK_DEPLOYMENT" = "null" ] || [ -z "$NETWORK_DEPLOYMENT" ]; then
35+
NETWORK_DEPLOYMENT=""
36+
fi
37+
38+
if [ "$ESCROW_DEPLOYMENT" = "null" ] || [ -z "$ESCROW_DEPLOYMENT" ]; then
39+
ESCROW_DEPLOYMENT=""
40+
fi
41+
3342
stdbuf -oL echo "Escrow subgraph deployment ID: $ESCROW_DEPLOYMENT"
3443
stdbuf -oL echo "Using test Network subgraph deployment ID: $NETWORK_DEPLOYMENT"
3544
stdbuf -oL echo "Using test Verifier address: $VERIFIER_ADDRESS"
@@ -41,8 +50,19 @@ stdbuf -oL echo "Using test Account0 address: $ACCOUNT0_ADDRESS"
4150
cp /opt/config/config.toml /opt/config.toml
4251

4352
# Replace the placeholders with actual values
44-
sed -i "s/NETWORK_DEPLOYMENT_PLACEHOLDER/$NETWORK_DEPLOYMENT/g" /opt/config.toml
45-
sed -i "s/ESCROW_DEPLOYMENT_PLACEHOLDER/$ESCROW_DEPLOYMENT/g" /opt/config.toml
53+
if [ -n "$NETWORK_DEPLOYMENT" ]; then
54+
sed -i "s/NETWORK_DEPLOYMENT_PLACEHOLDER/$NETWORK_DEPLOYMENT/g" /opt/config.toml
55+
else
56+
# Remove the deployment_id line entirely for network subgraph
57+
sed -i '/deployment_id = "NETWORK_DEPLOYMENT_PLACEHOLDER"/d' /opt/config.toml
58+
fi
59+
60+
if [ -n "$ESCROW_DEPLOYMENT" ]; then
61+
sed -i "s/ESCROW_DEPLOYMENT_PLACEHOLDER/$ESCROW_DEPLOYMENT/g" /opt/config.toml
62+
else
63+
# Remove the deployment_id line entirely for escrow subgraph
64+
sed -i '/deployment_id = "ESCROW_DEPLOYMENT_PLACEHOLDER"/d' /opt/config.toml
65+
fi
4666
sed -i "s/VERIFIER_ADDRESS_PLACEHOLDER/$VERIFIER_ADDRESS/g" /opt/config.toml
4767
sed -i "s/INDEXER_ADDRESS_PLACEHOLDER/$RECEIVER_ADDRESS/g" /opt/config.toml
4868
sed -i "s/INDEXER_MNEMONIC_PLACEHOLDER/$INDEXER_MNEMONIC/g" /opt/config.toml

crates/monitor/src/escrow_accounts.rs

Lines changed: 119 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,77 @@ pub async fn escrow_accounts_v2(
112112
.await
113113
}
114114

115-
// TODO implement escrow accounts v2 query
116115
async fn get_escrow_accounts_v2(
117-
_escrow_subgraph: &'static SubgraphClient,
118-
_indexer_address: Address,
119-
_reject_thawing_signers: bool,
116+
escrow_subgraph: &'static SubgraphClient,
117+
indexer_address: Address,
118+
reject_thawing_signers: bool,
120119
) -> anyhow::Result<EscrowAccounts> {
121-
Ok(EscrowAccounts::new(HashMap::new(), HashMap::new()))
120+
// V2 TAP receipts use different field names (payer/service_provider) but the underlying
121+
// escrow account model is identical to V1. Both V1 and V2 receipts reference the same
122+
// sender addresses and the same escrow relationships.
123+
//
124+
// The separation of V1/V2 escrow account watchers allows for potential future differences
125+
// in escrow models, but currently both query the same subgraph data with identical logic.
126+
//
127+
// V2 receipt flow:
128+
// 1. V2 receipt contains payer address (equivalent to V1 sender)
129+
// 2. Receipt is signed by a signer authorized by the payer
130+
// 3. Escrow accounts map: signer -> payer (sender) -> balance
131+
// 4. Service provider (indexer) receives payments from payer's escrow
132+
133+
let response = escrow_subgraph
134+
.query::<EscrowAccountQuery, _>(escrow_account::Variables {
135+
indexer: format!("{:x?}", indexer_address),
136+
thaw_end_timestamp: if reject_thawing_signers {
137+
U256::ZERO.to_string()
138+
} else {
139+
U256::MAX.to_string()
140+
},
141+
})
142+
.await?;
143+
144+
let response = response?;
145+
146+
tracing::trace!("V2 Escrow accounts response: {:?}", response);
147+
148+
let senders_balances: HashMap<Address, U256> = response
149+
.escrow_accounts
150+
.iter()
151+
.map(|account| {
152+
let balance = U256::checked_sub(
153+
U256::from_str(&account.balance)?,
154+
U256::from_str(&account.total_amount_thawing)?,
155+
)
156+
.unwrap_or_else(|| {
157+
tracing::warn!(
158+
"Balance minus total amount thawing underflowed for V2 account {}. \
159+
Setting balance to 0, no V2 queries will be served for this sender.",
160+
account.sender.id
161+
);
162+
U256::from(0)
163+
});
164+
165+
Ok((Address::from_str(&account.sender.id)?, balance))
166+
})
167+
.collect::<Result<HashMap<_, _>, anyhow::Error>>()?;
168+
169+
let senders_to_signers = response
170+
.escrow_accounts
171+
.into_iter()
172+
.map(|account| {
173+
let sender = Address::from_str(&account.sender.id)?;
174+
let signers = account
175+
.sender
176+
.signers
177+
.ok_or(anyhow!("Could not find any signers for V2 sender {sender}"))?
178+
.iter()
179+
.map(|signer| Address::from_str(&signer.id))
180+
.collect::<Result<Vec<_>, _>>()?;
181+
Ok((sender, signers))
182+
})
183+
.collect::<Result<HashMap<_, _>, anyhow::Error>>()?;
184+
185+
Ok(EscrowAccounts::new(senders_balances, senders_to_signers))
122186
}
123187

124188
async fn get_escrow_accounts_v1(
@@ -262,4 +326,54 @@ mod tests {
262326
)
263327
);
264328
}
329+
330+
#[test(tokio::test)]
331+
async fn test_current_accounts_v2() {
332+
// Set up a mock escrow subgraph - V2 uses the same subgraph as V1
333+
let mock_server = MockServer::start().await;
334+
let escrow_subgraph = Box::leak(Box::new(
335+
SubgraphClient::new(
336+
reqwest::Client::new(),
337+
None,
338+
DeploymentDetails::for_query_url(&format!(
339+
"{}/subgraphs/id/{}",
340+
&mock_server.uri(),
341+
test_assets::ESCROW_SUBGRAPH_DEPLOYMENT
342+
))
343+
.unwrap(),
344+
)
345+
.await,
346+
));
347+
348+
let mock = Mock::given(method("POST"))
349+
.and(path(format!(
350+
"/subgraphs/id/{}",
351+
test_assets::ESCROW_SUBGRAPH_DEPLOYMENT
352+
)))
353+
.respond_with(
354+
ResponseTemplate::new(200)
355+
.set_body_raw(test_assets::ESCROW_QUERY_RESPONSE, "application/json"),
356+
);
357+
mock_server.register(mock).await;
358+
359+
// Test V2 escrow accounts watcher
360+
let mut accounts = escrow_accounts_v2(
361+
escrow_subgraph,
362+
test_assets::INDEXER_ADDRESS,
363+
Duration::from_secs(60),
364+
true,
365+
)
366+
.await
367+
.unwrap();
368+
accounts.changed().await.unwrap();
369+
370+
// V2 should produce identical results to V1 since they query the same data
371+
assert_eq!(
372+
accounts.borrow().clone(),
373+
EscrowAccounts::new(
374+
ESCROW_ACCOUNTS_BALANCES.to_owned(),
375+
ESCROW_ACCOUNTS_SENDERS_TO_SIGNERS.to_owned(),
376+
)
377+
);
378+
}
265379
}

crates/tap-agent/src/agent/sender_account.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,7 @@ impl Actor for SenderAccount {
869869
// Depends on Escrow Subgraph Schema
870870
SenderType::Horizon => {
871871
if config.horizon_enabled {
872+
// TODO: Implement query for unfinalized v2 transactions, It depends on Escrow Subgraph Schema
872873
todo!("Implement query for unfinalized v2 transactions, It depends on Escrow Subgraph Schema")
873874
}
874875
// if we have any problems, we don't want to filter out

0 commit comments

Comments
 (0)