์ปคํ”ผ ์›๊ฒฉ ์ฃผ๋ฌธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜

๊ฐœ์š”

๊ธฐ์ˆ ์ด ๋ฐœ์ „ํ•˜๋ฉด์„œ ์ด์ œ๋Š” ์นดํŽ˜์— ๊ฐ€์ง€ ์•Š์•„๋„ ์ง‘์—์„œ ์ปคํ”ผ๋ฅผ ์ฃผ๋ฌธํ•˜๋Š” ์‹œ๋Œ€๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์Šค๋งˆํŠธํฐ๊ณผ ์›น ๊ธฐ๋ฐ˜์˜ ์ด๋Ÿฌํ•œ ์ฃผ๋ฌธ ๋ฐฉ์‹์€ ์ฃผ๋ฌธ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œ๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์šด์šฉ์ด ํ•„์š”ํ•˜๋ฉฐ ์ด๋ฅผ ๊ตฌ์ถ•, ์šด์šฉํ•˜๋Š” ๋ฐ์—๋Š” ์ƒ๋‹นํ•œ ๊ฐœ๋ฐœ ์ธ๋ ฅ๊ณผ ๋น„์šฉ์ด ๋“ญ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋Ÿฌํ•œ ์˜จ๋ผ์ธ ์ปคํ”ผ ์ฃผ๋ฌธ ์‹œ์Šคํ…œ์€ ๊ทธ ํŽธ๋ฆฌํ•จ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ถฉ๋ถ„ํ•œ ์ž๋ณธ๊ณผ ์ธ๋ ฅ์„ ๊ฐ–์ถ˜ ์ผ๋ถ€ ๋Œ€ํ˜• ์ปคํ”ผ ์ „๋ฌธ์ ๋งŒ์ด ์šด์˜ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋งŒ์•ฝ, ๊ณ ์„ฑ๋Šฅ ํผ๋ธ”๋ฆญ ๋ธ”๋ก์ฒด์ธ์œผ๋กœ ์ด๋Ÿฌํ•œ ์ฃผ๋ฌธ ์‹œ์Šคํ…œ์„ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ƒํ™ฉ์€ ์–ด๋–ป๊ฒŒ ๋‹ฌ๋ผ์งˆ๊นŒ์š”? ์˜์„ธํ•œ ์นดํŽ˜๋ฅผ ํฌํ•จํ•ด ๋ˆ„๊ตฌ๋‚˜ ์˜จ๋ผ์ธ ์ปคํ”ผ ์ฃผ๋ฌธ ์‹œ์Šคํ…œ์„ ์†Œ๋น„์ž์—๊ฒŒ ์„œ๋น„์Šคํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์—์„œ๋Š”, Kaia ๋ธ”๋ก์ฒด์ธ๊ณผ KAS๋ฅผ ํ™œ์šฉํ•ด ์ปคํ”ผ ์ฃผ๋ฌธ์„ ์˜จ๋ผ์ธ์œผ๋กœ ๋ฐ›๊ณ  ๋˜ ์˜จ๋ผ์ธ ์ฃผ๋ฌธ ๊ณ ๊ฐ์—๊ฒŒ ์ธ์„ผํ‹ฐ๋ธŒ๋กœ KAIA๋ฅผ ์ œ๊ณตํ•  ์ˆ˜๋„ ์žˆ๋Š” ์ฃผ๋ฌธ ์‹œ์Šคํ…œ์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค. ์ฃผ๋ฌธ ๋‚ด์—ญ์€ Kaia ๋ธ”๋ก์ฒด์ธ์— ์ €์žฅ๋˜๋ฉฐ ์ฃผ๋ฌธ์— ๋Œ€ํ•œ ๋ณด์ƒ์œผ๋กœ KAIA, NFT, NT ๋“ฑ์„ ์ž๋™์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์นดํŽ˜ ์›๊ฒฉ ์ฃผ๋ฌธ ์‹œ์Šคํ…œ์€ ์•„๋ž˜์™€ ๊ฐ™์ด 3๊ฐ€์ง€ Application์œผ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค.

  1. customer-front: ์นดํŽ˜ ์ด์šฉ์ž๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋ฐ”์ผ ์›น์•ฑ์ด๋ฉฐ ์ปคํ”ผ๋ฅผ ์ฃผ๋ฌธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. store-front: ์นดํŽ˜ ์ฃผ์ธ๋“ค์ด ์‚ฌ์šฉํ•˜๋ฉด ๋ชจ๋ฐ”์ผ ์›น์•ฑ์ด๋ฉฐ ๊ฐ€๊ฒŒ ์œ„์น˜ ๋“ฑ๋ก, ๋ฉ”๋‰ด ๋“ฑ๋ก, ๋ณด์ƒ ์ •์ฑ… ๋“ฑ๋ก ๋“ฑ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. service-backend: ์นดํŽ˜ ์›๊ฒฉ ์ฃผ๋ฌธ ํ”Œ๋žซํผ์˜ ์„œ๋น„์Šค ์ฃผ์ฒด๊ฐ€ ์šด์˜ํ•˜๋Š” ์„œ๋ฒ„์ด๋ฉฐ customer-front์™€ store-front ์‚ฌ์šฉ์ž๋“ค์˜ ์š”์ฒญ์„ ์ „๋‹ฌ๋ฐ›์•„ ๋ธ”๋ก์ฒด์ธ์œผ๋กœ ์ค‘๊ฐœํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. KAS API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฃผ์ฒด์ž…๋‹ˆ๋‹ค.
warning

