About XRPL
home

Credential

์ฃผ์š” ๋‚ด์šฉ
์˜จ์ฒด์ธ ์‹ ์›/๊ถŒํ•œ ์ฆ๋ช…
ํด๋”๋ช…
Credentials

์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰ ๋ช…๋ น์–ด

#1) Credential ๋ฐœ๊ธ‰ (Admin -> User) $npx ts-node xrpl/Credential/createCredential.ts #2) Credential ์ˆ˜๋ฝ (User) $npx ts-node xrpl/Credential/acceptCredential.ts #3) Credential ์‚ญ์ œ (User) $npx ts-node xrpl/Credential/deleteCredential.ts #4) Credential ์กฐํšŒ (๋ˆ„๊ตฌ๋‚˜,Optional) $npx ts-node xrpl/Credential/checkCredential.ts
Bash
๋ณต์‚ฌ

1. Credential์ด๋ž€?

Credential์€ XRPL ์›์žฅ์— ์ €์žฅ๋˜๋Š” โ€œ๋ฐœ๊ธ‰์ž โ†’ ํ”ผ๋ฐœ๊ธ‰์žโ€ ์‹ ์›ยท๊ถŒํ•œ ์ฆ๋ช… ๋ ˆ์ฝ”๋“œ๋‹ค.
ํƒ€์ž…, ๋งŒ๋ฃŒ, ์ฐธ์กฐ URI ๊ฐ™์€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•œ๋‹ค.
โ€ข
๋ฐœ๊ธ‰(Create): ๋ฐœ๊ธ‰์ž(issuer) ์ง€๊ฐ‘์ด ์ˆ˜ํ–‰
โ€ข
์ˆ˜๋ฝ(Accept): ํ”ผ๋ฐœ๊ธ‰์ž(subject) ์ง€๊ฐ‘์ด ์ˆ˜ํ–‰
โ€ข
๋„๋ฉ”์ธ ์ •์ฑ… ์—ฐ๊ณ„: ๋„๋ฉ”์ธ์˜ AcceptedCredentials์™€ ๊ฒฐํ•ฉํ•ด ์ ‘๊ทผ ์ œ์–ด์— ์‚ฌ์šฉ
โ€ข
์ฃผ์š” ํ•„๋“œ ์˜ˆ
โ—ฆ
Subject: ํ”ผ๋ฐœ๊ธ‰์ž ์ฃผ์†Œ
โ—ฆ
CredentialType: ์˜ˆ "KYC" โ†’ hex ์ธ์ฝ”๋”ฉ ๋ฌธ์ž์—ด
โ—ฆ
Expiration: ๋งŒ๋ฃŒ ์‹œ๊ฐ(๋ฆฌํ”Œ ์—ํญ ๊ธฐ์ค€ ์ดˆ)
โ—ฆ
URI: ์ฐธ์กฐ ๋ฆฌ์†Œ์Šค์˜ hex ์ธ์ฝ”๋”ฉ URL
ํ—ฌํผ: toHex("KYC"), toHex("https://...") ๊ฐ™์€ ๊ฐ„๋‹จํ•œ hex ์ธ์ฝ”๋”๋ฅผ ์Šคํฌ๋ฆฝํŠธ์— ํฌํ•จํ•ด ์“ฐ๋ฉด ํŽธํ•จ.

2. ์™œ ํ•„์š”ํ•œ๊ฐ€?

โ€ข
๊ทœ์ œ ์ค€์ˆ˜: KYC/AML ์ถฉ์กฑ ๊ณ„์ •๋งŒ ์„œ๋น„์Šคยท๊ฑฐ๋ž˜ ์ ‘๊ทผ ํ—ˆ์šฉ
โ€ข
์ ‘๊ทผ ํ†ต์ œ: ํŠน์ • Credential ๋ณด์œ ์ž๋งŒ ๋„๋ฉ”์ธ/์˜ค๋”๋ถ/๊ธฐ๋Šฅ ์ฐธ์—ฌ
โ€ข
์ •์ฑ… ๋ถ„๋ฆฌ: ์ž์‚ฐยท์˜คํผ์— ์ง์ ‘ ์ œ์•ฝ์„ ๋ฐ•์ง€ ์•Š๊ณ  ๋„๋ฉ”์ธ ์ •์ฑ…์—์„œ ์ผ๊ด„ ๊ด€๋ฆฌ
โ€ข
์œ ์—ฐ์„ฑ/์ƒํ˜ธ์šด์šฉ: ํƒ€์ž…/๋งŒ๋ฃŒ/URI๋ฅผ ์กฐํ•ฉํ•ด ๋‹ค์–‘ํ•œ ์ ‘๊ทผ ๋ชจ๋ธ ๊ตฌ์„ฑ

3. ์‹œ๋‚˜๋ฆฌ์˜ค: create โ†’ accept โ†’ check โ†’ delete

