Skip to content

Commit 0666ebb

Browse files
authored
Merge pull request #149 from Merit-Systems/shafu/fee-on-fund
Fee on Fund
2 parents daca3fe + 2c6e246 commit 0666ebb

13 files changed

+698
-158
lines changed

interface/IEscrow.sol

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ interface IEscrow {
1313
uint256 indexed instanceId,
1414
address indexed token,
1515
address sender,
16-
uint256 amount,
16+
uint256 netAmount,
17+
uint256 feeAmount,
1718
bytes data
1819
);
1920

@@ -109,7 +110,8 @@ interface IEscrow {
109110
event AddedDistributor(uint256 indexed repoId, uint256 indexed instanceId, address indexed distributor);
110111
event RemovedDistributor(uint256 indexed repoId, uint256 indexed instanceId, address indexed distributor);
111112
event BatchLimitSet(uint256 newBatchLimit);
112-
event FeeSet(uint256 oldFee, uint256 newFee);
113+
event FeeOnClaimSet(uint256 oldFee, uint256 newFee);
114+
event FeeOnFundSet(uint256 oldFee, uint256 newFee);
113115
event FeeRecipientSet(address indexed oldRecipient, address indexed newRecipient);
114116
event SignerSet(address indexed oldSigner, address indexed newSigner);
115117
}