์ด ํŽ˜์ด์ง€์˜ ๋ชจ๋“  ๋‚ด์šฉ์€ KAS๋กœ ํƒˆ์ค‘์•™ํ™” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์„ ๋•๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.
์†Œ์Šค์ฝ”๋“œ๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  ๋‚ด์šฉ์€ ์‚ฌ์šฉ์ž ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์ ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
์†Œ์Šค์ฝ”๋“œ๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  ๋‚ด์šฉ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๋ชจ๋“  ์ฑ…์ž„์€ ์ „์ ์œผ๋กœ ์‚ฌ์šฉ์ž ๋ณธ์ธ์—๊ฒŒ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์„œ ํ˜น์€ KAS์— ๊ด€ํ•œ ๋ฌธ์˜๋Š” ๊ฐœ๋ฐœ์ž ํฌ๋Ÿผ์„ ๋ฐฉ๋ฌธํ•ด ๋„์›€ ๋ฐ›์œผ์‹ญ์‹œ์˜ค.

์‹œ์ž‘ํ•˜๊ธฐ

์นดํŽ˜ ์›๊ฒฉ ์ฃผ๋ฌธ ์‹œ์Šคํ…œ์„ ๊ตฌ์„ฑํ•˜๋Š” 3๊ฐ€์ง€ Application์˜ ์†Œ์Šค์ฝ”๋“œ๋Š” ํ•˜๋‚˜์˜ github repository์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์œผ์‹ญ์‹œ์˜ค.

git clone https://github.com/ground-x/kas-bapp-klaybucks.git

๋นŒ๋“œ ๋ฐ ์‹คํ–‰

kas-bapp-klaybucks ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ๊ฐ๊ฐ์˜ Application์„ ๋นŒ๋“œํ•˜๊ณ  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

customer-front

cd customer
npm install
npm start

store-front

cd store
npm install
npm start

service-backend

service-backend๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋จผ์ € ์„ค์ • ํŒŒ์ผ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. backend/config.template.json ํŒŒ์ผ์„ ๋ณต์‚ฌํ•˜์—ฌ backend/config.json ํŒŒ์ผ์„ ์ƒ์„ฑํ•œ ํ›„ ์ ์ ˆํ•œ ์„ค์ •๊ฐ’์„ ์ž…๋ ฅํ•˜์‹ญ์‹œ์˜ค.


  • authorization : Basic ์ธ์ฆ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋Š” KAS API ์ธ์ฆ ํ‚ค ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์‹ญ์‹œ์˜ค.
  • kas_account : KAS Wallet API๋ฅผ ์‚ฌ์šฉํ•ด ์ƒ์„ฑํ•œ ๊ณ„์ • ์ฃผ์†Œ ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์‹ญ์‹œ์˜ค.
  • master_contract : (Optional) ์ž์‹ ๋งŒ์˜ Klaybucks Master Contract๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด Master Contract์˜ ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์‹ญ์‹œ์˜ค. Master Contract๋ฅผ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” backend/storeList.sol ํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์„ค์ • ํŒŒ์ผ์„ ์ž‘์„ฑํ•œ ํ›„์—๋Š” ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด service-backend๋ฅผ ์‹คํ–‰ํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

cd backend
go run .

Klaybucks ๋ฐ๋ชจ

Klaybucks Customer-Front ํ™”๋ฉด


์ด ๋ฌธ์„œ ํ˜น์€ KAS์— ๊ด€ํ•œ ๋ฌธ์˜๋Š” ๊ฐœ๋ฐœ์ž ํฌ๋Ÿผ์„ ๋ฐฉ๋ฌธํ•ด ๋„์›€ ๋ฐ›์œผ์‹ญ์‹œ์˜ค.

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์›Œํฌํ”Œ๋กœ์šฐ

์ฃผ์š” ๊ธฐ๋Šฅ

"Klaybucks"๋Š” ๋“ฑ๋ก๋œ ์นดํŽ˜์˜ ์œ„์น˜, ๋ฉ”๋‰ด, ๊ฑฐ๋ž˜๋‚ด์—ญ ๋“ฑ์„ ๋ธ”๋ก์ฒด์ธ์ธ Kaia(๊ตฌ Klaytn)์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์„œ๋น„์Šค๊ฐ€ ์ค‘๋‹จ๋˜๋”๋ผ๋„ ์นดํŽ˜ ์ฃผ์ธ์€ ์›๊ฒฉ ์„œ๋น„์Šค์— ์‚ฌ์šฉํ•œ ํ•„์ˆ˜ ๋ฐ์ดํ„ฐ๋“ค์„ ์ง€์†์ ์œผ๋กœ ์กฐํšŒํ•˜๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ Kaia๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฃผ์š” ๊ธฐ๋Šฅ๋“ค์ž…๋‹ˆ๋‹ค.

  1. ์นดํŽ˜ ์ฃผ์ธ์€ ์นดํŽ˜์˜ ์œ„์น˜, ๋ฉ”๋‰ด ์ •๋ณด ๊ทธ๋ฆฌ๊ณ  ๋ณด์ƒ ์ •์ฑ…์„ Contract์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.
  2. ๊ณ ๊ฐ์˜ ๊ณ„์ •์€ KAS Wallet API๋ฅผ ์ด์šฉํ•˜์—ฌ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  3. ๊ณ ๊ฐ์€ Contract์—์„œ Klaybucks ์„œ๋น„์Šค์— ๋“ฑ๋ก๋œ ์ฃผ๋ณ€ ์นดํŽ˜๋“ค์˜ ์œ„์น˜๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  4. ๊ณ ๊ฐ์€ ์ฃผ๋ฌธํ•  ์นดํŽ˜๋ฅผ ์„ ํƒํ•˜๊ณ  Contract์—์„œ ๋ฉ”๋‰ด์™€ ๋ณด์ƒ ์ •์ฑ…์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
  5. ๊ณ ๊ฐ์€ KakaoPay ๋“ฑ ํƒ€ ๊ฒฐ์ œ ์‹œ์Šคํ‹ˆ์„ ํ†ตํ•ด ๊ฒฐ์ œ๋ฅผ ์™„๋ฃŒํ•˜๊ณ  ๊ทธ ๋‚ด์—ญ์„ Contract์— ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.
  6. ์นดํŽ˜ ์ฃผ์ธ์€ ์ž์‹ ์˜ Contract์— ๋“ฑ๋ก๋œ ๊ฒฐ์ œ ๋‚ด์—ญ์„ ํ™•์ธํ•˜๊ณ  ์ฃผ๋ฌธ์„ ์Šน์ธ/๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.

