Skip to content

Commit 7c79316

Browse files
feat (payment-service): lock and unlock user funds (#663)
Co-authored-by: Mariano A. Nicolini <mariano.nicolini.91@gmail.com>
1 parent 644528f commit 7c79316

4 files changed

Lines changed: 77 additions & 16 deletions

File tree

batcher/aligned-batcher/src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@ impl Batcher {
282282
// Checks user has sufficient balance
283283
// If user has sufficient balance, increments the user's proof count in the batch
284284
async fn check_user_balance(&self, addr: &Address) -> bool {
285+
if self.user_balance_is_unlocked(addr).await {
286+
return false;
287+
}
288+
285289
let mut user_proof_counts = self.user_proof_count_in_batch.lock().await;
286290
let user_proofs_in_batch = *user_proof_counts.get(addr).unwrap_or(&0) + 1;
287291

@@ -680,6 +684,15 @@ impl Batcher {
680684
.await
681685
.unwrap_or_default()
682686
}
687+
688+
async fn user_balance_is_unlocked(&self, addr: &Address) -> bool {
689+
self.payment_service
690+
.user_unlock_block(*addr)
691+
.call()
692+
.await
693+
.unwrap_or_default()
694+
!= U256::zero()
695+
}
683696
}
684697

685698
async fn send_batch_inclusion_data_responses(

batcher/aligned-sdk/abi/BatcherPaymentService.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

contracts/scripts/anvil/state/alignedlayer-deployed-anvil-state.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

contracts/src/core/BatcherPaymentService.sol

Lines changed: 62 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ contract BatcherPaymentService is
1111
PausableUpgradeable,
1212
UUPSUpgradeable
1313
{
14+
// CONSTANTS
15+
uint256 public constant UNLOCK_BLOCK_COUNT = 100;
16+
1417
// EVENTS
1518
event PaymentReceived(address indexed sender, uint256 amount);
1619
event FundsWithdrawn(address indexed recipient, uint256 amount);
@@ -22,14 +25,18 @@ contract BatcherPaymentService is
2225
uint256 nonce;
2326
}
2427

28+
struct UserInfo {
29+
uint256 balance;
30+
uint256 unlockBlock;
31+
uint256 nonce;
32+
}
33+
2534
// STORAGE
2635
address public AlignedLayerServiceManager;
2736
address public BatcherWallet;
2837

29-
mapping(address => uint256) public UserBalances;
30-
31-
// map to check signature is only submitted once
32-
mapping(address => uint256) public UserNonces;
38+
// map to user data
39+
mapping(address => UserInfo) public UserData;
3340

3441
// storage gap for upgradeability
3542
uint256[24] private __GAP;
@@ -54,7 +61,7 @@ contract BatcherPaymentService is
5461

5562
// PAYABLE FUNCTIONS
5663
receive() external payable {
57-
UserBalances[msg.sender] += msg.value;
64+
UserData[msg.sender].balance += msg.value;
5865
emit PaymentReceived(msg.sender, msg.value);
5966
}
6067

@@ -114,12 +121,30 @@ contract BatcherPaymentService is
114121
);
115122
}
116123

124+
function unlock() external whenNotPaused {
125+
require(
126+
UserData[msg.sender].balance > 0,
127+
"User has no funds to unlock"
128+
);
129+
130+
UserData[msg.sender].unlockBlock = block.number + UNLOCK_BLOCK_COUNT;
131+
}
132+
133+
function lock() external whenNotPaused {
134+
require(UserData[msg.sender].balance > 0, "User has no funds to lock");
135+
UserData[msg.sender].unlockBlock = 0;
136+
}
137+
117138
function withdraw(uint256 amount) external whenNotPaused {
139+
UserInfo storage user_data = UserData[msg.sender];
140+
require(user_data.balance >= amount, "Payer has insufficient balance");
141+
118142
require(
119-
UserBalances[msg.sender] >= amount,
120-
"Payer has insufficient balance"
143+
user_data.unlockBlock != 0 && user_data.unlockBlock <= block.number,
144+
"Funds are locked"
121145
);
122-
UserBalances[msg.sender] -= amount;
146+
147+
user_data.balance -= amount;
123148
payable(msg.sender).transfer(amount);
124149
emit FundsWithdrawn(msg.sender, amount);
125150
}
@@ -163,12 +188,20 @@ contract BatcherPaymentService is
163188
abi.encodePacked(leaves[2 * i], leaves[2 * i + 1])
164189
);
165190

166-
verifySignatureAndDecreaseBalance(leaves[i], signatures[i], feePerProof);
191+
verifySignatureAndDecreaseBalance(
192+
leaves[i],
193+
signatures[i],
194+
feePerProof
195+
);
167196
}
168197

169198
// Verify the rest of the signatures
170199
for (; i < signatures.length; i++) {
171-
verifySignatureAndDecreaseBalance(leaves[i], signatures[i], feePerProof);
200+
verifySignatureAndDecreaseBalance(
201+
leaves[i],
202+
signatures[i],
203+
feePerProof
204+
);
172205
}
173206

174207
// The next layer above has half as many nodes
@@ -210,13 +243,28 @@ contract BatcherPaymentService is
210243
signatureData.s
211244
);
212245

213-
require(UserNonces[signer] == signatureData.nonce, "Invalid Nonce");
214-
UserNonces[signer]++;
246+
UserInfo storage user_data = UserData[signer];
247+
248+
require(user_data.nonce == signatureData.nonce, "Invalid Nonce");
249+
user_data.nonce++;
215250

216251
require(
217-
UserBalances[signer] >= feePerProof,
252+
user_data.balance >= feePerProof,
218253
"Signer has insufficient balance"
219254
);
220-
UserBalances[signer] -= feePerProof;
255+
256+
user_data.balance -= feePerProof;
257+
}
258+
259+
function user_balances(address account) public view returns (uint256) {
260+
return UserData[account].balance;
261+
}
262+
263+
function user_nonces(address account) public view returns (uint256) {
264+
return UserData[account].nonce;
265+
}
266+
267+
function user_unlock_block(address account) public view returns (uint256) {
268+
return UserData[account].unlockBlock;
221269
}
222270
}

0 commit comments

Comments
 (0)