μ€ν¬λ¦½νΈ μ€ν λͺ
λ Ήμ΄ (AMM)
$npx ts-node xrpl/AMM/AMMCreate.ts
$npx ts-node xrpl/AMM/AMMDeposit.ts
$npx ts-node xrpl/AMM/AMMWithdraw.ts
$npx ts-node xrpl/AMM/AMMSwap.ts
$npx ts-node xrpl/AMM/AMMVote.ts
$npx ts-node xrpl/AMM/AMMBid.ts
$npx ts-node xrpl/AMM/AMMDelete.ts
$npx ts-node xrpl/AMM/AMMClawback.ts # (μ΅μ
) XLS-73 Clawback κΈ°λ₯
$npx ts-node xrpl/AMM/getAMMInfo.ts # (μ΅μ
) μμ±λ ν μ‘°ν κΈ°λ₯
Bash
볡μ¬
1. AMM(Automated Market Maker)λ?
AMMμ XRPL μμ₯μ μ§μ ν΅ν©λ μλνλ μ λμ± νμ΄λ€.
XLS-30d Amendmentλ‘ λμ
λμμΌλ©°, κΈ°μ‘΄ DEX μ€λλΆκ³Ό λ³νλμ΄ μ΅μ μ κ°κ²© κ²½λ‘λ₯Ό μ°Ύμ μ μλ€.
β’
μ€λλΆ κΈ°λ° κ±°λ: κΈ°μ‘΄ XRPLμ DEX λ©μ»€λμ¦, κ°λ³ λ§€λ/λ§€μ μ£Όλ¬Έ λ§€μΉ
β’
AMM κΈ°λ° κ±°λ: μμ° μλ³λ‘ νλμ μ λμ± νμ μ΄μ, 곑μ μ λ°λΌ μλ κ°κ²© κ²°μ
AMM νμ νΉλ³ κ³μ (Account)μ μν΄ μμ°μ 보κ΄νκ³ , μμΉμμκ² LP ν ν°μ λ°ννλ€.
LP ν ν° λ³΄μ μλ μ§λΆμ λ°λΌ μμ΅μ 곡μ νλ©°, μμλ£μ¨ ν¬νκΆμ κ°λλ€.
2. μ νμνκ°?
β’
DEX νμ₯μ±
Order Bookλ§μΌλ‘λ μμ μμ₯μμ μ¬λ¦¬νΌμ§κ° μ»€μ§ μ μμ β AMMμ ν΅ν΄ 보μ
β’
μ λμ± μ 곡μ μΈμΌν°λΈ
λ¨μ 보μ λμ νμ μ λμ±μ 곡κΈν΄ μμλ£ μμ΅ νλ κ°λ₯
β’
λ€μν μ°Έμ¬ λ°©μ
β¦
νΈλ μ΄λ: μ€μ(κ΅ν)
β¦
LP: μ λμ± μ
κΈ/μΆκΈ
β¦
LP ν ν° λ³΄μ μ: μμλ£μ¨ ν¬ν, κ²½λ§€ μ
μ°° μ°Έμ¬
β’
κ±°λ²λμ€ λ΄μ₯
μμλ£μ¨ ν¬ν, μ¬λ‘― κ²½λ§€ λ± ν λ΄λΆ κ·μΉμ΄ νλ‘ν μ½ μ°¨μμμ μ§μλ¨
3. μλ리μ€: AMMCreate β AMMDeposit β AMMWithdraw β AMMSwap β AMMVote β AMMBid β AMMDelete (+ μ΅μ : AMMClawback)
μλ μλ리μ€λ AMMμ μ 체 λΌμ΄νμ¬μ΄ν΄μ λ€λ£¬λ€.
ν μμ±λΆν° μ λμ± κ³΅κΈ/μ κ±°, κ±°λ, κ±°λ²λμ€, κ²½λ§€, μμ κΉμ§ μμ°¨μ μΌλ‘ μ§νλλ€.
Step 1. AMM ν μμ± (AMMCreate.ts)
β’
주체: ν ν° λ°νμ(ADMIN)
β’
νλ: AMMCreate νΈλμμ
μ μ‘
β’
λ΄μ©: λ μμ°(XRP + IOU)μΌλ‘ νμ μμ±νκ³ μ΄κΈ° μ λμ±μ 곡κΈ
β’
κ²°κ³Ό: μλ‘μ΄ AMM μΈμ€ν΄μ€κ° μμ₯μ λ±λ‘λκ³ , LP ν ν°μ΄ λ°κΈλ¨
const tx: Transaction= {
TransactionType: "AccountSet",
Account: admin.address, // AMM ν μμ±μ(Admin)
SetFlag: 8 // asfDefaultRipple : Enable rippling on this account's trust lines by default.
}
TypeScript
볡μ¬
β’
μ£Όμμ¬ν : μ¬μ μ Admin κ³μ μμ Ripping νμ© νλκ·Έ μΈν
ν΄ μ€μΌ ν¨. (AccountSetμμ νλκ·Έ 8λ‘ μ€μ )
β’
κΈμ΅κΈ°κ΄μ΄λ λ§μΌλ©μ΄μ»€μ²λΌ IOUλ₯Ό μ κ·Ήμ μΌλ‘ μ ν΅μν€κ³ μΆμ 주체λ μ΄ νλκ·Έλ₯Ό μΌλ κ² μΌλ°μ μ
import { Client, Wallet, Transaction } from "xrpl"
import path from "path"
import dotenv from "dotenv"
dotenv.config({ path: path.join(__dirname, "..", ".env") })
/**
* AMMCreate νΈλμμ
: μλ‘μ΄ AMM νμ μμ±
* - λ μμ°μ μμΉν΄ νμ λ§λ€κ³ μ΄κΈ° TradingFee μ€μ
* - TradingFee λ²μ: 0 ~ 1000 (0.001% ~ 1%)
*/
export async function AMMCreate() {
const client = new Client("wss://s.devnet.rippletest.net:51233")
await client.connect()
const ADMIN_SEED = process.env.ADMIN_SEED
if (!ADMIN_SEED) throw new Error("Missing env: ADMIN_SEED")
const admin = Wallet.fromSeed(ADMIN_SEED.trim())
/**
* β οΈ μμ μμ° μ€μ
* - Asset: XRP
* - Asset2: USD (ADMIN λ°ν IOU)
* - TradingFee: 30 (0.3%)
*/
const tx: Transaction = {
TransactionType: "AMMCreate",
Account: admin.address, // ν μμ±μ
Amount: "10000000", // 10 XRP (drops λ¨μ)
Amount2: {
currency: "ABC,
issuer: admin.address,
value: "100"
},
TradingFee: 30 // 0.3%
}
try {
const prepared = await client.autofill(tx)
const signed = admin.sign(prepared)
const result = await client.submitAndWait(signed.tx_blob)
console.log("β
AMMCreate Result:")
console.log(JSON.stringify(result, null, 2))
return result
} finally {
await client.disconnect()
console.log("π μ°κ²° μ’
λ£")
}
}
// μ§μ μ€ν
if (require.main === module) {
AMMCreate().catch(e => {
console.error(e)
process.exit(1)
})
}
TypeScript
볡μ¬
Step 2. μ λμ± μΆκ° (AMMDeposit.ts)
β’
주체: μΌλ° μ μ (USER)
β’
νλ: AMMDeposit νΈλμμ
μ μ‘
β’
λ΄μ©: νμ λ μμ°μ μμΉνκ³ , λΉμ¨λ§νΌ LP ν ν° μλ Ή
β’
κ²°κ³Ό: μ μ κ° LP ν ν°μ νλ, μ§λΆμ λ°λΌ μμ΅Β·ν¬νκΆ λ³΄μ
import { Client, Wallet, Transaction } from "xrpl"
import path from "path"
import dotenv from "dotenv"
dotenv.config({ path: path.join(__dirname, "..", ".env") })
/**
* AMMDeposit νΈλμμ
: κΈ°μ‘΄ AMM νμ μ λμ± μΆκ°
* - λ μμ° λΉμ¨μ λ§μΆ° μΆκ°νκ±°λ, λ¨μΌ μμ°λ§ μΆκ°ν μλ μμ
* - μμμμλ XRP + USD(IOU) λΉμ¨λ‘ μμΉ
*/
export async function AMMDeposit() {
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")
const admin = Wallet.fromSeed(ADMIN_SEED.trim())
const user = Wallet.fromSeed(USER_SEED.trim())
const tx: Transaction = {
TransactionType: "AMMDeposit",
Account: user.address, // μ λμ± μΆκ° 주체 (νΈλ μ΄λ)
Asset: { currency: "XRP" }, // 첫 λ²μ§Έ ν μμ°
Asset2: {
currency: "USD",
issuer: admin.address
},
Amount: "5000000", // 5 XRP (drops λ¨μ)
Amount2: {
currency: "USD",
issuer: admin.address,
value: "50"
},
Flags: 0x00100000 // tfTwoAsset: λ μμ° λΉμ¨ λ§μΆ° μμΉ
}
try {
const prepared = await client.autofill(tx)
const signed = user.sign(prepared)
const result = await client.submitAndWait(signed.tx_blob)
console.log("β
AMMDeposit Result:")
console.log(JSON.stringify(result, null, 2))
return result
} finally {
await client.disconnect()
console.log("π μ°κ²° μ’
λ£")
}
}
// μ§μ μ€ν
if (require.main === module) {
AMMDeposit().catch(e => {
console.error(e)
process.exit(1)
})
}
TypeScript
볡μ¬
Step 3. μ λμ± μ κ±° (AMMWithdraw.ts)
β’
주체: μ λμ± κ³΅κΈμ(USER)
β’
νλ: AMMWithdraw νΈλμμ
μ μ‘
β’
λ΄μ©: LP ν ν°μ λ°νν΄ μλ μμ°μ νμ
β’
κ²°κ³Ό: νμ μ λμ±μ΄ μ€κ³ , USER μ§κ°μ μμ° λ°ν
import { Client, Wallet, Transaction } from "xrpl"
import path from "path"
import dotenv from "dotenv"
dotenv.config({ path: path.join(__dirname, "..", ".env") })
/**
* AMMWithdraw νΈλμμ
: κΈ°μ‘΄ AMM νμμ μ λμ± μ κ±°
* - LPTokenμ μ μΆνλ©΄, κ·Έ λΉμ¨λ§νΌ μμ°(A/B)μ λ°νλ°μ
* - μ 체 μΈμΆ(tfWithdrawAll) λλ λΆλΆ μΈμΆ κ°λ₯
*/
export async function AMMWithdraw() {
const client = new Client("wss://s.devnet.rippletest.net:51233")
await client.connect()
const USER_SEED = process.env.USER_SEED
const ADMIN_SEED = process.env.ADMIN_SEED
if (!USER_SEED || !ADMIN_SEED) throw new Error("Missing env: USER_SEED, ADMIN_SEED")
const user = Wallet.fromSeed(USER_SEED.trim())
const admin = Wallet.fromSeed(ADMIN_SEED.trim())
const tx: Transaction = {
TransactionType: "AMMWithdraw",
Account: user.address, // μ λμ± μ κ±° 주체
Asset: { currency: "XRP" },
Asset2: {
currency: "USD",
issuer: admin.address
},
LPTokenIn: {
currency: "LP", // getAMMInfo κ²°κ³Όμμ λ°νλ lp_token.currency(16μ§ λ¬Έμμ΄)κ°
issuer: "rAMMInstance...", // AMM κ³μ μ£Όμ (ν μμ± μ μλ μμ±λ¨)
value: "10" // μ κ±°ν LPToken μλ
},
Flags: 0x00020000 // tfWithdrawAll: λͺ¨λ μμ° λΉμ¨λ‘ μΆκΈ
}
try {
const prepared = await client.autofill(tx)
const signed = user.sign(prepared)
const result = await client.submitAndWait(signed.tx_blob)
console.log("β
AMMWithdraw Result:")
console.log(JSON.stringify(result, null, 2))
return result
} finally {
await client.disconnect()
console.log("π μ°κ²° μ’
λ£")
}
}
// μ§μ μ€ν
if (require.main === module) {
AMMWithdraw().catch(e => {
console.error(e)
process.exit(1)
})
}
TypeScript
볡μ¬
Step 4. μμ° μ€μ (AMMSwap.ts)
β’
주체: νΈλ μ΄λ(USER)
β’
νλ: Payment νΈλμμ
μ μ‘
β’
λ΄μ©: SendMax, Amount νλλ₯Ό μ§μ ν΄ κ΅ν κ²½λ‘ νμ β μ€λλΆ+AMMμ μ‘°ν©ν΄ μ΅μ κ°κ²©μ 체결
β’
κ²°κ³Ό: μ μ μμ° κ΅ν μλ£, μμλ£λ νμ λΆλ°°
import { Client, Wallet, Transaction } from "xrpl"
import path from "path"
import dotenv from "dotenv"
dotenv.config({ path: path.join(__dirname, "..", ".env") })
/**
* AMMSwap (μ€μ λ‘λ Payment νΈλμμ
νμ©)
* - AssetA β AssetB κ΅ν
* - AMM νκ³Ό μ€λλΆμ μλμΌλ‘ κ²½μ νμ¬ μ΅μ κ°κ²© κ²½λ‘λ₯Ό μ ν
*/
export async function AMMSwap() {
const client = new Client("wss://s.devnet.rippletest.net:51233")
await client.connect()
const USER_SEED = process.env.USER_SEED
const ADMIN_SEED = process.env.ADMIN_SEED
if (!USER_SEED || !ADMIN_SEED) throw new Error("Missing env: USER_SEED, ADMIN_SEED")
const user = Wallet.fromSeed(USER_SEED.trim())
const admin = Wallet.fromSeed(ADMIN_SEED.trim())
/**
* β οΈ μμ: USERκ° 5 XRPλ₯Ό λ΄κ³ , μ΅μ 40 USD(IOU)λ₯Ό λ°κ³ μΆμ λ
* - Amount: λ΄κ° λ°κ³ μΆμ μμ°
* - SendMax: λ΄κ° μ΅λ ν¬μ
ν μμ°
*/
const tx: Transaction = {
TransactionType: "Payment",
Account: user.address,
Destination: user.address, // μκΈ° μμ μ λμμΌλ‘ μ€μ (μ€μ κ²°κ³Όλ₯Ό λ³ΈμΈ μ§κ°μ λ°μ)
Amount: {
currency: "USD",
issuer: admin.address,
value: "40" // λ°κ³ μΆμ USD μ΅μ μλ
},
SendMax: "5000000", // μ΅λ 5 XRP μ§λΆ (drops λ¨μ)
Flags: 0x00020000 // tfPartialPayment (μΌλΆλ§ μΆ©μ‘±ν΄λ μ€ν κ°λ₯)
}
try {
const prepared = await client.autofill(tx)
const signed = user.sign(prepared)
const result = await client.submitAndWait(signed.tx_blob)
console.log("β
AMMSwap (Payment) Result:")
console.log(JSON.stringify(result, null, 2))
return result
} finally {
await client.disconnect()
console.log("π μ°κ²° μ’
λ£")
}
}
// μ§μ μ€ν
if (require.main === module) {
AMMSwap().catch(e => {
console.error(e)
process.exit(1)
})
}
TypeScript
볡μ¬
Step 5. μμλ£ ν¬ν (AMMVote.ts)
β’
주체: LP ν ν° λ³΄μ μ(USER)
β’
νλ: AMMVote νΈλμμ
μ μ‘
β’
λ΄μ©: νμ TradingFee(0~1000, 0~1%)μ ν¬ν β κ°μ€μΉλ 보μ LP ν ν° μλ
β’
κ²°κ³Ό: λͺ¨λ ν¬νμ κ°μ€ νκ· μΌλ‘ ν μμλ£μ¨ κ²°μ
import { Client, Wallet, Transaction } from "xrpl"
import path from "path"
import dotenv from "dotenv"
dotenv.config({ path: path.join(__dirname, "..", ".env") })
/**
* AMMVote νΈλμμ
* - LPToken 보μ μκ° AMM μΈμ€ν΄μ€μ TradingFeeμ ν¬ν
* - TradingFee λ²μ: 0 ~ 1000 (0.001% ~ 1%)
*/
export async function AMMVote() {
const client = new Client("wss://s.devnet.rippletest.net:51233")
await client.connect()
const USER_SEED = process.env.USER_SEED
const ADMIN_SEED = process.env.ADMIN_SEED
if (!USER_SEED || !ADMIN_SEED) throw new Error("Missing env: USER_SEED, ADMIN_SEED")
const user = Wallet.fromSeed(USER_SEED.trim())
const admin = Wallet.fromSeed(ADMIN_SEED.trim())
/**
* β οΈ μμ: USERκ° XRP/USD AMM νμ λν΄ 0.25% (TradingFee = 25) λ‘ ν¬ν
*/
const tx: Transaction = {
TransactionType: "AMMVote",
Account: user.address,
Asset: { currency: "XRP" }, // νμ 첫 λ²μ§Έ μμ°
Asset2: { currency: "USD", issuer: admin.address }, // νμ λ λ²μ§Έ μμ°
TradingFee: 25 // 0.25%
}
try {
const prepared = await client.autofill(tx)
const signed = user.sign(prepared)
const result = await client.submitAndWait(signed.tx_blob)
console.log("β
AMMVote Result:")
console.log(JSON.stringify(result, null, 2))
return result
} finally {
await client.disconnect()
console.log("π μ°κ²° μ’
λ£")
}
}
// μ§μ μ€ν
if (require.main === module) {
AMMVote().catch(e => {
console.error(e)
process.exit(1)
})
}
TypeScript
볡μ¬
Step 6. κ²½λ§€ μ¬λ‘― μ μ°° (AMMBid.ts)
β’
주체: Arbitrageur(USER)
β’
νλ: AMMBid νΈλμμ
μ μ‘
β’
λ΄μ©: LP ν ν°μΌλ‘ μ
μ°° β μ¬λ‘― νλ μ 24μκ° λμ μμλ£ ν μΈ νν
β’
κ²°κ³Ό: κΈ°μ‘΄ μ¬λ‘― 보μ μκ° μλ€λ©΄ μΌλΆ 리νλ ν μκ°, USERκ° μ μ¬λ‘― 보μ μ λ¨
import { Client, Transaction, Wallet } from "xrpl"
import path from "path"
import dotenv from "dotenv"
dotenv.config({ path: path.join(__dirname, "..", ".env") })
/**
* AMMBid: AMM νμ κ²½λ§€ μ¬λ‘― μ
μ°°
* - μ
μ°°μλ LPToken λ¨μλ‘ BidMax/BidMin μ§μ κ°λ₯
* - μΉλ¦¬ μ 24μκ° λμ μμλ£ ν μΈ (TradingFee/10)
*/
export async function AMMBid() {
const client = new Client("wss://s.devnet.rippletest.net:51233")
await client.connect()
const USER_SEED = process.env.USER_SEED
const ADMIN_SEED = process.env.ADMIN_SEED
if (!USER_SEED || !ADMIN_SEED) throw new Error("Missing env: USER_SEED, ADMIN_SEED")
const user = Wallet.fromSeed(USER_SEED.trim())
const admin = Wallet.fromSeed(ADMIN_SEED.trim())
// currencyμ λ€μ΄κ°λ "LP"λ getAMMInfo κ²°κ³Όμμ λ°νλ lp_token.currency(16μ§ λ¬Έμμ΄)κ°μ λ£μ΄μΌ ν¨
const tx: Transaction = {
TransactionType: "AMMBid",
Account: user.address,
Asset: { currency: "XRP" },
Asset2: { currency: "USD", issuer: admin.address },
BidMax: {
currency: "LP", // ν μμ± μ λ°κΈλ LP ν ν° μ½λ (16μ§ λ¬Έμμ΄)
issuer: "rAMMInstance...", // AMM κ³μ μ£Όμ
value: "100" // μ
μ°°ν LPToken μλ
},
AuthAccounts: [
{ AuthAccount: { Account: "rOtherAccount..." } }
]
}
try {
const prepared = await client.autofill(tx)
const signed = user.sign(prepared)
const result = await client.submitAndWait(signed.tx_blob)
console.log("β
AMMBid Result:")
console.log(JSON.stringify(result, null, 2))
return result
} finally {
await client.disconnect()
console.log("π μ°κ²° μ’
λ£")
}
}
if (require.main === module) {
AMMBid().catch(e => {
console.error(e)
process.exit(1)
})
}
TypeScript
볡μ¬
Step 7. ν μμ (AMMDelete.ts)
β’
주체: ADMIN
β’
νλ: AMMDelete νΈλμμ
μ μ‘
β’
λ΄μ©: ν μ λμ±μ΄ λͺ¨λ μ κ±°λ μνμμ μΈμ€ν΄μ€ μμ
β’
κ²°κ³Ό: μμ₯μμ ν΄λΉ AMM μνΈλ¦¬ μ κ±°
import { Client, Wallet, Transaction } from "xrpl"
import path from "path"
import dotenv from "dotenv"
dotenv.config({ path: path.join(__dirname, "..", ".env") })
/**
* AMMDelete νΈλμμ
* - νμ λ¨μ μ λμ±μ΄ μ ν μμ λλ§ μ€ν κ°λ₯
* - AMM μΈμ€ν΄μ€λ₯Ό μμ₯μμ μ κ±°
*/
export async function AMMDelete() {
const client = new Client("wss://s.devnet.rippletest.net:51233")
await client.connect()
const ADMIN_SEED = process.env.ADMIN_SEED
if (!ADMIN_SEED) throw new Error("Missing env: ADMIN_SEED")
const admin = Wallet.fromSeed(ADMIN_SEED.trim())
/**
* β οΈ μμ: XRP / USD(IOU) ν μμ
* - Asset, Asset2: ν μμ± μ μ μνλ λμΌ μμ°μ
*/
const tx: Transaction = {
TransactionType: "AMMDelete",
Account: admin.address, // ν μμ±μ(νΉμ κ΄λ¦¬ κ³μ )
Asset: { currency: "XRP" },
Asset2: {
currency: "USD",
issuer: admin.address
}
}
try {
const prepared = await client.autofill(tx)
const signed = admin.sign(prepared)
const result = await client.submitAndWait(signed.tx_blob)
console.log("β
AMMDelete Result:")
console.log(JSON.stringify(result, null, 2))
return result
} finally {
await client.disconnect()
console.log("π μ°κ²° μ’
λ£")
}
}
// μ§μ μ€ν
if (require.main === module) {
AMMDelete().catch(e => {
console.error(e)
process.exit(1)
})
}
TypeScript
볡μ¬
μλλ¦¬μ€ μμ κ²°κ³Ό
β’
μ±κ³΅ μ
β¦
AMM μμ± β μ
κΈ/μΆκΈ β μ€μ β ν¬ν/κ²½λ§€ β μμ κΉμ§ μ μ λμ
β¦
Clawback ν ν°μ λ°νμκ° νμ κ°λ₯
β’
μ€ν¨ μ
β¦
μ λμ±μ΄ λ¨μμλ μνμμ μμ μλ β tecINCOMPLETE
β¦
Clawback λΉνμ± ν ν° β tecNO_PERMISSION
β¦
μλͺ»λ μμ° μ§μ β temMALFORMED
(μ΅μ 1) Clawback (AMMClawback.ts)
β’
주체: λ°νμ(ADMIN, Clawback κΆν 보μ )
β’
νλ: AMMClawback νΈλμμ
μ μ‘
β’
λ΄μ©: AMMμ μμΉλ Clawback νμ± ν ν°μ νΉμ Holder κ³μ μΌλ‘λΆν° νμ
β’
κ²°κ³Ό: λ°νμκ° μ§μ ν μλλ§νΌ ν ν°μ μ§κ°μΌλ‘ λμ°Ύμ
import { Client, Transaction, Wallet } from "xrpl"
import path from "path"
import dotenv from "dotenv"
dotenv.config({ path: path.join(__dirname, "..", ".env") })
export async function AMMClawback() {
const client = new Client("wss://s.devnet.rippletest.net:51233")
await client.connect()
const ADMIN_SEED = process.env.ADMIN_SEED
if (!ADMIN_SEED) throw new Error("Missing env: ADMIN_SEED")
const admin = Wallet.fromSeed(ADMIN_SEED.trim())
const tx: Transaction = {
TransactionType: "AMMClawback",
Account: admin.address,
Asset: { currency: "XRP", issuer: admin.address}, // XRPλ issuer νμ μμ (anyλ‘ μ°ν)
Asset2: { currency: "USD", issuer: admin.address },
Amount: {
currency: "USD",
issuer: admin.address,
value: "10"
},
Holder: "rSomeLiquidityProvider..." // β
clawback λμ κ³μ
}
try {
const prepared = await client.autofill(tx)
const signed = admin.sign(prepared)
const result = await client.submitAndWait(signed.tx_blob)
console.log("β
AMMClawback Result:")
console.log(JSON.stringify(result, null, 2))
return result
} finally {
await client.disconnect()
console.log("π μ°κ²° μ’
λ£")
}
}
if (require.main === module) {
AMMClawback().catch(e => { console.error(e); process.exit(1) })
}
TypeScript
볡μ¬
(μ΅μ 2) getAMMInfo (getAMMInfo.ts)
β’
주체: μ무λ
β’
νλ: amm_info RPC νΈμΆμ ν΅ν΄ νΉμ AMM ν μνλ₯Ό μ‘°ν
β’
λ΄μ©:
β¦
μμ°μ(Asset, Asset2)μ μ§μ νλ©΄ ν΄λΉ νμ μΈλΆ μ 보λ₯Ό λ°ν
β¦
ν μμ‘(amount, amount2), νμ¬ μμλ£μ¨(trading_fee), AMM κ³μ μ£Όμ(account) νμΈ κ°λ₯
β¦
νΉν lp_token νλμμ LP Tokenμ currency(16λ°μ΄νΈ hex μ½λ)μ issuer(ν μ μ© κ³μ μ£Όμ)λ₯Ό μ‘°ν κ°λ₯ β Withdraw/Clawback μ λ°λμ νμ
import { Client, Wallet } from "xrpl"
import path from "path"
import dotenv from "dotenv"
dotenv.config({ path: path.join(__dirname, "..", ".env") })
/**
* νΉμ AMM νμ μ 보 μ‘°ν
* - ν μν(μμ‘, TradingFee, LP Token λ±) νμΈ κ°λ₯
* - LP Token currency/issuer νμΈ ν Withdraw λ±μ νμ©
*/
export async function getAMMInfo() {
const client = new Client("wss://s.devnet.rippletest.net:51233")
await client.connect()
const ADMIN_SEED = process.env.ADMIN_SEED
if (!ADMIN_SEED) throw new Error("Missing env: ADMIN_SEED")
const admin = Wallet.fromSeed(ADMIN_SEED.trim())
// β οΈ μ‘°νν ν μμ°μ (μ: XRP + USD)
const req: any = {
command: "amm_info",
asset: { currency: "XRP" },
asset2: { currency: "USD", issuer: admin.address }
}
try {
const result: any = await client.request(req as any)
console.log("β
AMM Info Result:")
console.log(JSON.stringify(result.result, null, 2))
if (result.result?.amm?.lp_token) {
console.log("π LPToken Info:")
console.log(result.result.amm.lp_token)
}
return result.result
} finally {
await client.disconnect()
console.log("π μ°κ²° μ’
λ£")
}
}
// μ§μ μ€ν
if (require.main === module) {
getAMMInfo().catch(e => {
console.error(e)
process.exit(1)
})
}
TypeScript
볡μ¬
β’
κ²°κ³Ό:
β¦
μ±κ³΅ μ β νμ μ 체 μν λ° LP Token μ 보 λ°ν
β¦
"account": "rUd5wEYNLtC4NRoMEXDAmd8L9ASof8Hn18"
β XRPL μμ₯μ΄ νμ λ§λ€λ©΄μ μλ μμ±ν μ μ© AMM κ³μ
β¦
μ€ν¨ μ β μ‘΄μ¬νμ§ μλ μμ°μμ΄λ©΄ temBAD_AMM_ASSET μ€λ₯ λ°ν, μλͺ»λ issuer μ§μ μ temMALFORMED λ°μ
β
AMM Info Result:
{
"amm": {
"account": "rUd5wEYNLtC4NRoMEXDAmd8L9ASof8Hn18",
"amount": "1000000",
"amount2": {
"currency": "USD",
"issuer": "rER8ArNFu1bTgoAbNzCxUt9iMvTHWoPJus",
"value": "10"
},
"asset2_frozen": false,
"auction_slot": {
"account": "rER8ArNFu1bTgoAbNzCxUt9iMvTHWoPJus",
"discounted_fee": 3,
"expiration": "2025-09-01T13:51:32+0000",
"price": {
"currency": "03930D02208264E2E40EC1B0C09E4DB96EE197B1",
"issuer": "rUd5wEYNLtC4NRoMEXDAmd8L9ASof8Hn18",
"value": "0"
},
"time_interval": 10
},
"lp_token": {
"currency": "03930D02208264E2E40EC1B0C09E4DB96EE197B1",
"issuer": "rUd5wEYNLtC4NRoMEXDAmd8L9ASof8Hn18",
"value": "3162.277660168379"
},
"trading_fee": 30,
"vote_slots": [
{
"account": "rER8ArNFu1bTgoAbNzCxUt9iMvTHWoPJus",
"trading_fee": 30,
"vote_weight": 100000
}
]
},
"ledger_current_index": 5806953,
"validated": false
}
TypeScript
볡μ¬
μΆλ ₯ λ‘κ·Έ νλ¨(μ€μ) :
π LPToken Info:
{
currency: '03930D02208264E2E40EC1B0C09E4DB96EE197B1',
issuer: 'rUd5wEYNLtC4NRoMEXDAmd8L9ASof8Hn18',
value: '3162.277660168379'
}
TypeScript
볡μ¬