์œ„์˜ ๊ธฐ๋Šฅ๋“ค์€ Kaia๋ฅผ Application์˜ Storage ์ค‘ ํ•˜๋‚˜๋กœ์„œ ์‚ฌ์šฉํ•˜๋ฉฐ, ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๊ณ  ์ฝ๋Š” ํ–‰์œ„๋ฅผ ์ˆ˜์ƒํ•˜๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  Contract๋ฅผ ์ƒ์„ฑํ•˜๊ณ  Contract ๋‚ด๋ถ€์— ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

store-front์—์„œ๋Š” ๊ฐ ์นดํŽ˜์˜ ์ฃผ์ธ๋“ค์ด ์นดํŽ˜์˜ ๋ฉ”๋‰ด์™€ ์ฃผ๋ฌธ๋‚ด์—ญ์„ ์ €์žฅํ•˜๋Š” Store Contract๋ฅผ ๊ฐ๊ฐ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค. Store Contract์—๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์— ๊ฐ’์„ ์ €์žฅํ•˜๊ณ  ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ฉฐ ๊ฐ ์นดํŽ˜์˜ ์ฃผ์ธ์ด Contract Owner๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

Copy
Copied
enum MenuStatus {activated, deactivated}
enum OrderStatus {ordered, approved, denied}
enum RewardPolicyStatus {activated, deactivated}

struct Menu {
    string name;
    uint32 price;
    uint256 reward; // digit: peb
    MenuStatus status;
}

struct Order {
    address payable customer;
    string content;
    uint32 price;
    OrderStatus status;
}

event OrderReciept(
    uint32 indexed _id,
    address indexed _from,
    string _paymentTxId,
    string _content,
    uint32 _price
);

์œ„์˜ ๊ณผ์ •์„ ํ†ตํ•˜ ๋ฐฐํฌ๋œ ๊ฐ๊ฐ์˜ Store Contract๋“ค์„ Master Contract์— ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค. Master Contract๋Š” Klaybucks ์„œ๋น„์Šค ์šด์˜์ž๊ฐ€ Owner๊ฐ€ ๋˜๋ฉฐ, ์•„๋ž˜์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์นดํŽ˜์˜ ์œ„์น˜์™€ ๊ฐ Store Contract์˜ ์ฃผ์†Œ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. Store ๋ฐ์ดํ„ฐ๋Š” Map ๋˜๋Š” List ํ˜•ํƒœ๋กœ ๊ด€๋ฆฌ๋˜์–ด ๊ณ ๊ฐ๋“ค์ด ์ง€๋„์—์„œ ๋“ฑ๋ก๋œ ์นดํŽ˜๋“ค์„ ํ•œ๋ฒˆ์— ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ชจ์•„์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

Copy
Copied
enum Status {activated, deactivated}
struct Store {
    string name;
    string locationX;
    string locationY;
    address storeOwner;
    address storeContract;
    Status status;
}

Klaybucks์—์„ ๋Š” ์œ„์™€ ๊ฐ™์ด ๊ตฌํ˜„๋œ Contract๋ฅผ ์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ KAS API๋ฅผ ์ด์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ๋‚ด์šฉ์€ ์„œ๋น„์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์ฃผ์š”ํ•œ ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์„ค๋ช…๊ณผ ์ฝ”๋“œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค(์ƒ์„ธํ•œ ๊ตฌํ˜„ ์ฝ”๋“œ๋Š” ๋‹ค์Œ github์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ).

*KAS API๋Š” service-backend์—์„œ ํ˜ธ์ถœํ•˜๋ฉฐ, customer-front๋‚˜ store-front์—์„œ๋Š” ํ•„์š”ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ ๊ฐ’์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ž…๋ ฅ๋ฐ›์•„ service-backend๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์„ค๊ณ„๋Š” KAS ์‚ฌ์šฉ์— ํ•„์š”ํ•œ API Key๋ฅผ ๋Œ€์ค‘์—๊ฒŒ ๋…ธ์ถœ์‹œํ‚ค์ง€ ์•Š๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค.

์ด ๋ฌธ์„œ ํ˜น์€ KAS์— ๊ด€ํ•œ ๋ฌธ์˜๋Š” ๊ฐœ๋ฐœ์ž ํฌ๋Ÿผ์„ ๋ฐฉ๋ฌธํ•ด ๋„์›€ ๋ฐ›์œผ์‹ญ์‹œ์˜ค.

์นดํŽ˜ Contract ์ƒ์„ฑ

