์คํฌ๋ฆฝํธ ์คํ ๋ช
๋ น์ด
#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์ ์๊ตฌํ๋ ๊ฒฝ์ฐ, ์ญ์ ํ์ ์ ๊ทผ์ด ์ ํ๋ ์ ์์.