Step 1. Credential ๋ฐœ๊ธ‰ (Create)

โ€ข
์ฃผ์ฒด: ๋ฐœ๊ธ‰์ž(๊ด€๋ฆฌ์ž)
โ€ข
ํ–‰๋™: CredentialCreate ํŠธ๋žœ์žญ์…˜ ์ „์†ก
โ€ข
๋‚ด์šฉ:
โ—ฆ
Subject: ํ”ผ๋ฐœ๊ธ‰์ž ์ฃผ์†Œ
โ—ฆ
CredentialType: ์˜ˆ hex("KYC")
โ—ฆ
Expiration: ์˜ˆ now + 3600
โ—ฆ
URI: ์ฐธ์กฐ ๋งํฌ์˜ hex
import { Client, Wallet, Transaction, CredentialCreate } from "xrpl" import path from "path" import dotenv from "dotenv" // .env ํŒŒ์ผ ๋กœ๋“œ dotenv.config({ path: path.join(__dirname, "..", ".env") }) // ๋ฌธ์ž์—ด โ†’ hex ๋ณ€ํ™˜ (์˜ˆ: "KYC" โ†’ "4B5943") const toHex = (s: string) => Buffer.from(s, "utf8").toString("hex") // ํ˜„์žฌ ์‹œ๊ฐ„ (๋ฆฌํ”Œ ์—ํญ ์ดˆ ๋‹จ์œ„) const now = () => Math.floor(Date.now() / 1000) export async function createCredential() { const client = new Client("wss://s.devnet.rippletest.net:51233") await client.connect() const ADMIN_SEED = process.env.ADMIN_SEED const USER_SEED = process.env.USER_SEED if (!ADMIN_SEED || !USER_SEED) { throw new Error("Missing env: ADMIN_SEED, USER_SEED") } try { const issuer = Wallet.fromSeed(ADMIN_SEED) // โœ… ๋ฐœ๊ธ‰์ž(์„œ๋ช…์ž) const subject = Wallet.fromSeed(USER_SEED) // ํ”ผ๋ฐœ๊ธ‰์ž // CredentialCreate ํŠธ๋žœ์žญ์…˜ ๊ตฌ์„ฑ const tx: CredentialCreate = { TransactionType: "CredentialCreate", Account: issuer.address, // ๋ฐœ๊ธ‰์ž(์„œ๋ช…์ž) Subject: subject.address, // ํ”ผ๋ฐœ๊ธ‰์ž CredentialType: toHex("KYC"), // "KYC" โ†’ hex Expiration: now() + 3600, // 1์‹œ๊ฐ„ ํ›„ ๋งŒ๋ฃŒ URI: toHex("https://example.com/credentials/kyc/user") // ์ฐธ์กฐ ๋ฆฌ์†Œ์Šค URL(hex) } // ํŠธ๋žœ์žญ์…˜ ์ž๋™ ๋ณด์™„ & ์„œ๋ช… const prepared = await client.autofill(tx) const signed = issuer.sign(prepared) // ์ œ์ถœ ํ›„ ๊ฒฐ๊ณผ ํ™•์ธ const res = await client.submitAndWait(signed.tx_blob) console.log(JSON.stringify(res.result, null, 2)) return res.result } catch (err) { console.error("โŒ Credential ๋ฐœ๊ธ‰ ์‹คํŒจ:", err) throw err } finally { await client.disconnect() console.log("๐Ÿ”„ ์—ฐ๊ฒฐ ์ข…๋ฃŒ") } } // ๋‹จ๋… ์‹คํ–‰ ์‹œ ๋™์ž‘ if (require.main === module) { createCredential().catch((e) => { console.error(e) process.exit(1) }) }
TypeScript
๋ณต์‚ฌ
โ€ข
๋งŒ๋ฃŒ๊ฐ€ ์ง€๋‚˜๋ฉด ๋„๋ฉ”์ธ ์ •์ฑ…์—์„œ ํ•ด๋‹น Credential์„ ๋ถ€์ ๊ฒฉ์œผ๋กœ ๊ฐ„์ฃผํ•  ์ˆ˜ ์žˆ์Œ.

Step 2. Credential ์ˆ˜๋ฝ (Accept)