Klaybucks์—์„œ๋Š” ๋‘ ์ข…๋ฅ˜์˜ Contract๊ฐ€ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜๋Š” Master Contract์ด๋ฉฐ ์„œ๋น„์Šค ์‹œ์ž‘ ์‹œ ๋‹จ ํ•œ๋ฒˆ๋งŒ ๋ฐฐํฌ๋ฉ๋‹ˆ๋‹ค. ์ด Contract์—๋Š” Klaybucks์— ์†ํ•˜๋Š” ์นดํŽ˜๋“ค์˜ ์œ„์น˜์™€ ์ด๋ฆ„์ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ๋˜ ๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ Contract๋Š” Store Contract๋ผ ๋ถˆ๋ฆฌ์šฐ๋ฉฐ, ๊ฐ๊ฐ์˜ ์นดํŽ˜๊ฐ€ Klaybucks์— ๋“ฑ๋ก ์‹œ์— ์ž์‹ ๋งŒ์˜ Store Contract๋ฅผ ๋ฐฐํฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด Contract์—์„œ๋Š” ํ•ด๋‹น ์นดํŽ˜์˜ ๋ฉ”๋‰ด, ๋ณด์ƒ์ •์ฑ…, ๊ทธ๋ฆฌ๊ณ  ๊ฑฐ๋ž˜๋‚ด์—ญ์ด ๊ธฐ๋ก๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Klaybucks์—์„œ๋Š” Contract๋ฅผ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•ด์„œ Wallet API ์ค‘ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ๋ฐฐํฌํ•˜๊ธฐ: ๋‹ค๋ฅธ ๊ณ„์ •์ด ํŠธ๋žœ์žญ์…˜ ์ „์†ก ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ๋Œ€์‹  ๋ถ€๋‹ดํ•˜๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋Š” golang์œผ๋กœ ๊ตฌํ˜„๋œ service-backend์—์„œ์˜ Store Contract ๋ฐฐํฌ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค(์‹ค์ œ ์‚ฌ์šฉ๋œ ์ฝ”๋“œ๋Š” github์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ). ์˜ˆ์‹œ์—์„œ๋Š” ํ•ด๋‹น KAS API ์‚ฌ์šฉ์„ ์œ„ํ•ด ํ•„์š”ํ•œ Store Contract์˜ Bytecode์™€ ABI๊ฐ€ ์ด๋ฏธ String ํ˜•ํƒœ๋กœ ์ •์˜๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค. Store Contract๋Š” constructor ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰ ์‹œ์— input parameter๋กœ Store์˜ Name๊ณผ Owner๋ฅผ ์ž…๋ ฅ๋ฐ›๋„๋ก ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์‹œ์—์„œ๋Š” ์ด ๊ฐ’์„ ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ์ž…๋ ฅ๋ฐ›์€ ํ›„, ํ•ด๋‹น KAS API ์‚ฌ์šฉ์— ์ ํ•ฉํ•œ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Copy
Copied
// DeployStoreContract handles Store Contract deploying requests with the given store name and the owner address.
func DeployStoreContract(c *gin.Context) {
	url := "https://wallet-api.klaytnapi.com/v2/tx/fd/contract/deploy"
	params := struct {
		Name string `form:"name" json:"name"`
		Owner string `form:"owner" json:"owner"`
	}{}

	// Step 1. Get user inputs from the request
	if err := c.ShouldBindJSON(&params); err != nil {
		c.String(-1, "invalid input data" + err.Error())
		return
	}

	// Step 2. Prepare field values of contract deploy transaction.
	// STORE_CONTRACT_ABI and STORE_CONTRACT_CODE are generated from the solidity code of Store Contract.
	_abi , err := abi.JSON(bytes.NewBufferString(STORE_CONTRACT_ABI))
	if err != nil {
		c.String(-1, err.Error())
		return
	}

    // Pack function convert user inputs to the rlp-encoded constructor parameter.
	inputParam, err := _abi.Constructor.Inputs.Pack(params.Name, common.HexToAddress(params.Owner))
	if err != nil {
		c.String(-1, err.Error())
		return
	}

    // input format of the KAS API, /v2/tx/fd/contract/deploy
	bodyData, err := json.Marshal(&kasDeployTxFD{
		From: KAS_ACCOUNT,
		Value: "0x0",
		GasLimit: 8000000,
		Input: STORE_CONTRACT_CODE + Encode(inputParam),
		Submit: true,
	})
	if err != nil {
		c.String(-1, err.Error())
		return
	}

	// Step 3. Call a KAS API with parameters and HTTP headers.
	// "Authorization" header should be set with your KAS API Key (e.g., "Basic xxxx...") .
	// "x-krn" header should indicate your KAS Wallet resource (e.g., "krn:1001:wallet:80:account:default").
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyData))
	if err != nil {
		c.String(-1, err.Error())
		return
	}
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", KAS_API_KEY)
	req.Header.Add("x-krn", KAS_WALLET_KRN)

	res, err := httpClient.Do(req)
	if err != nil {
		c.String(-1, err.Error())
		return
	}

	// Step 4. Read and return back to the user the response of KAS.
	bodyContents, err := ioutil.ReadAll(res.Body)
	if err != nil {
		c.String(-1, err.Error())
		return
	}
	_ = res.Body.Close()

	c.String(200, string(bodyContents))
}

์ด ๋ฌธ์„œ ํ˜น์€ KAS์— ๊ด€ํ•œ ๋ฌธ์˜๋Š” ๊ฐœ๋ฐœ์ž ํฌ๋Ÿผ์„ ๋ฐฉ๋ฌธํ•ด ๋„์›€ ๋ฐ›์œผ์‹ญ์‹œ์˜ค.

์นดํŽ˜ ๋ฉ”๋‰ด/๋ณด์ƒ ๋“ฑ๋ก๊ณผ Klaybucks ์„œ๋น„์Šค ๋“ฑ๋ก

์นดํŽ˜ ์ฃผ์ธ์˜ ์ž์‹ ์ด ๋ฐฐํฌํ•œ ์นดํŽ˜ Contract์˜ ์ฃผ์†Œ์™€ ์œ„์น˜์ •๋ณด๋ฅผ Master Contract์— ๋“ฑ๋กํ•˜์—ฌ Klaybucks ์„œ๋น„์Šค์— ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณ ๊ฐ๋“ค์€ Klaybucks์— ๋“ฑ๋ก๋œ ์นดํŽ˜๋ฅผ ์กฐํšŒํ•  ๋•Œ ๊ฐ€์žฅ ๋จผ์ € ์กฐํšŒํ•˜๋Š” Contract์ž…๋‹ˆ๋‹ค.

