Skip to content

Commit

Permalink
[Contract] Change to allow setPointType to be called by Relay
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelKim20 committed Oct 3, 2023
1 parent 8e247cb commit 2ffdbb0
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 9 deletions.
19 changes: 15 additions & 4 deletions packages/contracts/contracts/Ledger.sol
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ contract Ledger {
event Deposited(address account, uint256 depositAmount, uint256 value, uint256 balanceToken);
/// @notice 토큰을 인출했을 때 발생하는 이벤트
event Withdrawn(address account, uint256 withdrawAmount, uint256 value, uint256 balanceToken);
/// @notice 구매 후 적립되는 포인트의 종류 (0: 포인트, 1: 토큰)
event ChangedPointType(address account, PointType pointType);

/// @notice 생성자
/// @param _foundationAccount 재단의 계정
Expand Down Expand Up @@ -295,6 +297,8 @@ contract Ledger {
unPayablePointBalances[_phone] = 0;
pointBalances[_account] += amount;

nonce[_account]++;

emit ChangedToPayablePoint(_phone, _account, amount, value, pointBalances[_account]);
}

Expand Down Expand Up @@ -491,9 +495,16 @@ contract Ledger {
}

/// @notice 사용자가 적립할 포인트의 종류를 리턴한다
/// @param _type 0: 포인트, 1: 토큰
function setPointType(PointType _type) public {
require(PointType.POINT <= _type && _type <= PointType.TOKEN);
pointTypes[msg.sender] = _type;
/// @param _pointType 0: 포인트, 1: 토큰
/// @param _account 지갑주소
/// @param _signature 서명
function setPointType(PointType _pointType, address _account, bytes calldata _signature) public {
require(PointType.POINT <= _pointType && _pointType <= PointType.TOKEN);
bytes32 dataHash = keccak256(abi.encode(_pointType, _account, nonce[_account]));
require(ECDSA.recover(ECDSA.toEthSignedMessageHash(dataHash), _signature) == _account, "Invalid signature");

pointTypes[_account] = _pointType;
nonce[_account]++;
emit ChangedPointType(_account, _pointType);
}
}
7 changes: 5 additions & 2 deletions packages/contracts/deploy/bosagora_devnet/06_ledger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {

const { deployments, getNamedAccounts, ethers } = hre;
const { deploy } = deployments;
const { deployer, foundation, settlements } = await getNamedAccounts();
const { deployer, foundation, settlements, validator1 } = await getNamedAccounts();

const linkCollectionContractAddress = await getEmailLinkCollectionContractAddress("EmailLinkCollection", hre);
const tokenContractAddress = await getContractAddress("Token", hre);
Expand Down Expand Up @@ -80,7 +80,10 @@ const func: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {
await tx8.wait();

if (user.pointType === 1) {
const tx9 = await ledgerContract.connect(signer).setPointType(1);
const pointType = 1;
const nonce = await ledgerContract.nonceOf(user.address);
const signature = ContractUtils.signPointType(signer, pointType, nonce);
const tx9 = await ledgerContract.connect(validator1).setPointType(pointType, user.address, signature);
console.log(`Deposit user's amount (tx: ${tx9.hash})...`);
await tx9.wait();
}
Expand Down
13 changes: 13 additions & 0 deletions packages/contracts/src/utils/ContractUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,17 @@ export class ContractUtils {
const message = ContractUtils.getChangePayablePointMessage(phone, await signer.getAddress(), nonce);
return signer.signMessage(message);
}

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

public static async signPointType(signer: Signer, type: BigNumberish, nonce: BigNumberish): Promise<string> {
const message = ContractUtils.getPointTypeMessage(type, await signer.getAddress(), nonce);
return signer.signMessage(message);
}
}
20 changes: 18 additions & 2 deletions packages/contracts/test/03-Ledger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,15 @@ describe("Test for Ledger", () => {
});

it("Change point type (user: 3)", async () => {
await ledgerContract.connect(userWallets[3]).setPointType(1);
const userIndex = 3;
const pointType = 1;
const nonce = await ledgerContract.nonceOf(userWallets[userIndex].address);
const signature = ContractUtils.signPointType(userWallets[userIndex], pointType, nonce);
await expect(
ledgerContract.connect(relay).setPointType(pointType, userWallets[userIndex].address, signature)
)
.to.emit(ledgerContract, "ChangedPointType")
.withNamedArgs({ account: userWallets[userIndex].address, pointType });
});

it("Save Purchase Data - phone and address are registered (user: 3, point type : 1)", async () => {
Expand Down Expand Up @@ -760,7 +768,15 @@ describe("Test for Ledger", () => {
});

it("Change point type (user: 1)", async () => {
await ledgerContract.connect(userWallets[1]).setPointType(1);
const userIndex = 1;
const pointType = 1;
const nonce = await ledgerContract.nonceOf(userWallets[userIndex].address);
const signature = ContractUtils.signPointType(userWallets[userIndex], pointType, nonce);
await expect(
ledgerContract.connect(relay).setPointType(pointType, userWallets[userIndex].address, signature)
)
.to.emit(ledgerContract, "ChangedPointType")
.withNamedArgs({ account: userWallets[userIndex].address, pointType });
});

it("Save Purchase Data - (user: 1, point type : 1)", async () => {
Expand Down
32 changes: 31 additions & 1 deletion packages/subgraph/generated/Ledger/Ledger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,28 @@ import {
BigInt
} from "@graphprotocol/graph-ts";

export class ChangedPointType extends ethereum.Event {
get params(): ChangedPointType__Params {
return new ChangedPointType__Params(this);
}
}

export class ChangedPointType__Params {
_event: ChangedPointType;

constructor(event: ChangedPointType) {
this._event = event;
}

get account(): Address {
return this._event.parameters[0].value.toAddress();
}

get pointType(): i32 {
return this._event.parameters[1].value.toI32();
}
}

export class ChangedToPayablePoint extends ethereum.Event {
get params(): ChangedToPayablePoint__Params {
return new ChangedToPayablePoint__Params(this);
Expand Down Expand Up @@ -1137,9 +1159,17 @@ export class SetPointTypeCall__Inputs {
this._call = call;
}

get _type(): i32 {
get _pointType(): i32 {
return this._call.inputValues[0].value.toI32();
}

get _account(): Address {
return this._call.inputValues[1].value.toAddress();
}

get _signature(): Bytes {
return this._call.inputValues[2].value.toBytes();
}
}

export class SetPointTypeCall__Outputs {
Expand Down
109 changes: 109 additions & 0 deletions packages/subgraph/generated/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,115 @@ import {
BigDecimal
} from "@graphprotocol/graph-ts";

export class ChangedPointType extends Entity {
constructor(id: Bytes) {
super();
this.set("id", Value.fromBytes(id));
}

save(): void {
let id = this.get("id");
assert(id != null, "Cannot save ChangedPointType entity without an ID");
if (id) {
assert(
id.kind == ValueKind.BYTES,
`Entities of type ChangedPointType must have an ID of type Bytes but the id '${id.displayData()}' is of type ${id.displayKind()}`
);
store.set("ChangedPointType", id.toBytes().toHexString(), this);
}
}

static loadInBlock(id: Bytes): ChangedPointType | null {
return changetype<ChangedPointType | null>(
store.get_in_block("ChangedPointType", id.toHexString())
);
}

static load(id: Bytes): ChangedPointType | null {
return changetype<ChangedPointType | null>(
store.get("ChangedPointType", id.toHexString())
);
}

get id(): Bytes {
let value = this.get("id");
if (!value || value.kind == ValueKind.NULL) {
throw new Error("Cannot return null for a required field.");
} else {
return value.toBytes();
}
}

set id(value: Bytes) {
this.set("id", Value.fromBytes(value));
}

get account(): Bytes {
let value = this.get("account");
if (!value || value.kind == ValueKind.NULL) {
throw new Error("Cannot return null for a required field.");
} else {
return value.toBytes();
}
}

set account(value: Bytes) {
this.set("account", Value.fromBytes(value));
}

get pointType(): i32 {
let value = this.get("pointType");
if (!value || value.kind == ValueKind.NULL) {
return 0;
} else {
return value.toI32();
}
}

set pointType(value: i32) {
this.set("pointType", Value.fromI32(value));
}

get blockNumber(): BigInt {
let value = this.get("blockNumber");
if (!value || value.kind == ValueKind.NULL) {
throw new Error("Cannot return null for a required field.");
} else {
return value.toBigInt();
}
}

set blockNumber(value: BigInt) {
this.set("blockNumber", Value.fromBigInt(value));
}

get blockTimestamp(): BigInt {
let value = this.get("blockTimestamp");
if (!value || value.kind == ValueKind.NULL) {
throw new Error("Cannot return null for a required field.");
} else {
return value.toBigInt();
}
}

set blockTimestamp(value: BigInt) {
this.set("blockTimestamp", Value.fromBigInt(value));
}

get transactionHash(): Bytes {
let value = this.get("transactionHash");
if (!value || value.kind == ValueKind.NULL) {
throw new Error("Cannot return null for a required field.");
} else {
return value.toBytes();
}
}

set transactionHash(value: Bytes) {
this.set("transactionHash", Value.fromBytes(value));
}
}

export class ChangedToPayablePoint extends Entity {
constructor(id: Bytes) {
super();
Expand Down
3 changes: 3 additions & 0 deletions packages/subgraph/manifest/subgraph.placeholder.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dataSources:
apiVersion: 0.0.7
language: wasm/assemblyscript
entities:
- ChangedPointType
- ChangedToPayablePoint
- Deposited
- PaidPoint
Expand All @@ -30,6 +31,8 @@ dataSources:
- name: Ledger
file: $DMS_OSX_MODULE/artifacts/contracts/Ledger.sol/Ledger.json
eventHandlers:
- event: ChangedPointType(address,uint8)
handler: handleChangedPointType
- event: ChangedToPayablePoint(bytes32,address,uint256,uint256,uint256)
handler: handleChangedToPayablePoint
- event: Deposited(address,uint256,uint256,uint256)
Expand Down
9 changes: 9 additions & 0 deletions packages/subgraph/schema.graphql
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
type ChangedPointType @entity(immutable: true) {
id: Bytes!
account: Bytes! # address
pointType: Int! # uint256
blockNumber: BigInt!
blockTimestamp: BigInt!
transactionHash: Bytes!
}

type ChangedToPayablePoint @entity(immutable: true) {
id: Bytes!
phone: Bytes! # bytes32
Expand Down
14 changes: 14 additions & 0 deletions packages/subgraph/src/ledger.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
ChangedPointType as ChangedPointTypeEvent,
ChangedToPayablePoint as ChangedToPayablePointEvent,
Deposited as DepositedEvent,
PaidPoint as PaidPointEvent,
Expand All @@ -11,6 +12,7 @@ import {
Withdrawn as WithdrawnEvent,
} from "../generated/Ledger/Ledger";
import {
ChangedPointType,
ChangedToPayablePoint,
Deposited,
PaidPoint,
Expand All @@ -28,6 +30,18 @@ import {
import { BigInt, Bytes } from "@graphprotocol/graph-ts";
import { AmountUnit, NullAccount } from "./utils";

export function handleChangedPointType(event: ChangedPointTypeEvent): void {
let entity = new ChangedPointType(event.transaction.hash.concatI32(event.logIndex.toI32()));
entity.account = event.params.account;
entity.pointType = event.params.pointType;

entity.blockNumber = event.block.number;
entity.blockTimestamp = event.block.timestamp;
entity.transactionHash = event.transaction.hash;

entity.save();
}

export function handleChangedToPayablePoint(event: ChangedToPayablePointEvent): void {
let entity = new ChangedToPayablePoint(event.transaction.hash.concatI32(event.logIndex.toI32()));
entity.phone = event.params.phone;
Expand Down

0 comments on commit 2ffdbb0

Please sign in to comment.