Skip to content

Commit

Permalink
Change the ability to change the type of loyalty
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelKim20 committed Oct 26, 2023
1 parent e6147ef commit 1223324
Show file tree
Hide file tree
Showing 16 changed files with 280 additions and 214 deletions.
2 changes: 1 addition & 1 deletion packages/contracts-lib/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dms-osx-lib",
"version": "1.1.6",
"version": "1.1.7",
"description": "",
"main": "dist/bundle-cjs.js",
"module": "dist/bundle-esm.js",
Expand Down
49 changes: 29 additions & 20 deletions packages/contracts/contracts/Ledger.sol
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ contract Ledger {
event Deposited(address account, uint256 depositedToken, uint256 depositedValue, uint256 balanceToken);
/// @notice 토큰을 인출했을 때 발생하는 이벤트
event Withdrawn(address account, uint256 withdrawnToken, uint256 withdrawnValue, uint256 balanceToken);
/// @notice 구매 후 적립되는 포인트의 종류 (0: 포인트, 1: 토큰)
event ChangedLoyaltyType(address account, LoyaltyType loyaltyType);
/// @notice 구매 후 적립되는 로열티를 토큰으로 변경했을 때 발생하는 이벤트
event ChangedToLoyaltyToken(address account, uint256 amountToken, uint256 amountPoint, uint256 balanceToken);

/// @notice 생성자
/// @param _foundationAccount 재단의 계정
Expand Down Expand Up @@ -593,33 +593,42 @@ contract Ledger {
return loyaltyTypes[_account];
}

/// @notice 사용자가 적립할 포인트의 종류를 리턴한다
/// @param _type 0: 포인트, 1: 토큰
/// @notice 사용자가 적립할 로열티를 토큰으로 변경한다.
/// @param _account 지갑주소
/// @param _signature 서명
/// @dev 중계서버를 통해서 호출됩니다.
function setLoyaltyType(LoyaltyType _type, address _account, bytes calldata _signature) public {
bytes32 dataHash = keccak256(abi.encode(_type, _account, nonce[_account]));
function changeToLoyaltyToken(address _account, bytes calldata _signature) public {
bytes32 dataHash = keccak256(abi.encode(_account, nonce[_account]));
require(ECDSA.recover(ECDSA.toEthSignedMessageHash(dataHash), _signature) == _account, "Invalid signature");

_setLoyaltyType(_type, _account);
_changeToLoyaltyToken(_account);
}

/// @notice 사용자가 적립할 포인트의 종류를 리턴한다
/// @param _type 0: 포인트, 1: 토큰
/// @dev 사용자에 의해 직접 호출됩니다.
function setLoyaltyTypeDirect(LoyaltyType _type) public {
_setLoyaltyType(_type, msg.sender);
}

function _setLoyaltyType(LoyaltyType _type, address _account) internal {
require(LoyaltyType.POINT <= _type && _type <= LoyaltyType.TOKEN, "Invalid value");

loyaltyTypes[_account] = _type;

nonce[_account]++;

emit ChangedLoyaltyType(_account, _type);
function changeToLoyaltyTokenDirect() public {
_changeToLoyaltyToken(msg.sender);
}

function _changeToLoyaltyToken(address _account) internal {
if (loyaltyTypes[_account] != LoyaltyType.TOKEN) {
loyaltyTypes[_account] = LoyaltyType.TOKEN;
uint256 amountPoint;
uint256 amountToken;
if (pointBalances[_account] > 0) {
amountPoint = pointBalances[_account];
amountToken = convertPointToToken(amountPoint);
require(tokenBalances[foundationAccount] >= amountToken, "Insufficient foundation balance");
tokenBalances[_account] += amountToken;
tokenBalances[foundationAccount] -= amountToken;
pointBalances[_account] = 0;
} else {
amountPoint = 0;
amountToken = 0;
}
nonce[_account]++;
emit ChangedToLoyaltyToken(_account, amountToken, amountPoint, tokenBalances[_account]);
}
}

/// @notice 포인트와 토큰의 사용수수료률을 설정합니다. 5%를 초과한 값은 설정할 수 없습니다.
Expand Down
9 changes: 4 additions & 5 deletions packages/contracts/deploy/bosagora_devnet/06_ledger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,16 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
await tx9.wait();

if (user.loyaltyType === 1) {
const loyaltyType = 1;
const nonce = await ledgerContract.nonceOf(user.address);
const signature = ContractUtils.signLoyaltyType(signer, loyaltyType, nonce);
const tx10 = await ledgerContract.connect(signer).setLoyaltyType(loyaltyType, user.address, signature);
const signature = ContractUtils.signLoyaltyType(signer, nonce);
const tx10 = await ledgerContract.connect(signer).changeToLoyaltyToken(user.address, signature);
console.log(`Deposit user's amount (tx: ${tx10.hash})...`);
await tx10.wait();

if ((await ledgerContract.connect(signer).loyaltyTypeOf(user.address)) === 1) {
console.log(`Success setLoyaltyType...`);
console.log(`Success changeToLoyaltyToken...`);
} else {
console.error(`Fail setLoyaltyType...`);
console.error(`Fail changeToLoyaltyToken...`);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dms-osx-artifacts",
"version": "1.1.6",
"version": "1.1.7",
"description": "Smart contracts that decentralized point systems",
"files": [
"**/*.sol"
Expand Down
11 changes: 4 additions & 7 deletions packages/contracts/src/utils/ContractUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,13 @@ export class ContractUtils {
return signer.signMessage(message);
}

public static getLoyaltyTypeMessage(type: BigNumberish, address: string, nonce: BigNumberish): Uint8Array {
const encodedResult = hre.ethers.utils.defaultAbiCoder.encode(
["uint256", "address", "uint256"],
[type, address, nonce]
);
public static getLoyaltyTypeMessage(address: string, nonce: BigNumberish): Uint8Array {
const encodedResult = hre.ethers.utils.defaultAbiCoder.encode(["address", "uint256"], [address, nonce]);
return arrayify(hre.ethers.utils.keccak256(encodedResult));
}

public static async signLoyaltyType(signer: Signer, type: BigNumberish, nonce: BigNumberish): Promise<string> {
const message = ContractUtils.getLoyaltyTypeMessage(type, await signer.getAddress(), nonce);
public static async signLoyaltyType(signer: Signer, nonce: BigNumberish): Promise<string> {
const message = ContractUtils.getLoyaltyTypeMessage(await signer.getAddress(), nonce);
return signer.signMessage(message);
}

Expand Down
27 changes: 10 additions & 17 deletions packages/contracts/test/03-Ledger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -654,16 +654,13 @@ describe("Test for Ledger", () => {

it("Change point type (user: 3, point type : 0)", async () => {
const userIndex = 3;
const loyaltyType = 1;
const nonce = await ledgerContract.nonceOf(userWallets[userIndex].address);
const signature = ContractUtils.signLoyaltyType(userWallets[userIndex], loyaltyType, nonce);
await expect(
ledgerContract
.connect(userWallets[userIndex].connect(hre.waffle.provider))
.setLoyaltyTypeDirect(loyaltyType)
.changeToLoyaltyTokenDirect()
)
.to.emit(ledgerContract, "ChangedLoyaltyType")
.withNamedArgs({ account: userWallets[userIndex].address, loyaltyType });
.to.emit(ledgerContract, "ChangedToLoyaltyToken")
.withNamedArgs({ account: userWallets[userIndex].address });
});

it("Save Purchase Data - phone and address are registered (user: 3, point type : 1)", async () => {
Expand Down Expand Up @@ -800,14 +797,13 @@ describe("Test for Ledger", () => {

it("Change point type (user: 1)", async () => {
const userIndex = 1;
const loyaltyType = 1;
const nonce = await ledgerContract.nonceOf(userWallets[userIndex].address);
const signature = ContractUtils.signLoyaltyType(userWallets[userIndex], loyaltyType, nonce);
const signature = ContractUtils.signLoyaltyType(userWallets[userIndex], nonce);
await expect(
ledgerContract.connect(relay).setLoyaltyType(loyaltyType, userWallets[userIndex].address, signature)
ledgerContract.connect(relay).changeToLoyaltyToken(userWallets[userIndex].address, signature)
)
.to.emit(ledgerContract, "ChangedLoyaltyType")
.withNamedArgs({ account: userWallets[userIndex].address, loyaltyType });
.to.emit(ledgerContract, "ChangedToLoyaltyToken")
.withNamedArgs({ account: userWallets[userIndex].address });
});

it("Save Purchase Data - (user: 1, point type : 1)", async () => {
Expand Down Expand Up @@ -1037,16 +1033,13 @@ describe("Test for Ledger", () => {

it("Change point type (user: 4, point type : 0)", async () => {
const userIndex = 4;
const loyaltyType = 1;
const nonce = await ledgerContract.nonceOf(userWallets[userIndex].address);
const signature = ContractUtils.signLoyaltyType(userWallets[userIndex], loyaltyType, nonce);
await expect(
ledgerContract
.connect(userWallets[userIndex].connect(hre.waffle.provider))
.setLoyaltyTypeDirect(loyaltyType)
.changeToLoyaltyTokenDirect()
)
.to.emit(ledgerContract, "ChangedLoyaltyType")
.withNamedArgs({ account: userWallets[userIndex].address, loyaltyType });
.to.emit(ledgerContract, "ChangedToLoyaltyToken")
.withNamedArgs({ account: userWallets[userIndex].address });
});

it("Save Purchase Data - phone and address are registered (user: 4, point type : 1)", async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/faker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"assert": "^2.0.0",
"axios": "^0.26.0",
"chai": "^4.3.7",
"dms-osx-artifacts": "^1.1.6",
"dms-osx-artifacts": "^1.1.7",
"dotenv": "^10.0.0",
"ethereum-waffle": "^4.0.10",
"ethers": "^5.7.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/relay/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"chai": "^4.3.7",
"chai-http": "^4.3.7",
"cors": "^2.8.5",
"dms-osx-artifacts": "^1.1.6",
"dms-osx-artifacts": "^1.1.7",
"dotenv": "^10.0.0",
"ethereum-waffle": "^4.0.10",
"ethers": "^5.7.0",
Expand Down
22 changes: 10 additions & 12 deletions packages/relay/src/routers/DefaultRouter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Ledger, ShopCollection, PhoneLinkCollection, Token } from "../../typechain-types";
import { Ledger, PhoneLinkCollection, ShopCollection, Token } from "../../typechain-types";
import { Config } from "../common/Config";
import { logger } from "../common/Logger";
import { GasPriceManager } from "../contract/GasPriceManager";
Expand Down Expand Up @@ -230,15 +230,14 @@ export class DefaultRouter {

// 포인트의 종류를 선택하는 기능
this.app.post(
"/changeLoyaltyType",
"/changeToLoyaltyToken",
[
body("type").exists().isIn(["0", "1"]),
body("account").exists().isEthereumAddress(),
body("signature")
.exists()
.matches(/^(0x)[0-9a-f]{130}$/i),
],
this.changeLoyaltyType.bind(this)
this.changeToLoyaltyToken.bind(this)
);

// 사용가능한 포인트로 전환
Expand Down Expand Up @@ -451,11 +450,11 @@ export class DefaultRouter {

/**
* 포인트의 종류를 선택한다.
* POST /changeLoyaltyType
* POST /changeToLoyaltyToken
* @private
*/
private async changeLoyaltyType(req: express.Request, res: express.Response) {
logger.http(`POST /changeLoyaltyType`);
private async changeToLoyaltyToken(req: express.Request, res: express.Response) {
logger.http(`POST /changeToLoyaltyToken`);

const errors = validationResult(req);
if (!errors.isEmpty()) {
Expand All @@ -469,13 +468,12 @@ export class DefaultRouter {

const signerItem = await this.getRelaySigner();
try {
const type: number = Number(req.body.type);
const account: string = String(req.body.account); // 구매자의 주소
const signature: string = String(req.body.signature); // 서명

// 서명검증
const userNonce = await (await this.getLedgerContract()).nonceOf(account);
if (!ContractUtils.verifyLoyaltyType(type, account, userNonce, signature))
if (!ContractUtils.verifyLoyaltyType(account, userNonce, signature))
return res.status(200).json(
this.makeResponseData(500, undefined, {
message: "Signature is not valid.",
Expand All @@ -484,14 +482,14 @@ export class DefaultRouter {

const tx = await (await this.getLedgerContract())
.connect(signerItem.signer)
.setLoyaltyType(type, account, signature);
.changeToLoyaltyToken(account, signature);

logger.http(`TxHash(changeLoyaltyType): `, tx.hash);
logger.http(`TxHash(changeToLoyaltyToken): `, 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 change point type";
logger.error(`POST /changeLoyaltyType :`, message);
logger.error(`POST /changeToLoyaltyToken :`, message);
return res.status(200).json(
this.makeResponseData(500, undefined, {
message,
Expand Down
20 changes: 6 additions & 14 deletions packages/relay/src/utils/ContractUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,26 +162,18 @@ export class ContractUtils {
return res.toLowerCase() === account.toLowerCase();
}

public static getLoyaltyTypeMessage(type: BigNumberish, account: string, nonce: BigNumberish): Uint8Array {
const encodedResult = hre.ethers.utils.defaultAbiCoder.encode(
["uint256", "address", "uint256"],
[type, account, nonce]
);
public static getLoyaltyTypeMessage(account: string, nonce: BigNumberish): Uint8Array {
const encodedResult = hre.ethers.utils.defaultAbiCoder.encode(["address", "uint256"], [account, nonce]);
return arrayify(hre.ethers.utils.keccak256(encodedResult));
}

public static async signLoyaltyType(signer: Signer, type: BigNumberish, nonce: BigNumberish): Promise<string> {
const message = ContractUtils.getLoyaltyTypeMessage(type, await signer.getAddress(), nonce);
public static async signLoyaltyType(signer: Signer, nonce: BigNumberish): Promise<string> {
const message = ContractUtils.getLoyaltyTypeMessage(await signer.getAddress(), nonce);
return signer.signMessage(message);
}

public static verifyLoyaltyType(
type: BigNumberish,
account: string,
nonce: BigNumberish,
signature: string
): boolean {
const message = ContractUtils.getLoyaltyTypeMessage(type, account, nonce);
public static verifyLoyaltyType(account: string, nonce: BigNumberish, signature: string): boolean {
const message = ContractUtils.getLoyaltyTypeMessage(account, nonce);
let res: string;
try {
res = ethers.utils.verifyMessage(message, signature);
Expand Down
62 changes: 30 additions & 32 deletions packages/relay/test/Endpoints.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,38 +403,6 @@ describe("Test of Server", function () {
});
});

context("Change loyalty type", () => {
it("Check loyalty type - before", async () => {
const userIndex = 0;
const loyaltyType = await ledgerContract.loyaltyTypeOf(users[userIndex].address);
expect(loyaltyType).to.equal(0);
});

it("Send loyalty type", async () => {
const loyaltyType = 1;
const userIndex = 0;
const nonce = await ledgerContract.nonceOf(users[userIndex].address);
const signature = await ContractUtils.signLoyaltyType(users[userIndex], loyaltyType, nonce);
const uri = URI(serverURL).directory("changeLoyaltyType");
const url = uri.toString();
const response = await client.post(url, {
type: loyaltyType,
account: users[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(users[userIndex].address);
expect(loyaltyType).to.equal(1);
});
});

context("payPoint & payToken", () => {
const purchase: IPurchaseData = {
purchaseId: "P000001",
Expand Down Expand Up @@ -619,6 +587,36 @@ describe("Test of Server", function () {
assert.ok(response.data.data.txHash !== undefined);
});
});

context("Change loyalty type", () => {
it("Check loyalty type - before", async () => {
const userIndex = 0;
const loyaltyType = await ledgerContract.loyaltyTypeOf(users[userIndex].address);
expect(loyaltyType).to.equal(0);
});

it("Send loyalty type", async () => {
const userIndex = 0;
const nonce = await ledgerContract.nonceOf(users[userIndex].address);
const signature = await ContractUtils.signLoyaltyType(users[userIndex], nonce);
const uri = URI(serverURL).directory("changeToLoyaltyToken");
const url = uri.toString();
const response = await client.post(url, {
account: users[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(users[userIndex].address);
expect(loyaltyType).to.equal(1);
});
});
});

context("Test token & point relay endpoints - using phone", () => {
Expand Down
Loading

0 comments on commit 1223324

Please sign in to comment.