Klaybucks์—์„œ๋Š” Contract๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ Wallet API ์ค‘ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ์‹คํ–‰ํ•˜๊ธฐ: ๋‹ค๋ฅธ ๊ณ„์ •์ด ํŠธ๋žœ์žญ์…˜ ์ „์†ก ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ๋Œ€์‹  ๋ถ€๋‹ดํ•˜๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋Š” golang์œผ๋กœ ๊ตฌํ˜„๋œ service-backend์—์„œ์˜ Master Contract ์‹คํ–‰ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค(์‹ค์ œ ์‚ฌ์šฉ๋œ ์ฝ”๋“œ๋Š” github์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ). ์˜ˆ์‹œ์—์„œ๋Š” ํ•ด๋‹น KAS API ์‚ฌ์šฉ์„ ์œ„ํ•ด ํ•„์š”ํ•œ Master Contract์˜ ABI๊ฐ€ ์ด๋ฏธ String ํ˜•ํƒœ๋กœ ์ •์˜๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Copy
Copied
// AddStoreInfo registers a new store to Master Contract
func AddStoreInfo(c *gin.Context) {
	url := "https://wallet-api.klaytnapi.com/v2/tx/fd/contract/execute"
	params := struct {
        Name string `json:"name"`
        X string `json:"x"`
        Y string `json:"y"`
        Owner string `json:"owner"`
        ContractAddr string `json:"contract_addr"`
	}{}

	// Step 1. Get user inputs from the request
	if err := c.ShouldBindJSON(&params); err != nil {
		c.String(-1, "invalid input data" + err.Error())
		return
	}

	// Step 2. Prepare field values of contract execution transaction.
	// MASTER_CONTRACT_ABI is generated from the solidity code of Store Contract.
	_abi , err := abi.JSON(bytes.NewBufferString(MASTER_CONTRACT_ABI))
	if err != nil {
		c.String(-1, err.Error())
		return
	}

    // Pack function convert user inputs to the rlp-encoded parameter.
    // "addStore" is a function of Master Contract.
	inputParam, err := _abi.Pack("addStore", params.Name, params.X, params.Y, common.HexToAddress(params.Owner), common.HexToAddress(params.ContractAddr))
	if err != nil {
		c.String(-1, err.Error())
		return
	}

    // input format of the KAS API, /v2/tx/fd/contract/execute
	bodyData, err := json.Marshal(&kasExecuteTxFD{
        From: KAS_ACCOUNT,
        To: MasterContract,
		Value: "0x0",
		GasLimit: 8000000,
		Input: Encode(inputParam),
		Submit: true,
	})
	if err != nil {
		c.String(-1, err.Error())
		return
	}

	// Step 3. Call a KAS API with parameters and HTTP headers.
	// "Authorization" header should be set with your KAS API Key (e.g., "Basic xxxx...") .
	// "x-krn" header should indicate your KAS Wallet resource (e.g., "krn:1001:wallet:80:account:default").
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyData))
	if err != nil {
		c.String(-1, err.Error())
		return
	}
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", KAS_API_KEY)
	req.Header.Add("x-krn", KAS_WALLET_KRN)

	res, err := httpClient.Do(req)
	if err != nil {
		c.String(-1, err.Error())
		return
	}

	// Step 4. Read and return back to the user the response of KAS.
	bodyContents, err := ioutil.ReadAll(res.Body)
	if err != nil {
		c.String(-1, err.Error())
		return
	}
	_ = res.Body.Close()

	c.String(200, string(bodyContents))
}

๋™์ผํ•œ API๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์นดํŽ˜ ์ฃผ์ธ์€ ์ž์‹ ์˜ Contract์— ๋ฉ”๋‰ด์™€ ๋ณด์ƒ ์ •์ฑ…์„ ๋“ฑ๋กํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
์œ„ ์˜ˆ์‹œ์™€ ๋น„์Šทํ•œ ์‚ฌ์šฉ์„ ํ†ตํ•˜์—ฌ ์–ธ์ œ๋“  ์ž์‹ ์˜ ์นดํŽ˜์˜ ์šด์˜ ์ƒํƒœ๋ฅผ ์Šค์Šค๋กœ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์„œ ํ˜น์€ KAS์— ๊ด€ํ•œ ๋ฌธ์˜๋Š” ๊ฐœ๋ฐœ์ž ํฌ๋Ÿผ์„ ๋ฐฉ๋ฌธํ•ด ๋„์›€ ๋ฐ›์œผ์‹ญ์‹œ์˜ค.

Transaction Receipt ํ™•์ธ

์ƒ์„ฑ๋œ Transaction์ด ๋ธ”๋ก์— ๋‹ด๊ธฐ๋ฉด Transaction Receipt์„ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๊ณ  ์ด ์ˆœ๊ฐ„์— ์ฒ˜๋ฆฌ๊ฐ€ ์™„๋ฃŒ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, Transaction์„ ๋ฐฐํฌํ•œ ํ›„์—๋Š” ๋ฐ˜๋“œ์‹œ Transaction Receipt์„ ํ™•์ธํ•˜์—ฌ ์ฒ˜๋ฆฌ๋œ ๊ฒฐ๊ณผ๊ฐ’์„ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Klaybucks์—์„œ๋Š” Transaction Receipt์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด KAS Node API๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. KAS Node API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋…ธ๋“œ์—์„œ ์ œ๊ณตํ•˜๋Š” API๋“ค์„ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋Š” golang์œผ๋กœ ๊ตฌํ˜„๋œ service-backend์—์„œ klay_getTransactionReceipt method๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Transaction Receipt์„ ํ™•์ธํ•˜๋Š” ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