โ€ข
์ฃผ์ฒด: ํ”ผ๋ฐœ๊ธ‰์ž(์‚ฌ์šฉ์ž)
โ€ข
ํ–‰๋™: CredentialAccept ํŠธ๋žœ์žญ์…˜ ์ „์†ก
โ€ข
๋‚ด์šฉ:
โ—ฆ
Account: ํ”ผ๋ฐœ๊ธ‰์ž(์„œ๋ช…์ž)
โ—ฆ
Issuer: ๋ฐœ๊ธ‰์ž ์ฃผ์†Œ
โ—ฆ
CredentialType: Create ๋‹จ๊ณ„์™€ ๋™์ผํ•œ hex ํƒ€์ž…
import { Client, Wallet, Transaction } from "xrpl" import path from "path" import dotenv from "dotenv" // .env ๋กœ๋“œ dotenv.config({ path: path.join(__dirname, "..", ".env") }) // ๋ฌธ์ž์—ด์„ hex๋กœ ๋ณ€ํ™˜ (์˜ˆ: "KYC" โ†’ 4B5943) const toHex = (s: string) => Buffer.from(s, "utf8").toString("hex") export async function acceptCredential() { const client = new Client("wss://s.devnet.rippletest.net:51233") await client.connect() const ADMIN_SEED = process.env.ADMIN_SEED const USER_SEED = process.env.USER_SEED if (!ADMIN_SEED || !USER_SEED) { throw new Error("Missing env: ADMIN_SEED, USER_SEED") } // ๋ฐœ๊ธ‰์ž = Admin, ํ”ผ๋ฐœ๊ธ‰์ž = User const issuer = Wallet.fromSeed(ADMIN_SEED) const subject = Wallet.fromSeed(USER_SEED) // โœ… ์„œ๋ช…์ž = ํ”ผ๋ฐœ๊ธ‰์ž try { // CredentialAccept ํŠธ๋žœ์žญ์…˜ ๊ตฌ์„ฑ const tx: Transaction = { TransactionType: "CredentialAccept", Account: subject.address, // โœ… ํ”ผ๋ฐœ๊ธ‰์ž ์„œ๋ช…/์ „์†ก Issuer: issuer.address, CredentialType: toHex("KYC"), // createCredential.ts์™€ ๋™์ผ } // ์ž๋™ ๋ณด์™„ & ์„œ๋ช… const prepared = await client.autofill(tx) const signed = subject.sign(prepared) // ์ œ์ถœ & ๊ฒฐ๊ณผ ๋Œ€๊ธฐ const result = await client.submitAndWait(signed.tx_blob) console.log(JSON.stringify(result, null, 2)) return result } catch (err) { console.error("โŒ Credential ์ˆ˜๋ฝ ์‹คํŒจ:", err) throw err } finally { await client.disconnect() console.log("๐Ÿ”„ ์—ฐ๊ฒฐ ์ข…๋ฃŒ") } } // ๋‹จ๋… ์‹คํ–‰ ์‹œ ๋™์ž‘ if (require.main === module) { acceptCredential().catch((e) => { console.error(e) process.exit(1) }) }
TypeScript
๋ณต์‚ฌ
โ€ข
์ผ๋ถ€ ๋„๋ฉ”์ธ์€ ์ˆ˜๋ฝ(accept) ๋œ Credential๋งŒ ์œ ํšจ๋กœ ์ธ์ •ํ•  ์ˆ˜ ์žˆ์Œ.

Step 3. Credential ์กฐํšŒ (Check)

โ€ข
์ฃผ์ฒด: ๋ˆ„๊ตฌ๋‚˜(๊ณต๊ฐœ ์›์žฅ ์กฐํšŒ)
โ€ข
ํ–‰๋™: account_objects RPC โ†’ LedgerEntryType === "Credential" ํ•„ํ„ฐ
import { Client, Wallet } from "xrpl" import path from "path" import dotenv from "dotenv" // .env ํŒŒ์ผ ๋กœ๋“œ dotenv.config({ path: path.join(__dirname, "..", ".env") }) export async function checkCredential() { const client = new Client("wss://s.devnet.rippletest.net:51233") await client.connect() const USER_SEED = process.env.USER_SEED if (!USER_SEED) throw new Error("Missing env: USER_SEED") try { const subject = Wallet.fromSeed(USER_SEED) // โœ… ์กฐํšŒ ๋Œ€์ƒ = User const all: any[] = [] let marker: any = undefined // account_objects ํŽ˜์ด์ง€๋„ค์ด์…˜ ์กฐํšŒ do { const r: any = await client.request({ command: "account_objects", account: subject.address, // ์กฐํšŒํ•  ๊ณ„์ • ์ฃผ์†Œ limit: 400, // ์ตœ๋Œ€ 400๊ฐœ ...(marker ? { marker } : {}) }) // Credential ํƒ€์ž…๋งŒ ํ•„ํ„ฐ๋ง const creds = (r.result.account_objects || []).filter( (o: any) => o.LedgerEntryType === "Credential" ) all.push(...creds) marker = r.result.marker } while (marker) // ๊ฒฐ๊ณผ ์ถœ๋ ฅ console.log(JSON.stringify(all, null, 2)) return all } catch (err) { console.error("โŒ Credential ์กฐํšŒ ์‹คํŒจ:", err) throw err } finally { await client.disconnect() console.log("๐Ÿ”„ ์—ฐ๊ฒฐ ์ข…๋ฃŒ") } } // ๋‹จ๋… ์‹คํ–‰ ์‹œ ๋™์ž‘ if (require.main === module) { checkCredential().catch((e) => { console.error(e) process.exit(1) }) }
TypeScript
๋ณต์‚ฌ
โ€ข
Issuer, CredentialType, Expiration ๋“ฑ์„ ๊ธฐ์ค€์œผ๋กœ ์œ ํšจ Credential๋งŒ ๊ณจ๋ผ์„œ ํ‘œ์‹œ.

