diff --git a/packages/contracts/contracts/Ledger.sol b/packages/contracts/contracts/Ledger.sol index ee1908de..366eac28 100644 --- a/packages/contracts/contracts/Ledger.sol +++ b/packages/contracts/contracts/Ledger.sol @@ -41,22 +41,6 @@ contract Ledger { mapping(string => PurchaseData) private purchases; string[] private purchaseIds; - struct PaymentData { - string purchaseId; - uint256 amount; - string currency; - bytes32 shopId; - address account; - bytes signature; - } - - struct PaymentDirectData { - string purchaseId; - uint256 amount; - string currency; - bytes32 shopId; - } - struct LoyaltyPaymentInputData { bytes32 paymentId; string purchaseId; @@ -172,31 +156,9 @@ contract Ledger { string purchaseId, bytes32 shopId ); - /// @notice 포인트로 지불을 완료했을 때 발생하는 이벤트 - event PaidPoint( - address account, - uint256 paidPoint, - uint256 paidValue, - uint256 feePoint, - uint256 feeValue, - uint256 balancePoint, - string purchaseId, - bytes32 shopId - ); - /// @notice 토큰으로 지불을 완료했을 때 발생하는 이벤트 - event PaidToken( - address account, - uint256 paidToken, - uint256 paidValue, - uint256 feeToken, - uint256 feeValue, - uint256 balanceToken, - string purchaseId, - bytes32 shopId - ); /// @notice 토큰/포인트로 지불을 완료했을 때 발생하는 이벤트 - event CreatedPayment( + event CreatedLoyaltyPayment( bytes32 paymentId, string purchaseId, string currency, @@ -213,7 +175,7 @@ contract Ledger { ); /// @notice 토큰/포인트로 지불의 취소가 완료되었을 때 발생하는 이벤트 - event CancelledLoyalty( + event CancelledLoyaltyPayment( bytes32 paymentId, string purchaseId, string currency, @@ -434,7 +396,7 @@ contract Ledger { /// @notice 로얄티(포인트/토큰)을 구매에 사용하는 함수 /// @dev 중계서버를 통해서 호출됩니다. - function createPayment(LoyaltyPaymentInputData calldata _data) public { + function createLoyaltyPayment(LoyaltyPaymentInputData calldata _data) public { require(loyaltyPayments[_data.paymentId].status == LoyaltyPaymentStatus.INVALID, "Payment ID already in use"); LoyaltyPaymentInputData memory data = _data; @@ -455,14 +417,14 @@ contract Ledger { ); if (loyaltyTypes[data.account] == LoyaltyType.POINT) { - _createPaymentPoint(data); + _createLoyaltyPaymentPoint(data); } else { - _createPaymentToken(data); + _createLoyaltyPaymentToken(data); } } /// @notice 포인트를 구매에 사용하는 함수 - function _createPaymentPoint(LoyaltyPaymentInputData memory _data) internal { + function _createLoyaltyPaymentPoint(LoyaltyPaymentInputData memory _data) internal { LoyaltyPaymentInputData memory data = _data; uint256 purchasePoint = convertCurrencyToPoint(data.amount, data.currency); uint256 purchaseToken = convertPointToToken(purchasePoint); @@ -519,7 +481,7 @@ contract Ledger { }); loyaltyPayments[payData.paymentId] = payData; - emit CreatedPayment( + emit CreatedLoyaltyPayment( payData.paymentId, payData.purchaseId, payData.currency, @@ -537,7 +499,7 @@ contract Ledger { } /// @notice 토큰을 구매에 사용하는 함수 - function _createPaymentToken(LoyaltyPaymentInputData memory _data) internal { + function _createLoyaltyPaymentToken(LoyaltyPaymentInputData memory _data) internal { LoyaltyPaymentInputData memory data = _data; uint256 purchasePoint = convertCurrencyToPoint(data.amount, data.currency); @@ -592,7 +554,7 @@ contract Ledger { }); loyaltyPayments[payData.paymentId] = payData; - emit CreatedPayment( + emit CreatedLoyaltyPayment( payData.paymentId, payData.purchaseId, payData.currency, @@ -609,7 +571,7 @@ contract Ledger { ); } - function cancelPayment(LoyaltyCancelInputData calldata _data) public { + function cancelLoyaltyPayment(LoyaltyCancelInputData calldata _data) public { require(loyaltyPayments[_data.paymentId].status != LoyaltyPaymentStatus.INVALID, "Payment ID does not exist"); require( block.timestamp <= loyaltyPayments[_data.paymentId].timestamp + 86400 * 7, @@ -639,7 +601,7 @@ contract Ledger { tokenBalances[feeAccount] -= payData.feeToken; pointBalances[data.account] += (payData.amountPoint + payData.feePoint); shopCollection.subUsedPoint(payData.shopId, payData.amountPoint, payData.purchaseId); - emit CancelledLoyalty( + emit CancelledLoyaltyPayment( payData.paymentId, payData.purchaseId, payData.currency, @@ -666,7 +628,7 @@ contract Ledger { tokenBalances[feeAccount] -= payData.feeToken; pointBalances[data.account] += (payData.amountToken + payData.feeToken); shopCollection.subUsedPoint(payData.shopId, payData.amountPoint, payData.purchaseId); - emit CancelledLoyalty( + emit CancelledLoyaltyPayment( payData.paymentId, payData.purchaseId, payData.currency, @@ -690,163 +652,6 @@ contract Ledger { nonce[certifierAddress]++; } - /// @notice 포인트를 구매에 사용하는 함수 - /// @dev 중계서버를 통해서 호출됩니다. - function payPoint(PaymentData calldata _data) public { - PaymentData memory data = _data; - bytes32 dataHash = keccak256( - abi.encode(data.purchaseId, data.amount, data.currency, data.shopId, data.account, nonce[data.account]) - ); - require( - ECDSA.recover(ECDSA.toEthSignedMessageHash(dataHash), data.signature) == data.account, - "Invalid signature" - ); - - PaymentDirectData memory directData = PaymentDirectData({ - purchaseId: data.purchaseId, - amount: data.amount, - currency: data.currency, - shopId: data.shopId - }); - - _payPoint(directData, data.account); - } - - /// @notice 포인트를 구매에 사용하는 함수 - /// @dev 사용자에 의해 직접 호출됩니다. - function payPointDirect(PaymentDirectData calldata _data) public { - _payPoint(_data, msg.sender); - } - - /// @notice 포인트를 구매에 사용하는 함수 - function _payPoint(PaymentDirectData memory _data, address _account) internal { - PaymentDirectData memory data = _data; - uint256 purchasePoint = convertCurrencyToPoint(data.amount, data.currency); - uint256 feeValue = (data.amount * fee) / 100; - uint256 feePoint = convertCurrencyToPoint(feeValue, data.currency); - uint256 feeToken = convertPointToToken(feePoint); - - require(pointBalances[_account] >= purchasePoint + feePoint, "Insufficient balance"); - require(tokenBalances[foundationAccount] >= feeToken, "Insufficient foundation balance"); - - pointBalances[_account] -= (purchasePoint + feePoint); - - // 재단의 토큰으로 교환해 지급한다. - tokenBalances[foundationAccount] -= feeToken; - tokenBalances[feeAccount] += feeToken; - - shopCollection.addUsedPoint(data.shopId, purchasePoint, data.purchaseId); - - uint256 settlementPoint = shopCollection.getSettlementPoint(data.shopId); - if (settlementPoint > 0) { - uint256 settlementToken = convertPointToToken(settlementPoint); - if (tokenBalances[foundationAccount] >= settlementToken) { - tokenBalances[settlementAccount] += settlementToken; - tokenBalances[foundationAccount] -= settlementToken; - shopCollection.addSettledPoint(data.shopId, settlementPoint, data.purchaseId); - emit ProvidedTokenForSettlement( - settlementAccount, - data.shopId, - settlementPoint, - settlementToken, - tokenBalances[settlementAccount], - data.purchaseId - ); - } - } - - nonce[_account]++; - - emit PaidPoint( - _account, - purchasePoint, - data.amount, - feePoint, - feeValue, - pointBalances[_account], - data.purchaseId, - data.shopId - ); - } - - /// @notice 토큰을 구매에 사용하는 함수 - /// @dev 중계서버를 통해서 호출됩니다. - function payToken(PaymentData calldata _data) public { - PaymentData memory data = _data; - bytes32 dataHash = keccak256( - abi.encode(data.purchaseId, data.amount, data.currency, data.shopId, data.account, nonce[data.account]) - ); - require( - ECDSA.recover(ECDSA.toEthSignedMessageHash(dataHash), data.signature) == data.account, - "Invalid signature" - ); - - PaymentDirectData memory directData = PaymentDirectData({ - purchaseId: data.purchaseId, - amount: data.amount, - currency: data.currency, - shopId: data.shopId - }); - - _payToken(directData, data.account); - } - - /// @notice 토큰을 구매에 사용하는 함수 - /// @dev 사용자에 의해 직접 호출됩니다. - function payTokenDirect(PaymentDirectData calldata _data) public { - _payToken(_data, msg.sender); - } - - /// @notice 토큰을 구매에 사용하는 함수 - function _payToken(PaymentDirectData memory _data, address _account) public { - PaymentDirectData memory data = _data; - - uint256 purchasePoint = convertCurrencyToPoint(data.amount, data.currency); - uint256 purchaseToken = convertPointToToken(purchasePoint); - uint256 feeValue = (data.amount * fee) / 100; - uint256 feePoint = convertCurrencyToPoint(feeValue, data.currency); - uint256 feeToken = convertPointToToken(feePoint); - - require(tokenBalances[_account] >= purchaseToken + feeToken, "Insufficient balance"); - - tokenBalances[_account] -= (purchaseToken + feeToken); - tokenBalances[foundationAccount] += purchaseToken; - tokenBalances[feeAccount] += feeToken; - - shopCollection.addUsedPoint(data.shopId, purchasePoint, data.purchaseId); - - uint256 settlementPoint = shopCollection.getSettlementPoint(data.shopId); - if (settlementPoint > 0) { - uint256 settlementToken = convertPointToToken(settlementPoint); - if (tokenBalances[foundationAccount] >= settlementToken) { - tokenBalances[settlementAccount] += settlementToken; - tokenBalances[foundationAccount] -= settlementToken; - shopCollection.addSettledPoint(data.shopId, settlementPoint, data.purchaseId); - emit ProvidedTokenForSettlement( - settlementAccount, - data.shopId, - settlementPoint, - settlementToken, - tokenBalances[settlementAccount], - data.purchaseId - ); - } - } - - nonce[_account]++; - - emit PaidToken( - _account, - purchaseToken, - data.amount, - feeToken, - feeValue, - tokenBalances[_account], - data.purchaseId, - data.shopId - ); - } - function convertPointToToken(uint256 amount) internal view returns (uint256) { uint256 price = currencyRate.get(token.symbol()); return (amount * currencyRate.MULTIPLE()) / price; diff --git a/packages/contracts/test/03-Ledger.test.ts b/packages/contracts/test/03-Ledger.test.ts index edd4d5cf..0f57b4f0 100644 --- a/packages/contracts/test/03-Ledger.test.ts +++ b/packages/contracts/test/03-Ledger.test.ts @@ -654,7 +654,7 @@ describe("Test for Ledger", () => { expect(await ledgerContract.unPayablePointBalanceOf(phoneHash)).to.equal(0); }); - it("Change point type (user: 3, point type : 0)", async () => { + it("Change Loyalty Type (user: 3, point type : 0)", async () => { const userIndex = 3; await expect( ledgerContract @@ -797,7 +797,7 @@ describe("Test for Ledger", () => { ); }); - it("Change point type (user: 1)", async () => { + it("Change Loyalty Type (user: 1)", async () => { const userIndex = 1; const nonce = await ledgerContract.nonceOf(userWallets[userIndex].address); const signature = ContractUtils.signLoyaltyType(userWallets[userIndex], nonce); @@ -1033,7 +1033,7 @@ describe("Test for Ledger", () => { expect(await ledgerContract.unPayablePointBalanceOf(phoneHash)).to.equal(0); }); - it("Change point type (user: 4, point type : 0)", async () => { + it("Change Loyalty Type (user: 4, point type : 0)", async () => { const userIndex = 4; await expect( ledgerContract @@ -1123,11 +1123,13 @@ describe("Test for Ledger", () => { userIndex: 0, }; + const paymentId = ContractUtils.getPaymentId(userWallets[purchase.userIndex].address); const purchaseAmount = Amount.make(purchase.amount, 18).value; const shop = shopData[purchase.shopIndex]; const nonce = await ledgerContract.nonceOf(userWallets[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( + const signature = await ContractUtils.signLoyaltyPayment( userWallets[purchase.userIndex], + paymentId, purchase.purchaseId, purchaseAmount, purchase.currency, @@ -1135,7 +1137,8 @@ describe("Test for Ledger", () => { nonce ); await expect( - ledgerContract.connect(relay).payPoint({ + ledgerContract.connect(relay).createLoyaltyPayment({ + paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, currency: purchase.currency.toLowerCase(), @@ -1157,11 +1160,13 @@ describe("Test for Ledger", () => { userIndex: 0, }; + const paymentId = ContractUtils.getPaymentId(userWallets[purchase.userIndex].address); const purchaseAmount = Amount.make(purchase.amount, 18).value; const shop = shopData[purchase.shopIndex]; const nonce = await ledgerContract.nonceOf(userWallets[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( + const signature = await ContractUtils.signLoyaltyPayment( userWallets[purchase.userIndex], + paymentId, purchase.purchaseId, purchaseAmount, purchase.currency, @@ -1169,7 +1174,8 @@ describe("Test for Ledger", () => { nonce ); await expect( - ledgerContract.connect(relay).payPoint({ + ledgerContract.connect(relay).createLoyaltyPayment({ + paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, currency: purchase.currency.toLowerCase(), @@ -1191,11 +1197,13 @@ describe("Test for Ledger", () => { userIndex: 0, }; + const paymentId = ContractUtils.getPaymentId(userWallets[purchase.userIndex].address); const purchaseAmount = Amount.make(purchase.amount, 18).value; const shop = shopData[purchase.shopIndex]; const nonce = await ledgerContract.nonceOf(userWallets[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( + const signature = await ContractUtils.signLoyaltyPayment( userWallets[purchase.userIndex], + paymentId, purchase.purchaseId, purchaseAmount, purchase.currency, @@ -1206,7 +1214,8 @@ describe("Test for Ledger", () => { const feeToken = feeAmount.mul(multiple).div(price); const oldFeeBalance = await ledgerContract.tokenBalanceOf(fee.address); await expect( - ledgerContract.connect(relay).payPoint({ + ledgerContract.connect(relay).createLoyaltyPayment({ + paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, currency: purchase.currency.toLowerCase(), @@ -1215,15 +1224,18 @@ describe("Test for Ledger", () => { signature, }) ) - .to.emit(ledgerContract, "PaidPoint") + .to.emit(ledgerContract, "CreatedLoyaltyPayment") .withNamedArgs({ + paymentId, + purchaseId: purchase.purchaseId, + currency: purchase.currency.toLowerCase(), account: userWallets[purchase.userIndex].address, + shopId: shop.shopId, + loyaltyType: 0, paidPoint: purchaseAmount, paidValue: purchaseAmount, feePoint: feeAmount, feeValue: feeAmount, - purchaseId: purchase.purchaseId, - shopId: shop.shopId, }); const newFeeBalance = await ledgerContract.tokenBalanceOf(fee.address); expect(newFeeBalance).to.deep.equal(oldFeeBalance.add(feeToken)); @@ -1264,7 +1276,7 @@ describe("Test for Ledger", () => { ); const oldFeeBalance = await ledgerContract.tokenBalanceOf(fee.address); await expect( - ledgerContract.connect(relay).createPayment({ + ledgerContract.connect(relay).createLoyaltyPayment({ paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, @@ -1274,7 +1286,7 @@ describe("Test for Ledger", () => { signature, }) ) - .to.emit(ledgerContract, "CreatedPayment") + .to.emit(ledgerContract, "CreatedLoyaltyPayment") .withNamedArgs({ paymentId, purchaseId: purchase.purchaseId, @@ -1307,7 +1319,7 @@ describe("Test for Ledger", () => { certifierNonce ); await expect( - ledgerContract.connect(relay).cancelPayment({ + ledgerContract.connect(relay).cancelLoyaltyPayment({ paymentId, purchaseId: purchase.purchaseId, account: userWallets[purchase.userIndex].address, @@ -1315,7 +1327,7 @@ describe("Test for Ledger", () => { certifierSignature, }) ) - .to.emit(ledgerContract, "CancelledLoyalty") + .to.emit(ledgerContract, "CancelledLoyaltyPayment") .withNamedArgs({ paymentId, purchaseId: purchase.purchaseId, @@ -1343,11 +1355,13 @@ describe("Test for Ledger", () => { userIndex: 1, }; + const paymentId = ContractUtils.getPaymentId(userWallets[purchase.userIndex].address); const purchaseAmount = Amount.make(purchase.amount, 18).value; const shop = shopData[purchase.shopIndex]; const nonce = await ledgerContract.nonceOf(userWallets[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( + const signature = await ContractUtils.signLoyaltyPayment( userWallets[purchase.userIndex], + paymentId, purchase.purchaseId, purchaseAmount, purchase.currency, @@ -1355,7 +1369,8 @@ describe("Test for Ledger", () => { nonce ); await expect( - ledgerContract.connect(relay).payToken({ + ledgerContract.connect(relay).createLoyaltyPayment({ + paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, currency: purchase.currency.toLowerCase(), @@ -1377,11 +1392,13 @@ describe("Test for Ledger", () => { userIndex: 1, }; + const paymentId = ContractUtils.getPaymentId(userWallets[purchase.userIndex].address); const purchaseAmount = Amount.make(purchase.amount, 18).value; const shop = shopData[purchase.shopIndex]; const nonce = await ledgerContract.nonceOf(userWallets[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( + const signature = await ContractUtils.signLoyaltyPayment( userWallets[purchase.userIndex], + paymentId, purchase.purchaseId, purchaseAmount, purchase.currency, @@ -1389,7 +1406,8 @@ describe("Test for Ledger", () => { nonce ); await expect( - ledgerContract.connect(relay).payToken({ + ledgerContract.connect(relay).createLoyaltyPayment({ + paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, currency: purchase.currency.toLowerCase(), @@ -1411,13 +1429,15 @@ describe("Test for Ledger", () => { userIndex: 1, }; + const paymentId = ContractUtils.getPaymentId(userWallets[purchase.userIndex].address); const purchaseAmount = Amount.make(purchase.amount, 18).value; const tokenAmount = purchaseAmount.mul(multiple).div(price); const oldFoundationTokenBalance = await ledgerContract.tokenBalanceOf(foundation.address); const shop = shopData[purchase.shopIndex]; const nonce = await ledgerContract.nonceOf(userWallets[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( + const signature = await ContractUtils.signLoyaltyPayment( userWallets[purchase.userIndex], + paymentId, purchase.purchaseId, purchaseAmount, purchase.currency, @@ -1428,7 +1448,8 @@ describe("Test for Ledger", () => { const feeToken = feeAmount.mul(multiple).div(price); const oldFeeBalance = await ledgerContract.tokenBalanceOf(fee.address); await expect( - ledgerContract.connect(relay).payToken({ + ledgerContract.connect(relay).createLoyaltyPayment({ + paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, currency: purchase.currency.toLowerCase(), @@ -1437,15 +1458,18 @@ describe("Test for Ledger", () => { signature, }) ) - .to.emit(ledgerContract, "PaidToken") + .to.emit(ledgerContract, "CreatedLoyaltyPayment") .withNamedArgs({ + paymentId, + purchaseId: purchase.purchaseId, + currency: purchase.currency.toLowerCase(), account: userWallets[purchase.userIndex].address, + shopId: shop.shopId, + loyaltyType: 1, paidToken: tokenAmount, paidValue: purchaseAmount, feeToken, feeValue: feeAmount, - purchaseId: purchase.purchaseId, - shopId: shop.shopId, }); expect(await ledgerContract.tokenBalanceOf(foundation.address)).to.deep.equal( oldFoundationTokenBalance.add(tokenAmount) @@ -1491,7 +1515,7 @@ describe("Test for Ledger", () => { ); const oldFeeBalance = await ledgerContract.tokenBalanceOf(fee.address); await expect( - ledgerContract.connect(relay).createPayment({ + ledgerContract.connect(relay).createLoyaltyPayment({ paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, @@ -1501,7 +1525,7 @@ describe("Test for Ledger", () => { signature, }) ) - .to.emit(ledgerContract, "CreatedPayment") + .to.emit(ledgerContract, "CreatedLoyaltyPayment") .withNamedArgs({ paymentId, purchaseId: purchase.purchaseId, @@ -1537,7 +1561,7 @@ describe("Test for Ledger", () => { certifierNonce ); await expect( - ledgerContract.connect(relay).cancelPayment({ + ledgerContract.connect(relay).cancelLoyaltyPayment({ paymentId, purchaseId: purchase.purchaseId, account: userWallets[purchase.userIndex].address, @@ -1545,7 +1569,7 @@ describe("Test for Ledger", () => { certifierSignature, }) ) - .to.emit(ledgerContract, "CancelledLoyalty") + .to.emit(ledgerContract, "CancelledLoyaltyPayment") .withNamedArgs({ paymentId, purchaseId: purchase.purchaseId, @@ -2100,11 +2124,13 @@ describe("Test for Ledger", () => { userIndex: 0, }; + const paymentId = ContractUtils.getPaymentId(userWallets[purchase.userIndex].address); const purchaseAmount = Amount.make(purchase.amount, 18).value; const shop = shopData[purchase.shopIndex]; const nonce = await ledgerContract.nonceOf(userWallets[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( + const signature = await ContractUtils.signLoyaltyPayment( userWallets[purchase.userIndex], + paymentId, purchase.purchaseId, purchaseAmount, purchase.currency, @@ -2114,7 +2140,8 @@ describe("Test for Ledger", () => { const amt = purchaseAmount.mul(shop.providePercent).div(100); await expect( - ledgerContract.connect(relay).payPoint({ + ledgerContract.connect(relay).createLoyaltyPayment({ + paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, currency: purchase.currency.toLowerCase(), @@ -2129,13 +2156,14 @@ describe("Test for Ledger", () => { shopId: shop.shopId, providedPoint: Amount.make(200, 18).value, }) - .to.emit(ledgerContract, "PaidPoint") + .to.emit(ledgerContract, "CreatedLoyaltyPayment") .withNamedArgs({ + paymentId, + purchaseId: purchase.purchaseId, account: userWallets[purchase.userIndex].address, + shopId: shop.shopId, paidPoint: purchaseAmount, paidValue: purchaseAmount, - purchaseId: purchase.purchaseId, - shopId: shop.shopId, }); const shopInfo = await shopCollection.shopOf(shop.shopId); expect(shopInfo.providedPoint).to.equal(Amount.make(100, 18).value); @@ -2144,18 +2172,32 @@ describe("Test for Ledger", () => { }); }); + context("Change Loyalty Type", () => { + it("Change Loyalty Type (user: 0)", async () => { + const userIndex = 0; + await expect( + ledgerContract + .connect(userWallets[userIndex].connect(hre.waffle.provider)) + .changeToLoyaltyTokenDirect() + ) + .to.emit(ledgerContract, "ChangedToLoyaltyToken") + .withNamedArgs({ account: userWallets[userIndex].address }); + }); + }); + context("Deposit token", () => { it("Deposit token - Success", async () => { - const oldTokenBalance = await ledgerContract.tokenBalanceOf(userWallets[0].address); - await tokenContract.connect(userWallets[0]).approve(ledgerContract.address, amount.value); - await expect(ledgerContract.connect(userWallets[0]).deposit(amount.value)) + const userIndex = 0; + const oldTokenBalance = await ledgerContract.tokenBalanceOf(userWallets[userIndex].address); + await tokenContract.connect(userWallets[userIndex]).approve(ledgerContract.address, amount.value); + await expect(ledgerContract.connect(userWallets[userIndex]).deposit(amount.value)) .to.emit(ledgerContract, "Deposited") .withNamedArgs({ - account: userWallets[0].address, + account: userWallets[userIndex].address, depositedToken: amount.value, balanceToken: oldTokenBalance.add(amount.value), }); - expect(await ledgerContract.tokenBalanceOf(userWallets[0].address)).to.deep.equal( + expect(await ledgerContract.tokenBalanceOf(userWallets[userIndex].address)).to.deep.equal( oldTokenBalance.add(amount.value) ); }); @@ -2173,13 +2215,15 @@ describe("Test for Ledger", () => { userIndex: 0, }; + const paymentId = ContractUtils.getPaymentId(userWallets[purchase.userIndex].address); const purchaseAmount = Amount.make(purchase.amount, 18).value; const tokenAmount = purchaseAmount.mul(multiple).div(price); const oldFoundationTokenBalance = await ledgerContract.tokenBalanceOf(foundation.address); const shop = shopData[purchase.shopIndex]; const nonce = await ledgerContract.nonceOf(userWallets[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( + const signature = await ContractUtils.signLoyaltyPayment( userWallets[purchase.userIndex], + paymentId, purchase.purchaseId, purchaseAmount, purchase.currency, @@ -2188,7 +2232,8 @@ describe("Test for Ledger", () => { ); await expect( - ledgerContract.connect(relay).payToken({ + ledgerContract.connect(relay).createLoyaltyPayment({ + paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, currency: purchase.currency.toLowerCase(), @@ -2197,13 +2242,15 @@ describe("Test for Ledger", () => { signature, }) ) - .to.emit(ledgerContract, "PaidToken") + .to.emit(ledgerContract, "CreatedLoyaltyPayment") .withNamedArgs({ + paymentId, + purchaseId: purchase.purchaseId, account: userWallets[purchase.userIndex].address, + shopId: shop.shopId, + loyaltyType: 1, paidToken: tokenAmount, paidValue: purchaseAmount, - purchaseId: purchase.purchaseId, - shopId: shop.shopId, }); const shopInfo2 = await shopCollection.shopOf(shop.shopId); expect(shopInfo2.providedPoint).to.equal(Amount.make(100, 18).value); @@ -2272,445 +2319,6 @@ describe("Test for Ledger", () => { }); }); - context("Clearing for shops [Direct]", () => { - const userData: IUserData[] = [ - { - phone: "08201012341001", - address: userWallets[0].address, - privateKey: userWallets[0].privateKey, - }, - { - phone: "08201012341002", - address: userWallets[1].address, - privateKey: userWallets[1].privateKey, - }, - { - phone: "08201012341003", - address: userWallets[2].address, - privateKey: userWallets[2].privateKey, - }, - { - phone: "08201012341004", - address: userWallets[3].address, - privateKey: userWallets[3].privateKey, - }, - { - phone: "08201012341005", - address: userWallets[4].address, - privateKey: userWallets[4].privateKey, - }, - ]; - - const purchaseData: IPurchaseData[] = [ - { - purchaseId: "P000001", - timestamp: 1672844400, - amount: 10000, - method: 0, - currency: "krw", - shopIndex: 0, - userIndex: 0, - }, - { - purchaseId: "P000002", - timestamp: 1675522800, - amount: 10000, - method: 0, - currency: "krw", - shopIndex: 0, - userIndex: 0, - }, - { - purchaseId: "P000003", - timestamp: 1677942000, - amount: 10000, - method: 0, - currency: "krw", - shopIndex: 0, - userIndex: 0, - }, - { - purchaseId: "P000004", - timestamp: 1680620400, - amount: 10000, - method: 0, - currency: "krw", - shopIndex: 1, - userIndex: 0, - }, - { - purchaseId: "P000005", - timestamp: 1683212400, - amount: 10000, - method: 0, - currency: "krw", - shopIndex: 2, - userIndex: 0, - }, - { - purchaseId: "P000005", - timestamp: 1683212400, - amount: 10000, - method: 0, - currency: "krw", - shopIndex: 3, - userIndex: 0, - }, - ]; - - const shopData: IShopData[] = [ - { - shopId: "F000100", - name: "Shop1", - provideWaitTime: 0, - providePercent: 1, - wallet: shopWallets[0], - }, - { - shopId: "F000200", - name: "Shop2", - provideWaitTime: 0, - providePercent: 1, - wallet: shopWallets[1], - }, - { - shopId: "F000300", - name: "Shop3", - provideWaitTime: 0, - providePercent: 1, - wallet: shopWallets[2], - }, - { - shopId: "F000400", - name: "Shop4", - provideWaitTime: 0, - providePercent: 1, - wallet: shopWallets[3], - }, - { - shopId: "F000500", - name: "Shop5", - provideWaitTime: 0, - providePercent: 1, - wallet: shopWallets[4], - }, - { - shopId: "F000600", - name: "Shop6", - provideWaitTime: 0, - providePercent: 1, - wallet: shopWallets[5], - }, - ]; - - before("Set Shop ID", async () => { - for (const elem of shopData) { - elem.shopId = ContractUtils.getShopId(elem.wallet.address); - } - }); - - before("Deploy", async () => { - await deployAllContract(); - }); - - before("Prepare Token", async () => { - for (const elem of userWallets) { - await tokenContract.connect(deployer).transfer(elem.address, amount.value); - } - }); - - context("Prepare shop data", () => { - it("Add Shop Data", async () => { - for (const elem of shopData) { - await expect( - shopCollection - .connect(elem.wallet) - .addDirect(elem.shopId, elem.name, elem.provideWaitTime, elem.providePercent) - ) - .to.emit(shopCollection, "AddedShop") - .withNamedArgs({ - shopId: elem.shopId, - name: elem.name, - provideWaitTime: elem.provideWaitTime, - providePercent: elem.providePercent, - account: elem.wallet.address, - }); - } - expect(await shopCollection.shopsLength()).to.equal(shopData.length); - }); - }); - - context("Prepare foundation's asset", () => { - it("Deposit foundation's token", async () => { - await tokenContract.connect(deployer).transfer(foundation.address, assetAmount.value); - await tokenContract.connect(foundation).approve(ledgerContract.address, assetAmount.value); - await expect(ledgerContract.connect(foundation).deposit(assetAmount.value)) - .to.emit(ledgerContract, "Deposited") - .withNamedArgs({ - account: foundation.address, - depositedToken: assetAmount.value, - balanceToken: assetAmount.value, - }); - }); - }); - - context("Save Purchase Data", () => { - it("Save Purchase Data", async () => { - for (const purchase of purchaseData) { - const phoneHash = ContractUtils.getPhoneHash(userData[purchase.userIndex].phone); - const purchaseAmount = Amount.make(purchase.amount, 18).value; - const shop = shopData[purchase.shopIndex]; - const amt = purchaseAmount.mul(shop.providePercent).div(100); - const userAccount = - userData[purchase.userIndex].address.trim() !== "" - ? userData[purchase.userIndex].address.trim() - : AddressZero; - await expect( - ledgerContract.connect(validatorWallets[0]).savePurchase({ - purchaseId: purchase.purchaseId, - timestamp: purchase.timestamp, - amount: purchaseAmount, - currency: purchase.currency.toLowerCase(), - shopId: shopData[purchase.shopIndex].shopId, - method: purchase.method, - account: userAccount, - phone: phoneHash, - }) - ) - .to.emit(ledgerContract, "SavedPurchase") - .withArgs( - purchase.purchaseId, - purchase.timestamp, - purchaseAmount, - purchase.currency.toLowerCase(), - shopData[purchase.shopIndex].shopId, - purchase.method, - userAccount, - phoneHash - ) - .emit(ledgerContract, "ProvidedPoint") - .withNamedArgs({ - account: userAccount, - providedPoint: amt, - providedValue: amt, - purchaseId: purchase.purchaseId, - }); - } - }); - - it("Check balances", async () => { - const expected: Map = new Map(); - for (const purchase of purchaseData) { - const purchaseAmount = Amount.make(purchase.amount, 18).value; - const key = - userData[purchase.userIndex].address.trim() !== "" - ? userData[purchase.userIndex].address.trim() - : ContractUtils.getPhoneHash(userData[purchase.userIndex].phone.trim()); - const oldValue = expected.get(key); - - const shop = shopData[purchase.shopIndex]; - const point = purchaseAmount.mul(shop.providePercent).div(100); - - if (oldValue !== undefined) expected.set(key, oldValue.add(point)); - else expected.set(key, point); - } - for (const key of expected.keys()) { - if (key.match(/^0x[A-Fa-f0-9]{64}$/i)) { - expect(await ledgerContract.unPayablePointBalanceOf(key)).to.deep.equal(expected.get(key)); - } else { - expect(await ledgerContract.pointBalanceOf(key)).to.deep.equal(expected.get(key)); - } - } - }); - - it("Check shop data", async () => { - const shopInfo1 = await shopCollection.shopOf(shopData[0].shopId); - expect(shopInfo1.providedPoint).to.equal( - Amount.make(10000 * 3, 18) - .value.mul(shopData[0].providePercent) - .div(100) - ); - - const shopInfo2 = await shopCollection.shopOf(shopData[1].shopId); - expect(shopInfo2.providedPoint).to.equal( - Amount.make(10000 * 1, 18) - .value.mul(shopData[1].providePercent) - .div(100) - ); - const shopInfo3 = await shopCollection.shopOf(shopData[2].shopId); - expect(shopInfo3.providedPoint).to.equal( - Amount.make(10000 * 1, 18) - .value.mul(shopData[2].providePercent) - .div(100) - ); - const shopInfo4 = await shopCollection.shopOf(shopData[3].shopId); - expect(shopInfo4.providedPoint).to.equal( - Amount.make(10000 * 1, 18) - .value.mul(shopData[3].providePercent) - .div(100) - ); - }); - }); - - context("Pay point", () => { - it("Pay point - Success", async () => { - const purchase = { - purchaseId: "P000100", - timestamp: 1672849000, - amount: 300, - method: 0, - currency: "krw", - shopIndex: 1, - userIndex: 0, - }; - - const purchaseAmount = Amount.make(purchase.amount, 18).value; - const shop = shopData[purchase.shopIndex]; - const amt = purchaseAmount.mul(shop.providePercent).div(100); - await expect( - ledgerContract - .connect(userWallets[purchase.userIndex].connect(hre.waffle.provider)) - .payPointDirect({ - purchaseId: purchase.purchaseId, - amount: purchaseAmount, - currency: purchase.currency.toLowerCase(), - shopId: shop.shopId, - }) - ) - .to.emit(ledgerContract, "ProvidedTokenForSettlement") - .withNamedArgs({ - account: settlements.address, - shopId: shop.shopId, - providedPoint: Amount.make(200, 18).value, - }) - .to.emit(ledgerContract, "PaidPoint") - .withNamedArgs({ - account: userWallets[purchase.userIndex].address, - paidPoint: purchaseAmount, - paidValue: purchaseAmount, - purchaseId: purchase.purchaseId, - shopId: shop.shopId, - }); - const shopInfo = await shopCollection.shopOf(shop.shopId); - expect(shopInfo.providedPoint).to.equal(Amount.make(100, 18).value); - expect(shopInfo.usedPoint).to.equal(Amount.make(300, 18).value); - expect(shopInfo.settledPoint).to.equal(Amount.make(200, 18).value); - }); - }); - - context("Deposit token", () => { - it("Deposit token - Success", async () => { - const oldTokenBalance = await ledgerContract.tokenBalanceOf(userWallets[0].address); - await tokenContract.connect(userWallets[0]).approve(ledgerContract.address, amount.value); - await expect(ledgerContract.connect(userWallets[0]).deposit(amount.value)) - .to.emit(ledgerContract, "Deposited") - .withNamedArgs({ - account: userWallets[0].address, - depositedToken: amount.value, - balanceToken: oldTokenBalance.add(amount.value), - }); - expect(await ledgerContract.tokenBalanceOf(userWallets[0].address)).to.deep.equal( - oldTokenBalance.add(amount.value) - ); - }); - }); - - context("Pay token", () => { - it("Pay token - Success", async () => { - const purchase: IPurchaseData = { - purchaseId: "P000200", - timestamp: 1672849000, - amount: 500, - method: 0, - currency: "krw", - shopIndex: 2, - userIndex: 0, - }; - - const purchaseAmount = Amount.make(purchase.amount, 18).value; - const tokenAmount = purchaseAmount.mul(multiple).div(price); - const oldFoundationTokenBalance = await ledgerContract.tokenBalanceOf(foundation.address); - const shop = shopData[purchase.shopIndex]; - - await expect( - ledgerContract - .connect(userWallets[purchase.userIndex].connect(hre.waffle.provider)) - .payTokenDirect({ - purchaseId: purchase.purchaseId, - amount: purchaseAmount, - currency: purchase.currency.toLowerCase(), - shopId: shop.shopId, - }) - ) - .to.emit(ledgerContract, "PaidToken") - .withNamedArgs({ - account: userWallets[purchase.userIndex].address, - paidToken: tokenAmount, - paidValue: purchaseAmount, - purchaseId: purchase.purchaseId, - shopId: shop.shopId, - }); - const shopInfo2 = await shopCollection.shopOf(shop.shopId); - expect(shopInfo2.providedPoint).to.equal(Amount.make(100, 18).value); - expect(shopInfo2.usedPoint).to.equal(Amount.make(500, 18).value); - expect(shopInfo2.settledPoint).to.equal(Amount.make(400, 18).value); - - const settledToken = shopInfo2.settledPoint.mul(multiple).div(price); - expect((await ledgerContract.tokenBalanceOf(foundation.address)).toString()).to.deep.equal( - oldFoundationTokenBalance.add(tokenAmount).sub(settledToken).toString() - ); - }); - }); - - context("Withdrawal of settlement", () => { - const shopIndex = 2; - const shop = shopData[shopIndex]; - const amount2 = Amount.make(400, 18).value; - it("Check Settlement", async () => { - const withdrawalAmount = await shopCollection.withdrawableOf(shop.shopId); - expect(withdrawalAmount).to.equal(amount2); - }); - - it("Open Withdrawal", async () => { - await expect( - shopCollection - .connect(shopData[shopIndex].wallet.connect(hre.waffle.provider)) - .openWithdrawalDirect(shop.shopId, amount2) - ) - .to.emit(shopCollection, "OpenedWithdrawal") - .withNamedArgs({ - shopId: shop.shopId, - amount: amount2, - account: shopWallets[shopIndex].address, - }); - const withdrawalAmount = await shopCollection.withdrawableOf(shop.shopId); - expect(withdrawalAmount).to.equal(amount2); - }); - - it("Close Withdrawal", async () => { - const nonce = await shopCollection.nonceOf(shopData[shopIndex].wallet.address); - const signature = await ContractUtils.signShopId( - shopData[shopIndex].wallet, - shopData[shopIndex].shopId, - nonce - ); - await expect( - shopCollection - .connect(shopData[shopIndex].wallet.connect(hre.waffle.provider)) - .closeWithdrawalDirect(shop.shopId) - ) - .to.emit(shopCollection, "ClosedWithdrawal") - .withNamedArgs({ - shopId: shop.shopId, - amount: amount2, - account: shopWallets[shopIndex].address, - }); - const withdrawalAmount = await shopCollection.withdrawableOf(shop.shopId); - expect(withdrawalAmount).to.equal(0); - }); - }); - }); - context("Multi Currency", () => { const userData: IUserData[] = [ { diff --git a/packages/relay/src/routers/LedgerRouter.ts b/packages/relay/src/routers/LedgerRouter.ts index 234878d1..b5fdb6ae 100644 --- a/packages/relay/src/routers/LedgerRouter.ts +++ b/packages/relay/src/routers/LedgerRouter.ts @@ -164,46 +164,6 @@ export class LedgerRouter { } public registerRoutes() { - // 포인트를 이용하여 구매 - this.app.post( - "/v1/ledger/payPoint", - [ - body("purchaseId").exists(), - body("amount").exists().custom(Validation.isAmount), - body("currency").exists(), - body("shopId") - .exists() - .trim() - .matches(/^(0x)[0-9a-f]{64}$/i), - body("account").exists().trim().isEthereumAddress(), - body("signature") - .exists() - .trim() - .matches(/^(0x)[0-9a-f]{130}$/i), - ], - this.payPoint.bind(this) - ); - - // 토큰을 이용하여 구매할 때 - this.app.post( - "/v1/ledger/payToken", - [ - body("purchaseId").exists(), - body("amount").exists().trim().custom(Validation.isAmount), - body("currency").exists(), - body("shopId") - .exists() - .trim() - .matches(/^(0x)[0-9a-f]{64}$/i), - body("account").exists().trim().isEthereumAddress(), - body("signature") - .exists() - .trim() - .matches(/^(0x)[0-9a-f]{130}$/i), - ], - this.payToken.bind(this) - ); - // 포인트의 종류를 선택하는 기능 this.app.post( "/v1/ledger/changeToLoyaltyToken", @@ -234,121 +194,6 @@ export class LedgerRouter { ); } - /** - * 사용자 포인트 지불 - * POST /v1/ledger/payPoint - * @private - */ - private async payPoint(req: express.Request, res: express.Response) { - logger.http(`POST /v1/ledger/payPoint`); - - const errors = validationResult(req); - if (!errors.isEmpty()) { - return res.status(200).json( - this.makeResponseData(501, undefined, { - message: "Failed to check the validity of parameters.", - validation: errors.array(), - }) - ); - } - - const signerItem = await this.getRelaySigner(); - try { - const purchaseId: string = String(req.body.purchaseId).trim(); // 구매 아이디 - const amount: string = String(req.body.amount).trim(); // 구매 금액 - const currency: string = String(req.body.currency).toLowerCase().trim(); // 구매한 금액 통화코드 - const shopId: string = String(req.body.shopId).trim(); // 구매한 가맹점 아이디 - const account: string = String(req.body.account).trim(); // 구매자의 주소 - const signature: string = String(req.body.signature).trim(); // 서명 - - // TODO amount > 0 조건 검사 - - // 서명검증 - const userNonce = await (await this.getLedgerContract()).nonceOf(account); - if (!ContractUtils.verifyPayment(purchaseId, amount, currency, shopId, account, userNonce, signature)) - return res.status(200).json( - this.makeResponseData(500, undefined, { - message: "Signature is not valid.", - }) - ); - - const tx = await (await this.getLedgerContract()) - .connect(signerItem.signer) - .payPoint({ purchaseId, amount, currency, shopId, account, signature }); - - logger.http(`TxHash(payPoint): `, tx.hash); - return res.status(200).json(this.makeResponseData(200, { txHash: tx.hash })); - } catch (error: any) { - let message = ContractUtils.cacheEVMError(error as any); - if (message === "") message = "Failed pay point"; - logger.error(`POST /v1/ledger/payPoint :`, message); - return res.status(200).json( - this.makeResponseData(500, undefined, { - message, - }) - ); - } finally { - this.releaseRelaySigner(signerItem); - } - } - - /** - * 사용자 토큰 지불 - * POST /v1/ledger/payToken - * @private - */ - private async payToken(req: express.Request, res: express.Response) { - logger.http(`POST /v1/ledger/payToken`); - - const errors = validationResult(req); - if (!errors.isEmpty()) { - return res.status(200).json( - this.makeResponseData(501, undefined, { - message: "Failed to check the validity of parameters.", - validation: errors.array(), - }) - ); - } - - const signerItem = await this.getRelaySigner(); - try { - const purchaseId: string = String(req.body.purchaseId).trim(); // 구매 아이디 - const amount: string = String(req.body.amount).trim(); // 구매 금액 - const currency: string = String(req.body.currency).toLowerCase().trim(); // 구매한 금액 통화코드 - const shopId: string = String(req.body.shopId).trim(); // 구매한 가맹점 아이디 - const account: string = String(req.body.account).trim(); // 구매자의 주소 - const signature: string = String(req.body.signature).trim(); // 서명 - - // TODO amount > 0 조건 검사 - // 서명검증 - const userNonce = await (await this.getLedgerContract()).nonceOf(account); - if (!ContractUtils.verifyPayment(purchaseId, amount, currency, shopId, account, userNonce, signature)) - return res.status(200).json( - this.makeResponseData(500, undefined, { - message: "Signature is not valid.", - }) - ); - - const tx = await (await this.getLedgerContract()) - .connect(signerItem.signer) - .payToken({ purchaseId, amount, currency, shopId, account, signature }); - - logger.http(`TxHash(payToken): `, tx.hash); - return res.status(200).json(this.makeResponseData(200, { txHash: tx.hash })); - } catch (error: any) { - let message = ContractUtils.cacheEVMError(error as any); - if (message === "") message = "Failed pay token"; - logger.error(`POST /v1/ledger/payToken :`, message); - return res.status(200).json( - this.makeResponseData(500, undefined, { - message, - }) - ); - } finally { - this.releaseRelaySigner(signerItem); - } - } - /** * 포인트의 종류를 선택한다. * POST /v1/ledger/changeToLoyaltyToken diff --git a/packages/relay/src/utils/ContractUtils.ts b/packages/relay/src/utils/ContractUtils.ts index 66e8a261..476bb7ca 100644 --- a/packages/relay/src/utils/ContractUtils.ts +++ b/packages/relay/src/utils/ContractUtils.ts @@ -296,4 +296,77 @@ export class ContractUtils { } return res.toLowerCase() === account.toLowerCase(); } + + public static getLoyaltyPaymentMessage( + address: string, + paymentId: string, + purchaseId: string, + amount: BigNumberish, + currency: string, + shopId: string, + nonce: BigNumberish + ): Uint8Array { + const encodedResult = hre.ethers.utils.defaultAbiCoder.encode( + ["bytes32", "string", "uint256", "string", "bytes32", "address", "uint256"], + [paymentId, purchaseId, amount, currency, shopId, address, nonce] + ); + return arrayify(hre.ethers.utils.keccak256(encodedResult)); + } + + public static async signLoyaltyPayment( + signer: Signer, + paymentId: string, + purchaseId: string, + amount: BigNumberish, + currency: string, + shopId: string, + nonce: BigNumberish + ): Promise { + const message = ContractUtils.getLoyaltyPaymentMessage( + await signer.getAddress(), + paymentId, + purchaseId, + amount, + currency, + shopId, + nonce + ); + return signer.signMessage(message); + } + + public static getLoyaltyPaymentCancelMessage( + address: string, + paymentId: string, + purchaseId: string, + nonce: BigNumberish + ): Uint8Array { + const encodedResult = hre.ethers.utils.defaultAbiCoder.encode( + ["bytes32", "string", "address", "uint256"], + [paymentId, purchaseId, address, nonce] + ); + return arrayify(hre.ethers.utils.keccak256(encodedResult)); + } + + public static async signLoyaltyPaymentCancel( + signer: Signer, + paymentId: string, + purchaseId: string, + nonce: BigNumberish + ): Promise { + const message = ContractUtils.getLoyaltyPaymentCancelMessage( + await signer.getAddress(), + paymentId, + purchaseId, + nonce + ); + return signer.signMessage(message); + } + + public static getPaymentId(account: string): string { + const encodedResult = hre.ethers.utils.defaultAbiCoder.encode( + ["address", "bytes32"], + [account, crypto.randomBytes(32)] + ); + return hre.ethers.utils.keccak256(encodedResult); + } } diff --git a/packages/relay/test/Endpoints.test.ts b/packages/relay/test/Endpoints.test.ts index 0cc45f7a..97eddbdd 100644 --- a/packages/relay/test/Endpoints.test.ts +++ b/packages/relay/test/Endpoints.test.ts @@ -351,245 +351,6 @@ describe("Test of Server", function () { }); }); - context("Save Purchase Data", () => { - const purchase: IPurchaseData = { - purchaseId: "P000001", - timestamp: 1672844400, - amount: 10000, - method: 0, - currency: "krw", - shopIndex: 1, - userIndex: 0, - }; - - it("Save Purchase Data", async () => { - const phoneHash = ContractUtils.getPhoneHash(userData[purchase.userIndex].phone); - const userAccount = - userData[purchase.userIndex].address.trim() !== "" - ? userData[purchase.userIndex].address.trim() - : AddressZero; - const purchaseAmount = Amount.make(purchase.amount, 18).value; - const shop = shopData[purchase.shopIndex]; - const pointAmount = purchaseAmount.mul(shop.providePercent).div(100); - await expect( - ledgerContract.connect(validators[0]).savePurchase({ - purchaseId: purchase.purchaseId, - timestamp: purchase.timestamp, - amount: purchaseAmount, - currency: purchase.currency.toLowerCase(), - shopId: shop.shopId, - method: purchase.method, - account: userAccount, - phone: phoneHash, - }) - ) - .to.emit(ledgerContract, "SavedPurchase") - .withNamedArgs({ - purchaseId: purchase.purchaseId, - timestamp: purchase.timestamp, - amount: purchaseAmount, - currency: purchase.currency.toLowerCase(), - shopId: shop.shopId, - method: purchase.method, - account: userAccount, - phone: phoneHash, - }) - .emit(ledgerContract, "ProvidedPoint") - .withNamedArgs({ - account: userAccount, - providedPoint: pointAmount, - providedValue: pointAmount, - purchaseId: purchase.purchaseId, - shopId: shop.shopId, - }); - }); - }); - - context("payPoint & payToken", () => { - const purchase: IPurchaseData = { - purchaseId: "P000001", - timestamp: 100, - amount: 100, - method: 0, - currency: "krw", - shopIndex: 1, - userIndex: 0, - }; - - const amountDepositToken = BigNumber.from(amount.value.mul(2)); - const amountToken = BigNumber.from(amount.value); - const amountPoint = amountToken.mul(price).div(multiple); - const shop = shopData[purchase.shopIndex]; - - before("Deposit token", async () => { - await tokenContract.connect(users[0]).approve(ledgerContract.address, amountDepositToken); - await expect(ledgerContract.connect(users[0]).deposit(amountDepositToken)).to.emit( - ledgerContract, - "Deposited" - ); - }); - - it("Failure test of the path /v1/ledger/payPoint 'Insufficient balance'", async () => { - const over_purchaseAmount = Amount.make(90_000_000, 18).value; - const nonce = await ledgerContract.nonceOf(users[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( - users[purchase.userIndex], - purchase.purchaseId, - over_purchaseAmount, - purchase.currency, - shop.shopId, - nonce - ); - const uri = URI(serverURL).directory("/v1/ledger/payPoint"); - const url = uri.toString(); - const response = await client.post(url, { - purchaseId: purchase.purchaseId, - amount: over_purchaseAmount.toString(), - currency: purchase.currency, - shopId: shop.shopId, - account: users[purchase.userIndex].address, - signature, - }); - - assert.deepStrictEqual(response.data.code, 500); - assert.ok(response.data.error.message === "Insufficient balance"); - }); - - it("Failure test of the path /v1/ledger/payPoint 'Invalid signature'", async () => { - const purchaseAmount = Amount.make(purchase.amount, 18).value; - const nonce = await ledgerContract.nonceOf(users[0].address); - const signature = await ContractUtils.signPayment( - users[purchase.userIndex + 1], - purchase.purchaseId, - purchaseAmount, - purchase.currency, - shop.shopId, - nonce - ); - const uri = URI(serverURL).directory("/v1/ledger/payPoint"); - const url = uri.toString(); - const response = await client.post(url, { - purchaseId: purchase.purchaseId, - amount: purchaseAmount.toString(), - currency: purchase.currency, - shopId: shop.shopId, - account: users[purchase.userIndex].address, - signature, - }); - - assert.deepStrictEqual(response.data.code, 500); - assert.ok(response.data.error.message === "Signature is not valid."); - }); - - it("Success Test of the path /v1/ledger/payPoint", async () => { - const purchaseAmount = Amount.make(purchase.amount, 18).value; - const nonce = await ledgerContract.nonceOf(users[0].address); - const signature = await ContractUtils.signPayment( - users[purchase.userIndex], - purchase.purchaseId, - purchaseAmount, - purchase.currency, - shop.shopId, - nonce - ); - const uri = URI(serverURL).directory("/v1/ledger/payPoint"); - const url = uri.toString(); - const response = await client.post(url, { - purchaseId: purchase.purchaseId, - amount: purchaseAmount.toString(), - currency: purchase.currency, - shopId: shop.shopId, - account: users[purchase.userIndex].address, - signature, - }); - - console.log(response.data); - - assert.deepStrictEqual(response.data.code, 200); - assert.ok(response.data.data !== undefined); - assert.ok(response.data.data.txHash !== undefined); - }); - - it("Failure test of the path /v1/ledger/payToken 'Insufficient balance'", async () => { - const over_purchaseAmount = Amount.make(90_000_000, 18).value; - const nonce = await ledgerContract.nonceOf(users[0].address); - const signature = await ContractUtils.signPayment( - users[purchase.userIndex], - purchase.purchaseId, - over_purchaseAmount, - purchase.currency, - shop.shopId, - nonce - ); - const uri = URI(serverURL).directory("/v1/ledger/payToken"); - const url = uri.toString(); - const response = await client.post(url, { - purchaseId: purchase.purchaseId, - amount: over_purchaseAmount.toString(), - currency: purchase.currency, - shopId: shop.shopId, - account: users[purchase.userIndex].address, - signature, - }); - - assert.deepStrictEqual(response.data.code, 500); - assert.ok(response.data.error.message === "Insufficient balance"); - }); - - it("Failure test of the path /v1/ledger/payToken 'Invalid signature'", async () => { - const purchaseAmount = Amount.make(purchase.amount, 18).value; - const nonce = await ledgerContract.nonceOf(users[0].address); - const signature = await ContractUtils.signPayment( - users[purchase.userIndex + 1], - purchase.purchaseId, - purchaseAmount, - purchase.currency, - shop.shopId, - nonce - ); - const uri = URI(serverURL).directory("/v1/ledger/payToken"); - const url = uri.toString(); - const response = await client.post(url, { - purchaseId: purchase.purchaseId, - amount: purchaseAmount.toString(), - currency: purchase.currency, - shopId: shop.shopId, - account: users[purchase.userIndex].address, - signature, - }); - - assert.deepStrictEqual(response.data.code, 500); - assert.ok(response.data.error.message === "Signature is not valid."); - }); - - it("Success Test of the path /v1/ledger/payToken", async () => { - const purchaseAmount = Amount.make(purchase.amount, 18).value; - const nonce = await ledgerContract.nonceOf(users[0].address); - const signature = await ContractUtils.signPayment( - users[purchase.userIndex], - purchase.purchaseId, - purchaseAmount, - purchase.currency, - shop.shopId, - nonce - ); - const uri = URI(serverURL).directory("/v1/ledger/payToken"); - const url = uri.toString(); - const response = await client.post(url, { - purchaseId: purchase.purchaseId, - amount: purchaseAmount.toString(), - currency: purchase.currency, - shopId: shop.shopId, - account: users[purchase.userIndex].address, - signature, - }); - - assert.deepStrictEqual(response.data.code, 200); - assert.ok(response.data.data !== undefined); - assert.ok(response.data.data.txHash !== undefined); - }); - }); - context("Change loyalty type", () => { it("Check loyalty type - before", async () => { const userIndex = 0; diff --git a/packages/relay/test/ShopWithdraw.test.ts b/packages/relay/test/ShopWithdraw.test.ts index bdd8785e..e705442c 100644 --- a/packages/relay/test/ShopWithdraw.test.ts +++ b/packages/relay/test/ShopWithdraw.test.ts @@ -472,11 +472,13 @@ describe("Test for ShopCollection", () => { userIndex: 0, }; + const paymentId = ContractUtils.getPaymentId(userWallets[purchase.userIndex].address); const purchaseAmount = Amount.make(purchase.amount, 18).value; const shop = shopData[purchase.shopIndex]; const nonce = await ledgerContract.nonceOf(userWallets[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( + const signature = await ContractUtils.signLoyaltyPayment( userWallets[purchase.userIndex], + paymentId, purchase.purchaseId, purchaseAmount, purchase.currency, @@ -486,7 +488,8 @@ describe("Test for ShopCollection", () => { const amt = purchaseAmount.mul(shop.providePercent).div(100); await expect( - ledgerContract.connect(relay).payPoint({ + ledgerContract.connect(relay).createLoyaltyPayment({ + paymentId, purchaseId: purchase.purchaseId, amount: purchaseAmount, currency: purchase.currency.toLowerCase(), @@ -501,13 +504,15 @@ describe("Test for ShopCollection", () => { shopId: shop.shopId, providedPoint: Amount.make(200, 18).value, }) - .to.emit(ledgerContract, "PaidPoint") + .to.emit(ledgerContract, "CreatedLoyaltyPayment") .withNamedArgs({ + paymentId, + purchaseId: purchase.purchaseId, account: userWallets[purchase.userIndex].address, + shopId: shop.shopId, + loyaltyType: 0, paidPoint: purchaseAmount, paidValue: purchaseAmount, - purchaseId: purchase.purchaseId, - shopId: shop.shopId, }); const shopInfo = await shopCollection.shopOf(shop.shopId); expect(shopInfo.providedPoint).to.equal(Amount.make(100, 18).value); @@ -516,6 +521,36 @@ describe("Test for ShopCollection", () => { }); }); + context("Change loyalty type", () => { + it("Check loyalty type - before", async () => { + const userIndex = 0; + const loyaltyType = await ledgerContract.loyaltyTypeOf(userWallets[userIndex].address); + expect(loyaltyType).to.equal(0); + }); + + it("Send loyalty type", async () => { + const userIndex = 0; + const nonce = await ledgerContract.nonceOf(userWallets[userIndex].address); + const signature = await ContractUtils.signLoyaltyType(userWallets[userIndex], nonce); + const uri = URI(serverURL).directory("/v1/ledger/changeToLoyaltyToken"); + const url = uri.toString(); + const response = await client.post(url, { + account: userWallets[userIndex].address, + signature, + }); + + expect(response.data.code).to.equal(200); + expect(response.data.data).to.not.equal(undefined); + expect(response.data.data.txHash).to.match(/^0x[A-Fa-f0-9]{64}$/i); + }); + + it("Check point type - after", async () => { + const userIndex = 0; + const loyaltyType = await ledgerContract.loyaltyTypeOf(userWallets[userIndex].address); + expect(loyaltyType).to.equal(1); + }); + }); + context("Deposit token", () => { it("Deposit token - Success", async () => { const oldTokenBalance = await ledgerContract.tokenBalanceOf(userWallets[0].address); @@ -545,13 +580,15 @@ describe("Test for ShopCollection", () => { userIndex: 0, }; + const paymentId = ContractUtils.getPaymentId(userWallets[purchase.userIndex].address); const purchaseAmount = Amount.make(purchase.amount, 18).value; const tokenAmount = purchaseAmount.mul(multiple).div(price); const oldFoundationTokenBalance = await ledgerContract.tokenBalanceOf(foundation.address); const shop = shopData[purchase.shopIndex]; const nonce = await ledgerContract.nonceOf(userWallets[purchase.userIndex].address); - const signature = await ContractUtils.signPayment( + const signature = await ContractUtils.signLoyaltyPayment( userWallets[purchase.userIndex], + paymentId, purchase.purchaseId, purchaseAmount, purchase.currency, @@ -560,8 +597,9 @@ describe("Test for ShopCollection", () => { ); await expect( - ledgerContract.connect(relay).payToken({ + ledgerContract.connect(relay).createLoyaltyPayment({ purchaseId: purchase.purchaseId, + paymentId, amount: purchaseAmount, currency: purchase.currency.toLowerCase(), shopId: shop.shopId, @@ -569,13 +607,15 @@ describe("Test for ShopCollection", () => { signature, }) ) - .to.emit(ledgerContract, "PaidToken") + .to.emit(ledgerContract, "CreatedLoyaltyPayment") .withNamedArgs({ + paymentId, + purchaseId: purchase.purchaseId, account: userWallets[purchase.userIndex].address, + shopId: shop.shopId, + loyaltyType: 1, paidToken: tokenAmount, paidValue: purchaseAmount, - purchaseId: purchase.purchaseId, - shopId: shop.shopId, }); const shopInfo2 = await shopCollection.shopOf(shop.shopId); expect(shopInfo2.providedPoint).to.equal(Amount.make(100, 18).value);