Copy
Copied
func GetTxReceipt(c *gin.Context) {
	url := "https://node-api.klaytnapi.com/v2/klaytn"
	params := struct {
		TxHash string `form:"tx_hash" json:"tx_hash"`
	}{}

	// Step 1. Get user inputs from the request
	if err := c.ShouldBindQuery(&params); err != nil {
		c.String(-1, "invalid input data" + err.Error())
		return
	}

	// Step 2. Prepare a Klaytn node method and parameters to use KAS Node API.
	// You can see the detailed information of node methods from
	// https://ko.docs.klaytn.foundation/dapp/json-rpc/api-references
	bodyData, _ := json.Marshal(&kasKlaytnRPC{
		JsonRpc: "2.0",
		Method: "klay_getTransactionReceipt",
		Params: []string{params.TxHash},
		Id: 1,
	})

	// Step 3. Call a KAS API with parameters and HTTP headers.
	// "Authorization" header should be set with your KAS API Key (e.g., "Basic xxxx...") .
	// "x-krn" header should indicate your KAS Node resource (e.g., "krn:1001:node").
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyData))
	if err != nil {
		c.String(-1, err.Error())
		return
	}
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", KAS_API_KEY)
	req.Header.Add("x-krn", KAS_NODE_KRN)

	res, err := httpClient.Do(req)
	if err != nil {
		c.String(-1, err.Error())
		return
	}

	// Step 4. Read and return back to the user the response of KAS.
	bodyContents, err := ioutil.ReadAll(res.Body)
	if err != nil {
		c.String(-1, err.Error())
		return
	}
	_ = res.Body.Close()

	c.String(200, string(bodyContents))
}

Transaction Receipt์„ ํ™•์ธํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผํ•  ์ ์€ Receipt์ด ์ƒ์„ฑ๋˜๋Š” ์ˆœ๊ฐ„์ด deterministicํ•˜์ง€ ์•Š๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. Kaia ๋„คํŠธ์›Œํฌ๋Š” ๋น ๋ฅธ ๋ธ”๋Ÿญ์ƒ์„ฑ ๋Šฅ๋ ฅ์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ์— ์ผ๋ฐ˜์ ์œผ๋กœ 1์ดˆ๋‚ด์— Transaction์ด ์ฒ˜๋ฆฌ๋  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ทธ ์‹œ๊ฐ„์€ ๋„คํŠธ์›Œํฌ์˜ ์ง€์—ฐ์ด๋‚˜ Transaction ํ˜ผ์žก๋„์— ๋”ฐ๋ผ ์ง€์—ฐ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ Receipt์„ ํ™•์ธํ•˜๋ ค๋Š” ์ˆœ๊ฐ„์— ์•„์ง Transaction์ด ์ฒ˜๋ฆฌ๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ์—๋Š” Transaction์ด ์ฒ˜๋ฆฌ๋  ๋•Œ๊นŒ์ง€ ๋ฐ˜๋ณต์ ์œผ๋กœ Receipt์„ ์š”์ฒญํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

Klaybucks์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด front ์ฝ”๋“œ์—์„œ Receipt์„ ๋ฐ˜๋ณต์ ์œผ๋กœ ์š”์ฒญํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก , golang์œผ๋กœ ๊ตฌํ˜„๋œ service-backend์—์„œ ๋ฐ˜๋ณต loop์„ ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ์ž๋งŒ ๊ทธ๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉ์ž ์š”์ฒญ์— ๋Œ€ํ•œ ์‘๋‹ต์ด blocking ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด์„œ Klaybucks์—์„œ๋Š” ์•„๋ž˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ javascript๋กœ ๊ตฌํ˜„๋œ front ์ฝ”๋“œ์—์„œ receipt์„ ํ™•์ธํ•˜๊ณ  ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ๋ฐ˜๋ณต์ ์œผ๋กœ ์š”์ฒญํ•˜๋Š” ๋กœ์ง์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Copy
Copied
function CheckTransactionReceipt(txHash) {
  let self = this;
  function getReceipt(txHash) {
    axios
      .get(klaybucks_backend + "/klaytn/receipt?tx_hash=" + txHash)
      .then((response) => {
        let result = response.data["result"];
        if (result !== null) {
          clearInterval(self.intervalId2);
          // check the result and process next steps
        }
      })
      .catch((error) => {
        console.log(error);
      });
  }

  self.intervalId2 = setInterval(getReceipt, 1000, txHash);
  setTimeout(clearInterval, 10000, self.intervalId2);
}

์ด ๋ฌธ์„œ ํ˜น์€ KAS์— ๊ด€ํ•œ ๋ฌธ์˜๋Š” ๊ฐœ๋ฐœ์ž ํฌ๋Ÿผ์„ ๋ฐฉ๋ฌธํ•ด ๋„์›€ ๋ฐ›์œผ์‹ญ์‹œ์˜ค.

๊ณ ๊ฐ ๊ณ„์ • ์ƒ์„ฑ

Klaybucks์—์„œ ์นดํŽ˜ ๊ณ ๊ฐ์„ ์œ„ํ•œ customer-front๋ฅผ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Kaia Account๋ฅผ ์ƒ์„ฑํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ๊ณ ๊ฐ์ด ์นดํŽ˜ ๋ฉ”๋‰ด๋ฅผ ์ฃผ๋ฌธํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์ด Account๋กœ๋ถ€ํ„ฐ Transaction์ด ๋ฐœ์ƒํ•˜๊ฒŒ๋˜๊ณ , ์นดํŽ˜๋กœ๋ถ€ํ„ฐ KAIA ๋“ฑ์„ ๋ณด์ƒ์œผ๋กœ ๋ฐ›๋Š” ๊ฒฝ์šฐ์—๋„ ์ด Account ์ฃผ์†Œ๋กœ ๋ฐ›๊ฒŒ๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, Klaybucks์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๊ณ ๊ฐ์˜ ํ–‰์œ„๋“ค์€ ์ด Account์— ๊ท€์†๋˜์–ด ์ €์žฅ๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์œ„์™€ ๊ฐ™์ด Klaybucks ์„œ๋น„์Šค์˜ ๊ณ„์ •์„ Kaia Account๋กœ ๋Œ€์ฒดํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉ์ž๊ฐ€ Private Key๋ฅผ ๊ด€๋ฆฌํ•ด์•ผํ•œ๋‹ค๋Š” ๋ถ€๋‹ด์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ๋ณต์žกํ•œ ํ˜•ํƒœ์— Private Key๋ฅผ ๋ณด๊ด€ํ•˜๊ณ  ๊ด€๋ฆฌํ•ด์•ผํ•˜๋ฉฐ, ๋ถ„์‹คํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๋˜์ฐพ์„ ๋ฐฉ๋ฒ•์ด ์—†๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ธ”๋ก์ฒด์ธ์˜ ๋ถˆํŽธํ•œ ์‚ฌ์šฉ์„ฑ์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด Klaybucks์—์„œ๋Š” KAS์˜ Wallet API๋ฅผ ์ด์šฉํ•˜์—ฌ ์นดํŽ˜ ๊ณ ๊ฐ๋“ค์˜ Private Key ๊ด€๋ฆฌ๋ฅผ ์œ„์ž„ํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋Š” golang์œผ๋กœ ๊ตฌํ˜„๋œ service-backend์—์„œ์˜ ๊ณ ๊ฐ ๊ณ„์ •์˜ ์ƒ์„ฑ์„ ์œ„ํ•ด KAS Wallet API ์ค‘ ๊ณ„์ • ์ƒ์„ฑํ•˜๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