Step 4. Credential ์‚ญ์ œ (Delete)

โ€ข
์ฃผ์ฒด: ํ”ผ๋ฐœ๊ธ‰์ž(๋ณธ์ธ)
โ€ข
ํ–‰๋™: CredentialDelete ํŠธ๋žœ์žญ์…˜ ์ „์†ก
โ€ข
๋‚ด์šฉ:
โ—ฆ
Account: ํ”ผ๋ฐœ๊ธ‰์ž(์„œ๋ช…์ž)
โ—ฆ
Issuer: ๋ฐœ๊ธ‰์ž ์ฃผ์†Œ
โ—ฆ
Subject: ํ”ผ๋ฐœ๊ธ‰์ž ์ฃผ์†Œ
โ—ฆ
CredentialType: ์‚ญ์ œํ•  ํƒ€์ž…(hex)
import { Client, Wallet, Transaction } from "xrpl" import path from "path" import dotenv from "dotenv" // .env ํŒŒ์ผ ๋กœ๋“œ dotenv.config({ path: path.join(__dirname, "..", ".env") }) // ๋ฌธ์ž์—ด์„ hex ์ธ์ฝ”๋”ฉ์œผ๋กœ ๋ณ€ํ™˜ (CredentialType, URI ๋“ฑ์— ์‚ฌ์šฉ) const toHex = (s: string) => Buffer.from(s, "utf8").toString("hex") export async function deleteCredential() { const client = new Client("wss://s.devnet.rippletest.net:51233") await client.connect() const ADMIN_SEED = process.env.ADMIN_SEED // ๋ฐœ๊ธ‰์ž (Issuer, ์ง€์ •์šฉ) const USER_SEED = process.env.USER_SEED // โœ… ์‚ญ์ œ ์ฃผ์ฒด(๋ณธ์ธ, Subject) if (!ADMIN_SEED || !USER_SEED) throw new Error("Missing env: ADMIN_SEED, USER_SEED") try { const issuer = Wallet.fromSeed(ADMIN_SEED) const subject = Wallet.fromSeed(USER_SEED) // โœ… ๋ณธ์ธ(Subject) // Credential ์‚ญ์ œ ํŠธ๋žœ์žญ์…˜ const tx: Transaction = { TransactionType: "CredentialDelete", Account: subject.address, // โœ… ๋ณธ์ธ์ด ์„œ๋ช…/์ „์†ก Issuer: issuer.address, // ๋ฐœ๊ธ‰์ž ์ฃผ์†Œ (๋ช…์‹œ ๊ถŒ์žฅ) Subject: subject.address, // ํ”ผ๋ฐœ๊ธ‰์ž ๋ณธ์ธ ์ฃผ์†Œ (๋ช…์‹œ ๊ถŒ์žฅ) CredentialType: toHex("KYC") // create/accept๊ณผ ๋™์ผํ•œ hex ํƒ€์ž… } const prepared = await client.autofill(tx) // ์ž๋™ ํ•„๋“œ ์ฑ„์›€(์ˆ˜์ˆ˜๋ฃŒ, ์‹œํ€€์Šค ๋“ฑ) const signed = subject.sign(prepared) // โœ… ๋ณธ์ธ(Subject) ์„œ๋ช… const result = await client.submitAndWait(signed.tx_blob) console.log(JSON.stringify(result, null, 2)) return result } catch (err) { console.error("โŒ Credential ์‚ญ์ œ ์‹คํŒจ:", err) throw err } finally { await client.disconnect() console.log("๐Ÿ”„ ์—ฐ๊ฒฐ ์ข…๋ฃŒ") } } // ์ง์ ‘ ์‹คํ–‰ ์‹œ ๋™์ž‘ if (require.main === module) { deleteCredential().catch((e) => { console.error(e); process.exit(1) }) }
TypeScript
๋ณต์‚ฌ
โ€ข
๋„๋ฉ”์ธ ์ •์ฑ…์ด โ€œ๋ณด์œ  ์ค‘โ€ Credential์„ ์š”๊ตฌํ•˜๋Š” ๊ฒฝ์šฐ, ์‚ญ์ œ ํ›„์—” ์ ‘๊ทผ์ด ์ œํ•œ๋  ์ˆ˜ ์žˆ์Œ.