Skip to content

Commit b599e23

Browse files
committed
add integration test
1 parent 9b43cc3 commit b599e23

4 files changed

Lines changed: 201 additions & 11 deletions

File tree

contracts/payment/MCPayment.sol

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ contract MCPayment is
2424
/**
2525
* @dev Version of contract
2626
*/
27-
string public constant VERSION = "1.0.5";
27+
string public constant VERSION = "1.0.6";
2828

2929
/**
3030
* @dev Version of EIP 712 domain
@@ -129,10 +129,8 @@ contract MCPayment is
129129
}
130130

131131
/**
132-
* @dev Modifier to make a function callable only by a certain role. In
133-
* addition to checking the sender's role, `address(0)` 's role is also
134-
* considered. Granting a role to `address(0)` is equivalent to enabling
135-
* this role for everyone.
132+
* @dev Modifier to make a function callable only by a Withdrawer role
133+
* or the owner.
136134
*/
137135
modifier onlyWithdrawerRoleOrOwner() {
138136
if (_msgSender() != owner()) {
@@ -160,6 +158,7 @@ contract MCPayment is
160158
__Ownable_init(owner);
161159
__EIP712_init("MCPayment", DOMAIN_VERSION);
162160
__ReentrancyGuard_init();
161+
__AccessControl_init();
163162
}
164163

165164
/**
@@ -405,7 +404,7 @@ contract MCPayment is
405404
}
406405

407406
/**
408-
* @dev Get owner balance
407+
* @dev Get owner balance from owner or withdrawer role
409408
* @return balance of owner
410409
*/
411410
function getOwnerBalance() public view onlyWithdrawerRoleOrOwner returns (uint256) {
@@ -414,7 +413,7 @@ contract MCPayment is
414413
}
415414

416415
/**
417-
* @dev Get owner ERC-20 balance
416+
* @dev Get owner ERC-20 balance from owner or withdrawer role
418417
* @return balance of owner
419418
*/
420419
function getOwnerERC20Balance(
@@ -431,7 +430,7 @@ contract MCPayment is
431430
}
432431

433432
/**
434-
* @dev Withdraw balance to owner
433+
* @dev Withdraw balance to owner or withdrawer role
435434
*/
436435
function ownerWithdraw() public onlyWithdrawerRoleOrOwner nonReentrant {
437436
MCPaymentStorage storage $ = _getMCPaymentStorage();
@@ -444,7 +443,7 @@ contract MCPayment is
444443
}
445444

446445
/**
447-
* @dev Withdraw ERC-20 balance to owner
446+
* @dev Withdraw ERC-20 balance to owner or withdrawer role
448447
*/
449448
function ownerERC20Withdraw(address token) public onlyWithdrawerRoleOrOwner nonReentrant {
450449
uint256 amount = IERC20(token).balanceOf(address(this));

helpers/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ export const contractsInfo = Object.freeze({
312312
},
313313
MC_PAYMENT: {
314314
name: "MCPayment",
315-
version: "1.0.5",
315+
version: "1.0.6",
316316
unifiedAddress: "0xe317A4f1450116b2fD381446DEaB41c882D6136D",
317317
create2Calldata: ethers.hexlify(ethers.toUtf8Bytes("iden3.create2.MCPayment")),
318318
verificationOpts: {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { ethers } from "hardhat";
2+
3+
async function main() {
4+
const mcPaymentAddress = "0xYourMCPaymentContractAddressHere"; // replace with actual deployed contract address
5+
const tokenAddress = "0xYourTokenAddressHere"; // replace with actual token address
6+
const issuerPrivateKey = "0xYourIssuerPrivateKeyHere"; // replace with actual issuer private key
7+
const amount = 1000; // amount to be paid
8+
9+
const issuerWallet = new ethers.Wallet(issuerPrivateKey);
10+
11+
const [signer] = await ethers.getSigners();
12+
const token = await ethers.getContractAt("ERC20Token", tokenAddress);
13+
const signerBalance = await token.balanceOf(signer.address);
14+
console.log(
15+
`Balance ${signer.address}: ${ethers.formatEther(signerBalance)} ${await token.symbol()}`,
16+
);
17+
18+
const payment = await ethers.getContractAt("MCPayment", mcPaymentAddress);
19+
20+
const domainData = {
21+
name: "MCPayment",
22+
version: "1.0.0",
23+
chainId: 31337,
24+
verifyingContract: await payment.getAddress(),
25+
};
26+
27+
const erc20types = {
28+
Iden3PaymentRailsERC20RequestV1: [
29+
{ name: "tokenAddress", type: "address" },
30+
{ name: "recipient", type: "address" },
31+
{ name: "amount", type: "uint256" },
32+
{ name: "expirationDate", type: "uint256" },
33+
{ name: "nonce", type: "uint256" },
34+
{ name: "metadata", type: "bytes" },
35+
],
36+
};
37+
38+
const paymentData = {
39+
tokenAddress: tokenAddress,
40+
recipient: issuerWallet.address,
41+
amount: amount,
42+
expirationDate: Math.round(new Date().getTime() / 1000) + 60 * 60, // 1 hour
43+
nonce: 35,
44+
metadata: "0x",
45+
};
46+
const signature = await issuerWallet.signTypedData(domainData, erc20types, paymentData);
47+
48+
// approve MCPayment contract to spend tokens
49+
const txApprove = await token.connect(signer).approve(await payment.getAddress(), amount);
50+
await txApprove.wait();
51+
console.log(
52+
`${signer.address} approved ${amount} ${await token.symbol()} for MCPayment contract at ${mcPaymentAddress}`,
53+
);
54+
55+
const erc20PaymentGas = await payment
56+
.connect(signer)
57+
.payERC20.estimateGas(paymentData, signature);
58+
console.log("ERC-20 Payment Gas: " + erc20PaymentGas);
59+
60+
let ownerBalance = await payment.getOwnerERC20Balance(tokenAddress);
61+
console.log(`Owner ERC-20 Balance before payment: ${ownerBalance}`);
62+
63+
const tx = await payment.connect(signer).payERC20(paymentData, signature);
64+
console.log("ERC-20 Payment Tx Hash: " + tx.hash);
65+
await tx.wait();
66+
67+
ownerBalance = await payment.getOwnerERC20Balance(tokenAddress);
68+
console.log(`Owner ERC-20 Balance after payment: ${ownerBalance}`);
69+
}
70+
71+
main()
72+
.then(() => process.exit(0))
73+
.catch((error) => {
74+
console.error(error);
75+
process.exit(1);
76+
});

test/payment/mc-payment.test.ts

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ describe("MC Payment Contract", () => {
228228
// grant WITHDRAWER_ROLE to userSigner
229229
await payment
230230
.connect(owner)
231-
.grantRole(await payment.WITHDRAWER_ROLE(), userSigner.getAddress());
231+
.grantRole(await payment.WITHDRAWER_ROLE(), await userSigner.getAddress());
232232

233233
// now userSigner can withdraw owner balance
234234
await expect(payment.connect(userSigner).ownerWithdraw()).to.changeEtherBalance(userSigner, 10);
@@ -241,6 +241,24 @@ describe("MC Payment Contract", () => {
241241
payment,
242242
"WithdrawErrorNoBalance",
243243
);
244+
await expect(payment.connect(owner).ownerWithdraw()).to.be.revertedWithCustomError(
245+
payment,
246+
"WithdrawErrorNoBalance",
247+
);
248+
});
249+
250+
it("Calling setAdminRole by owner:", async () => {
251+
expect(await payment.hasRole(await payment.DEFAULT_ADMIN_ROLE(), owner.address)).to.be.false;
252+
await payment.connect(owner).setAdminRole(owner.address);
253+
expect(await payment.hasRole(await payment.DEFAULT_ADMIN_ROLE(), owner.address)).to.be.true;
254+
await payment.connect(owner).revokeRole(await payment.DEFAULT_ADMIN_ROLE(), owner.address);
255+
expect(await payment.hasRole(await payment.DEFAULT_ADMIN_ROLE(), owner.address)).to.be.false;
256+
});
257+
258+
it("Calling setAdminRole by non-owner:", async () => {
259+
await expect(
260+
payment.connect(userSigner).setAdminRole(owner.address),
261+
).to.be.revertedWithCustomError(payment, "OwnableUnauthorizedAccount");
244262
});
245263

246264
it("Update owner percentage:", async () => {
@@ -391,6 +409,62 @@ describe("MC Payment Contract", () => {
391409
expect(await payment.getOwnerERC20Balance(tokenAddress)).to.be.eq(0);
392410
});
393411

412+
it("ERC-20 payment with withdrawer role:", async () => {
413+
const tokenFactory = await ethers.getContractFactory("ERC20Token", owner);
414+
const token = await tokenFactory.deploy(1_000);
415+
await token.connect(owner).transfer(await userSigner.getAddress(), 100);
416+
expect(await token.balanceOf(await userSigner.getAddress())).to.be.eq(100);
417+
418+
await token.connect(userSigner).approve(await payment.getAddress(), 10);
419+
420+
const paymentData = {
421+
tokenAddress: await token.getAddress(),
422+
recipient: issuer1Signer.address,
423+
amount: 10,
424+
expirationDate: Math.round(new Date().getTime() / 1000) + 60 * 60, // 1 hour
425+
nonce: 35,
426+
metadata: "0x",
427+
};
428+
429+
const signature = await issuer1Signer.signTypedData(domainData, erc20types, paymentData);
430+
const erc20PaymentGas = await payment
431+
.connect(userSigner)
432+
.payERC20.estimateGas(paymentData, signature);
433+
console.log("ERC-20 Payment Gas: " + erc20PaymentGas);
434+
435+
await expect(
436+
payment.connect(userSigner).payERC20(paymentData, signature),
437+
).to.changeTokenBalances(token, [userSigner, issuer1Signer, payment], [-10, 9, 1]);
438+
expect(await payment.isPaymentDone(issuer1Signer.address, 35)).to.be.true;
439+
// owner ERC-20 withdraw
440+
const tokenAddress = await token.getAddress();
441+
const ownerBalance = await payment.getOwnerERC20Balance(tokenAddress);
442+
expect(ownerBalance).to.be.eq(1);
443+
444+
await expect(
445+
payment.connect(userSigner).ownerERC20Withdraw(tokenAddress),
446+
).to.be.revertedWithCustomError(payment, "AccessControlUnauthorizedAccount");
447+
448+
// grant admin role to owner
449+
await payment.connect(owner).setAdminRole(owner.address);
450+
// grant WITHDRAWER_ROLE to userSigner
451+
await payment.grantRole(await payment.WITHDRAWER_ROLE(), await userSigner.getAddress());
452+
453+
await expect(
454+
payment.connect(userSigner).ownerERC20Withdraw(tokenAddress),
455+
).to.changeTokenBalances(token, [userSigner, payment], [1, -1]);
456+
457+
// second owner or withdrawer withdraw
458+
await expect(
459+
payment.connect(userSigner).ownerERC20Withdraw(tokenAddress),
460+
).to.be.revertedWithCustomError(payment, "WithdrawErrorNoBalance");
461+
462+
await expect(
463+
payment.connect(owner).ownerERC20Withdraw(tokenAddress),
464+
).to.be.revertedWithCustomError(payment, "WithdrawErrorNoBalance");
465+
expect(await payment.getOwnerERC20Balance(tokenAddress)).to.be.eq(0);
466+
});
467+
394468
it("ERC-20 payment with different issuer owner percentage:", async () => {
395469
const tokenFactory = await ethers.getContractFactory("ERC20Token", owner);
396470
const token = await tokenFactory.deploy(1_000);
@@ -432,6 +506,47 @@ describe("MC Payment Contract", () => {
432506
expect(await payment.getOwnerERC20Balance(tokenAddress)).to.be.eq(0);
433507
});
434508

509+
it("ERC-20 payment with different issuer owner percentage 100%:", async () => {
510+
const tokenFactory = await ethers.getContractFactory("ERC20Token", owner);
511+
const token = await tokenFactory.deploy(1_000);
512+
await token.connect(owner).transfer(await userSigner.getAddress(), 100);
513+
expect(await token.balanceOf(await userSigner.getAddress())).to.be.eq(100);
514+
515+
await token.connect(userSigner).approve(await payment.getAddress(), 10);
516+
517+
await payment.connect(owner).updateIssuerOwnerPercentage(issuer1Signer.address, 100);
518+
519+
const paymentData = {
520+
tokenAddress: await token.getAddress(),
521+
recipient: issuer1Signer.address,
522+
amount: 10,
523+
expirationDate: Math.round(new Date().getTime() / 1000) + 60 * 60, // 1 hour
524+
nonce: 35,
525+
metadata: "0x",
526+
};
527+
528+
const signature = await issuer1Signer.signTypedData(domainData, erc20types, paymentData);
529+
const erc20PaymentGas = await payment
530+
.connect(userSigner)
531+
.payERC20.estimateGas(paymentData, signature);
532+
console.log("ERC-20 Payment Gas: " + erc20PaymentGas);
533+
534+
await expect(
535+
payment.connect(userSigner).payERC20(paymentData, signature),
536+
).to.changeTokenBalances(token, [userSigner, issuer1Signer, payment], [-10, 0, 10]);
537+
expect(await payment.isPaymentDone(issuer1Signer.address, 35)).to.be.true;
538+
// owner ERC-20 withdraw
539+
const tokenAddress = await token.getAddress();
540+
const ownerBalance = await payment.getOwnerERC20Balance(tokenAddress);
541+
expect(ownerBalance).to.be.eq(10);
542+
await expect(payment.connect(owner).ownerERC20Withdraw(tokenAddress)).to.changeTokenBalances(
543+
token,
544+
[owner, payment],
545+
[10, -10],
546+
);
547+
expect(await payment.getOwnerERC20Balance(tokenAddress)).to.be.eq(0);
548+
});
549+
435550
it("ERC-20 payment - expired:", async () => {
436551
const tokenFactory = await ethers.getContractFactory("ERC20Token", owner);
437552
const token = await tokenFactory.deploy(1_000);

0 commit comments

Comments
 (0)