libraries/Params.sol

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ library Params {
1111
address constant BASE_OWNER = 0x7163a6C74a3caB2A364F9aDD054bf83E50A1d8Bc;
1212
address constant BASE_SIGNER = 0x7F26a8d1A94bD7c1Db651306f503430dF37E9037;
1313
address constant BASE_USDC = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913;
14-
uint constant BASE_FEE_BPS = 250;
14+
uint constant BASE_FEE_ON_FUND_BPS = 0;
15+
uint constant BASE_FEE_ON_CLAIM_BPS = 250;
1516

1617
/*//////////////////////////////////////////////////////////////
1718
SEPOLIA
@@ -23,7 +24,8 @@ library Params {
2324
address constant SEPOLIA_TESTER = 0x99ecA80b4Ebf8fDACe6627BEcb75EF1e620E6956;
2425
address constant SEPOLIA_TESTER_JSON = 0x5C87eA705eE49a96532F45f5db606A5f5fEF9780;
2526
address constant SEPOLIA_TESTER_SHAFU = 0x39053B170bBD9580d0b86e8317c685aEFB65f1ec;
26-
uint constant SEPOLIA_FEE_BPS = 250;
27+
uint constant SEPOLIA_FEE_ON_FUND_BPS = 0;
28+
uint constant SEPOLIA_FEE_ON_CLAIM_BPS = 250;
2729

2830
/*//////////////////////////////////////////////////////////////
2931
BASE SEPOLIA
@@ -35,5 +37,6 @@ library Params {
3537
address constant BASESEPOLIA_TESTER = 0x5C87eA705eE49a96532F45f5db606A5f5fEF9780;
3638
address constant BASESEPOLIA_TESTER_SHAFU = 0x39053B170bBD9580d0b86e8317c685aEFB65f1ec;
3739
address constant BASESEPOLIA_TESTER_JSON = 0x5C87eA705eE49a96532F45f5db606A5f5fEF9780;
38-
uint constant BASESEPOLIA_FEE_BPS = 250;
40+
uint constant BASESEPOLIA_FEE_ON_FUND_BPS = 0;
41+
uint constant BASESEPOLIA_FEE_ON_CLAIM_BPS = 250;
3942
}

script/Deploy.Anvil.s.sol

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,11 @@ contract DeployAnvil is Script {
9292
MockERC20 token3 = new MockERC20("Test Token 3", "TKN3", 18);
9393
escrow.whitelistToken(address(token3));
9494

95-
// Test FeeSet event
96-
escrow.setFee(300); // 3%
95+
// Test FeeOnClaimSet event
96+
escrow.setFeeOnClaim(300); // 3%
97+
98+
// Test FeeOnFundSet event
99+
escrow.setFeeOnFund(100); // 1%
97100

98101
// Test FeeRecipientSet event
99102
escrow.setFeeRecipient(USER1);
@@ -1103,7 +1106,7 @@ contract DeployAnvil is Script {
11031106
feeRates[4] = 250; // Back to 2.5%
11041107

11051108
for (uint i = 0; i < feeRates.length; i++) {
1106-
escrow.setFee(feeRates[i]);
1109+
escrow.setFeeOnClaim(feeRates[i]);
11071110
}
11081111

11091112
// Test different fee recipients
@@ -1461,7 +1464,7 @@ contract DeployAnvil is Script {
14611464
extremeFees[7] = 250; // Back to 2.5%
14621465

14631466
for (uint i = 0; i < extremeFees.length; i++) {
1464-
escrow.setFee(extremeFees[i]);
1467+
escrow.setFeeOnClaim(extremeFees[i]);
14651468
}
14661469

14671470
// Test rotating fee recipients

script/Deploy.Base.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ contract DeployBase is Deploy {
1616
Params.BASE_OWNER,
1717
Params.BASE_SIGNER,
1818
initialWhitelistedTokens,
19-
Params.BASE_FEE_BPS,
19+
Params.BASE_FEE_ON_CLAIM_BPS,
2020
Params.BATCH_LIMIT
2121
);
2222

script/Deploy.BaseSepolia.s.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ contract DeployBaseSepolia is Deploy {
2323
Params.BASESEPOLIA_OWNER,
2424
Params.BASESEPOLIA_SIGNER,
2525
initialWhitelistedTokens,
26-
Params.BASESEPOLIA_FEE_BPS,
26+
Params.BASESEPOLIA_FEE_ON_CLAIM_BPS,
2727
Params.BATCH_LIMIT
2828
);
2929

script/Deploy.Core.s.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ contract Deploy is Script {
1212
address owner,
1313
address signer,
1414
address[] memory initialWhitelistedTokens,
15-
uint feeBps,
15+
uint feeOnClaimBps,
1616
uint batchLimit
1717
)
1818
public
@@ -26,7 +26,7 @@ contract Deploy is Script {
2626
owner,
2727
signer,
2828
initialWhitelistedTokens,
29-
feeBps,
29+
feeOnClaimBps,
3030
batchLimit
3131
);
3232

@@ -42,7 +42,7 @@ contract Deploy is Script {
4242
owner,
4343
signer,
4444
initialWhitelistedTokens,
45-
feeBps,
45+
feeOnClaimBps,
4646
batchLimit
4747
);
4848

src/Escrow.sol

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ contract Escrow is Owned, IEscrow {
8585
mapping(uint => mapping(uint => uint)) public repoSetAdminNonce; // repoId → instanceId → nonce
8686
mapping(address => uint) public recipientClaimNonce; // recipient → nonce
8787

88-
uint public fee;
88+
uint public feeOnFund;
89+
uint public feeOnClaim;
8990
address public feeRecipient;
9091
uint public batchLimit;
9192

@@ -125,14 +126,14 @@ contract Escrow is Owned, IEscrow {
125126
address _owner,
126127
address _signer,
127128
address[] memory _whitelistedTokens,
128-
uint _fee,
129+
uint _feeOnClaim,
129130
uint _batchLimit
130131
) Owned(_owner) {
131-
require(_fee <= MAX_FEE, Errors.INVALID_FEE);
132+
require(_feeOnClaim <= MAX_FEE, Errors.INVALID_FEE);
132133

133134
signer = _signer;
134135
feeRecipient = _owner;
135-
fee = _fee;
136+
feeOnClaim = _feeOnClaim;
136137
batchLimit = _batchLimit;
137138
INITIAL_CHAIN_ID = block.chainid;
138139
INITIAL_DOMAIN_SEPARATOR = _domainSeparator();
@@ -205,10 +206,17 @@ contract Escrow is Owned, IEscrow {
205206

206207
token.safeTransferFrom(msg.sender, address(this), amount);
207208

208-
accounts[repoId][instanceId].balance[address(token)] += amount;
209-
fundings[repoId][instanceId][address(token)][msg.sender] += amount;
209+
uint feeAmount = amount.mulDivUp(feeOnFund, 10_000);
210+
require(amount > feeAmount, Errors.INVALID_AMOUNT);
210211

211-
emit FundedRepo(repoId, instanceId, address(token), msg.sender, amount, data);
212+
if (feeAmount > 0) token.safeTransfer(feeRecipient, feeAmount);
213+
214+
uint netAmount = amount - feeAmount;
215+
216+
accounts[repoId][instanceId].balance[address(token)] += netAmount;
217+
fundings[repoId][instanceId][address(token)][msg.sender] += netAmount;
218+
219+
emit FundedRepo(repoId, instanceId, address(token), msg.sender, netAmount, feeAmount, data);
212220
}
213221

214222
/* -------------------------------------------------------------------------- */
@@ -305,8 +313,7 @@ contract Escrow is Owned, IEscrow {
305313
require(distribution.amount > 0, Errors.INVALID_AMOUNT);
306314
require(whitelistedTokens.contains(address(distribution.token)), Errors.INVALID_TOKEN);
307315

308-
// Validate that after fees, recipient will receive at least 1 wei
309-
uint feeAmount = distribution.amount.mulDivUp(fee, 10_000);
316+
uint feeAmount = distribution.amount.mulDivUp(feeOnClaim, 10_000);
310317
require(distribution.amount > feeAmount, Errors.INVALID_AMOUNT);
311318

312319
distributionId = distributionCount++;
@@ -320,7 +327,7 @@ contract Escrow is Owned, IEscrow {
320327
exists: true,
321328
_type: _type,
322329
payer: _type == DistributionType.Solo ? msg.sender : address(0),
323-
fee: fee
330+
fee: feeOnClaim
324331
});
325332
}
326333

@@ -486,14 +493,24 @@ contract Escrow is Owned, IEscrow {
486493
emit WhitelistedToken(token);
487494
}
488495

489-
function setFee(uint newFee)
496+
function setFeeOnFund(uint newFee)
497+
external
498+
onlyOwner
499+
{
500+
require(newFee <= MAX_FEE, Errors.INVALID_FEE);
501+
uint oldFee = feeOnFund;
502+
feeOnFund = newFee;
503+
emit FeeOnFundSet(oldFee, newFee);
504+
}
505+
506+
function setFeeOnClaim(uint newFee)
490507
external
491508
onlyOwner
492509
{
493510
require(newFee <= MAX_FEE, Errors.INVALID_FEE);
494-
uint oldFee = fee;
495-
fee = newFee;
496-
emit FeeSet(oldFee, newFee);
511+
uint oldFee = feeOnClaim;
512+
feeOnClaim = newFee;
513+
emit FeeOnClaimSet(oldFee, newFee);
497514
}
498515

499516
function setFeeRecipient(address newRec)

test/01_Deploy.t.sol

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ contract Deploy_Test is Base_Test {
4343
assertEq(deployedEscrow.owner(), Params.BASE_OWNER);
4444
assertEq(deployedEscrow.signer(), Params.BASE_SIGNER);
4545
assertEq(deployedEscrow.feeRecipient(), Params.BASE_OWNER);
46-
assertEq(deployedEscrow.fee(), Params.BASE_FEE_BPS);
46+
assertEq(deployedEscrow.feeOnClaim(), Params.BASE_FEE_ON_CLAIM_BPS);
47+
assertEq(deployedEscrow.feeOnFund(), Params.BASE_FEE_ON_FUND_BPS);
4748
assertEq(deployedEscrow.batchLimit(), Params.BATCH_LIMIT);
4849
assertEq(deployedEscrow.repoSetAdminNonce(0, 0), 0);
4950
assertEq(deployedEscrow.batchCount(), 0);
@@ -83,7 +84,8 @@ contract Deploy_Test is Base_Test {
8384
assertEq(deployedEscrow.owner(), testOwner);
8485
assertEq(deployedEscrow.signer(), testSigner);
8586
assertEq(deployedEscrow.feeRecipient(), testOwner);
86-
assertEq(deployedEscrow.fee(), feeBps);
87+
assertEq(deployedEscrow.feeOnClaim(), feeBps);
88+
assertEq(deployedEscrow.feeOnFund(), Params.BASE_FEE_ON_FUND_BPS);
8789
assertEq(deployedEscrow.batchLimit(), batchLimit);
8890
assertEq(deployedEscrow.repoSetAdminNonce(0, 0), 0);
8991
assertEq(deployedEscrow.batchCount(), 0);
@@ -131,7 +133,7 @@ contract Deploy_Test is Base_Test {
131133
batchLimit
132134
);
133135

134-
assertEq(deployedEscrow.fee(), maxFeeBps);
136+
assertEq(deployedEscrow.feeOnClaim(), maxFeeBps);
135137
}
136138

137139
function test_deploy_revert_invalidFeeBps() public {
@@ -312,7 +314,7 @@ contract Deploy_Test is Base_Test {
312314
assertEq(deployedEscrow.owner(), _owner);
313315
assertEq(deployedEscrow.signer(), _signer);
314316
assertEq(deployedEscrow.feeRecipient(), _owner);
315-
assertEq(deployedEscrow.fee(), _feeBps);
317+
assertEq(deployedEscrow.feeOnClaim(), _feeBps);
316318
assertEq(deployedEscrow.batchLimit(), _batchLimit);
317319
}
318320

0 commit comments

Comments
 (0)