Copy
Copied
func CreateAccount(c *gin.Context){
	url := "https://wallet-api.klaytnapi.com/v2/account"

	// Step 1. Call a KAS API with parameters and HTTP headers.
	// "Authorization" header should be set with your KAS API Key (e.g., "Basic xxxx...") .
	// "x-krn" header should indicate your KAS Wallet resource (e.g., "krn:1001:wallet:80:account:default").
	req, err := http.NewRequest("POST", url, nil)
	if err != nil {
		c.String(-1, err.Error())
		return
	}
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", KAS_API_KEY)
	req.Header.Add("x-krn", KAS_NODE_KRN)

	res, err := httpClient.Do(req)
	if err != nil {
		c.String(-1, err.Error())
		return
	}

	// Step 2. Read and return back to the user the response of KAS.
	bodyContents, err := ioutil.ReadAll(res.Body)
	if err != nil {
		c.String(-1, err.Error())
		return
	}
	_ = res.Body.Close()

	c.String(200, string(bodyContents))
}

์ด ๋ฌธ์„œ ํ˜น์€ KAS์— ๊ด€ํ•œ ๋ฌธ์˜๋Š” ๊ฐœ๋ฐœ์ž ํฌ๋Ÿผ์„ ๋ฐฉ๋ฌธํ•ด ๋„์›€ ๋ฐ›์œผ์‹ญ์‹œ์˜ค.

์นดํŽ˜ ์ •๋ณด ์กฐํšŒ

๊ณ„์ •์„ ์ƒ์„ฑํ•œ ๊ณ ๊ฐ์€ Klaybucks์— ๋“ฑ๋ก๋œ ์นดํŽ˜๋“ค์˜ ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•˜์—ฌ Master Contract์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค. ๊ทธ ์ค‘ ์ž์‹ ์ด ์ด์šฉํ•˜๊ณ  ์‹ถ์€ ์นดํŽ˜๋ฅผ ์„ ํƒํ•˜๋ฉด ๋™์ผํ•œ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ์นดํŽ˜์˜ ๋ฉ”๋‰ด์ •๋ณด๋ฅผ ์ถ”๊ฐ€๋กœ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.

Klaybucks์—์„œ๋Š” Contract ์ค‘ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด Node API๋ฅผ ์‚ฌ์šฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋Š” golang์œผ๋กœ ๊ตฌํ˜„๋œ service-backend์—์„œ์˜ Master Contract ์‹คํ–‰ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค(์‹ค์ œ ์‚ฌ์šฉ๋œ ์ฝ”๋“œ๋Š” github์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ). ์˜ˆ์‹œ์—์„œ๋Š” ํ•ด๋‹น KAS API ์‚ฌ์šฉ์„ ์œ„ํ•ด ํ•„์š”ํ•œ Master Contract์˜ ABI๊ฐ€ ์ด๋ฏธ String ํ˜•ํƒœ๋กœ ์ •์˜๋˜์–ด ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Copy
Copied
func GetStores(c *gin.Context) {
	url := "https://node-api.klaytnapi.com/v2/klaytn"

	// Step 1. Prepare "klay_call" API among Node APIs
	bodyData, _ := json.Marshal(&kasKlaytnRPC{
		JsonRpc: "2.0",
		Method: "klay_call",
		Params: []interface{}{
			structCall{
				From: KASAccount,
				To: MASTER_CONTRACT,
				Gas: "0x7A1200", // 8000000
				Data: MASTER_CONTRACT_ABI_GETSTORE,
			},
			"latest",
		},
		Id: 1,
	})

	// Step 2. Call a KAS API with parameters and HTTP headers.
	// "Authorization" header should be set with your KAS API Key (e.g., "Basic xxxx...") .
	// "x-krn" header should indicate your KAS Node resource (e.g., "krn:1001:node").
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyData))
	if err != nil {
		c.String(-1, err.Error())
	}
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Authorization", KAS_API_KEY)
	req.Header.Add("x-krn", KAS_NODE_KRN)

	res, err := httpClient.Do(req)
	if err != nil {
		c.String(-1, err.Error())
		return
	}

	bodyContents, err := ioutil.ReadAll(res.Body)
	if err != nil {
		c.String(-1, err.Error())
		return
	}
	res.Body.Close()

	// Step 3. Parse the return of Node API
	var rpcRet struct {
		JsonRPC string `json:"jsonrpc"`
		Id int `json:"id"`
		Result string `json:"result"`
	}

	if err := json.Unmarshal(bodyContents, &rpcRet); err != nil {
		c.String(-1, err.Error())
		return
	}

	// Step 4. Parse the store information which is stored as an array in the Contract
	_abi , err := abi.JSON(bytes.NewBufferString(MASTER_CONTRACT_ABI))
	if err != nil {
		c.String(-1, err.Error())
		return
	}

	var storeArray []struct {
		Name string `json:"name"`
		X string `json:"x"`
		Y string `json:"y"`
		StoreOwner common.Address `json:"store_owner"`
		StoreContract common.Address `json:"store_contract"`
		Status uint8 `json:"status"`
	}

	if err := _abi.Unpack(&storeArray, "getStores", hexutil.MustDecode(rpcRet.Result)); err != nil {
		c.String(-1, err.Error())
		return
	}

	c.JSON(200, storeArray)
}

๊ณ ๊ฐ์€ ์œ„์™€๊ฐ™์ด ๋“ฑ๋ก๋œ ์นดํŽ˜๋“ค์„ ์กฐํšŒํ•œ ํ›„, ์›ํ•˜๋Š” ์นดํŽ˜ ๋ฉ”๋‰ด๋ฅผ ์ถ”๊ฐ€ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด ๋™์ผํ•œ API๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ๋ฉ”๋‰ด๋ฅผ ์„ ํƒํ•˜๊ณ  ์ฃผ๋ฌธ์„ ํ•˜๋ฉด 2. ์นดํŽ˜ ๋ฉ”๋‰ด/๋ณด์ƒ ๋“ฑ๋ก๊ณผ Klaybucks ์„œ๋น„์Šค ๋“ฑ๋ก์—์„œ ์‚ฌ์šฉ๋œ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ฃผ๋ฌธ๋‚ด์—ญ์„ Contract์— ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก , ๋“ฑ๋ก๋œ Transaction์˜ Receipt์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด 3. Transaction Receipt ํ™•์ธ์— ์–ธ๊ธ‰๋œ ๋‚ด์šฉ๋„ ์ˆ˜ํ–‰ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ฌธ์„œ ํ˜น์€ KAS์— ๊ด€ํ•œ ๋ฌธ์˜๋Š” ๊ฐœ๋ฐœ์ž ํฌ๋Ÿผ์„ ๋ฐฉ๋ฌธํ•ด ๋„์›€ ๋ฐ›์œผ์‹ญ์‹œ์˜ค.

์ฃผ๋ฌธ ๋‚ด์—ญ ์กฐํšŒ

๊ณ ๊ฐ์ด ํŠน์ • ์นดํŽ˜์˜ Store Contract์— ์ฃผ๋ฌธ๋‚ด์—ญ์„ ๋“ฑ๋กํ•˜๊ฒŒ ๋˜๋ฉด, ์นดํŽ˜ ์ฃผ์ธ์€ Kaia Block ๋‚ด์— ํฌํ•จ๋œ Receipt์„ ์กฐํšŒํ•˜์—ฌ ์ด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์นดํŽ˜ ์ฃผ์ธ์€ ๋งค ๋ธ”๋Ÿญ๋งˆ๋‹ค ์ž์‹ ์˜ Store Contract๋กœ๋ถ€ํ„ฐ ์ƒ์„ฑ๋œ Receipt์ด ์žˆ๋Š”์ง€๋ฅผ ์ง€์†์ ์œผ๋กœ ํ™•์ธํ•˜์—ฌ์•ผํ•ฉ๋‹ˆ๋‹ค.

Copy
Copied
// Retrieves the receipt logs of the given contract address
RetrieveLogs = (address) => {
  let self = this;
  function getReceipt(address) {
    let hexBlockNumber = caver.utils.toHex(self.state.blockNumber);
    axios
      .get(
        klaybucks_backend +
          "getReceipts?from_block=" +
          hexBlockNumber +
          "&to_block=" +
          hexBlockNumber +
          "&contract=" +
          address
      )
      .then((response) => {
        self.setState((current) => ({
          blockNumber: self.state.blockNumber + 1,
        }));
        if (response.data !== null) {
          let current_receipts = response.data;
          // Add validation logic for the receipts
          current_receipts.map((item) => {
            let order = qs.parse(item.content);
            let receipt = {
              index: item.index,
              order: JSON.stringify(order),
              price: item.price,
              reward: self.CalculateReward(order),
              tid: item.payment_tx_id,
              status: "ordered",
            };
            self.setState((current) => ({
              receipts: self.state.receipts.concat(receipt),
            }));
            return item;
          });
        }
      })
      .catch((error) => {
        console.log(error);
      });
  }
  self.intervalId = setInterval(getReceipt, 1000, address);
};

์œ„์˜ ๊ณผ์ •์„ ํ†ตํ•ด ์ฃผ๋ฌธ ๋‚ด์—ญ์ด ํ™•์ธ๋˜๋ฉด ์นดํŽ˜ ๋ฉ”๋‰ด/๋ณด์ƒ ๋“ฑ๋ก๊ณผ Klaybucks ์„œ๋น„์Šค ๋“ฑ๋ก์™€ Transaction Receipt ํ™•์ธ์—์„œ ์–ธ๊ธ‰๋œ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ๋ฌธ์˜ ์ƒํƒœ ๊ฐ’์„ "์Šน์ธ" ๋˜๋Š” "๊ฑฐ๋ถ€"๋กœ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์—…๋ฐ์ดํŠธ๋œ ์ฃผ๋ฌธ ์ƒํƒœ๋‚ด์—ญ์€ ๊ณ ๊ฐ์ธก์—์„œ๋„ ๋™์ผํ•œ ๊ณผ์ •์„ ํ†ตํ•˜์—ฌ ์กฐํšŒํ•œ ํ›„ ์ตœ์ข…์ ์œผ๋กœ ์ฃผ๋ฌธ์ด ๋ฐ›์•„๋“ค์—ฌ์กŒ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์„œ ํ˜น์€ KAS์— ๊ด€ํ•œ ๋ฌธ์˜๋Š” ๊ฐœ๋ฐœ์ž ํฌ๋Ÿผ์„ ๋ฐฉ๋ฌธํ•ด ๋„์›€ ๋ฐ›์œผ์‹ญ์‹œ์˜ค.