[TOC]
탈중앙화 거래소(DEX)가 암호화폐 거래 분야에서 빠르게 성장하면서 퀀트 트레이더들도 이러한 플랫폼을 활용한 효율적인 자동화 거래로 전환하고 있습니다. dYdX는 가장 인기 있는 탈중앙화 거래 플랫폼 중 하나로, 강력한 거래 기능과 영구 선물 거래를 지원하며 최신 버전인 v4에서는 성능과 사용자 경험이 더욱 개선되어 많은 알고리즘 트레이더들의 주력 플랫폼으로 자리매김했습니다.
본 아티클에서는 dYdX v4에서의 알고리즘 트레이딩 실전 적용 방법을 소개합니다. API를 이용한 거래 실행, 시장 데이터 수집, 계좌 관리 등의 핵심 내용을 다룹니다.
dYdX v3
와 마찬가지로 거래 시 dYdX
토큰 보상이 발생합니다.기존 dYdX v3 프로토콜 DEX 거래소는 서비스가 종료되었으며, 현재 dYdX v4 앱 주소는 다음과 같습니다:
앱 페이지 접속 후 우측 상단의 지갑 연결 버튼을 통해 QR 코드 스캔으로 지갑을 연결할 수 있습니다.
테스트넷 환경에서 먼저 기능을 익히고 싶은 경우 다음 테스트넷을 이용하시기 바랍니다:
동일하게 우측 상단 지갑 연결 버튼을 클릭한 후 서명 인증을 완료하면 됩니다. 지갑 연결 성공 시 자동으로 dydx v4 주소가 생성되며, 앱 페이지 우측 상단에 해당 주소가 표시됩니다. 해당 주소 클릭 시 입금, 출금, 자산 이체 등의 메뉴가 나타납니다. dYdX 메인넷(프로덕션 환경)과 테스트넷의 주요 차이점 중 하나는 테스트넷에서 입금 버튼 클릭 시 자동으로 faucet을 통해 300 USDC 테스트 자금이 충전된다는 점입니다. 실제 거래를 위해서는 USDC 자금을 직접 입금해야 하며, 다양한 자산과 체인을 통한 입금이 지원됩니다.
dYdX v4 계좌 주소
dYdX v4 계좌 주소는 지갑 주소에서 파생되며, dydx1xxxxxxxxxxxxxxxxxxxxq2ge5jr4nzfeljxxxx
형식의 ‘dydx1’ 접두사로 시작하는 주소입니다. 해당 주소는 블록체인 탐색기에서 조회 가능합니다.
니모닉 구문 우측 상단 메뉴에서 ‘비밀 복구 구문 내보내기’ 버튼을 클릭하면 현재 dYdX 계좌의 니모닉 구문을 확인할 수 있습니다. FMZ 플랫폼에 거래소를 추가할 때 이 니모닉 구문 설정이 필요합니다.
니모닉 구문은 FMZ 플랫폼에 직접 설정하거나 호스팅 머신에 로컬 저장할 수 있으며, 본문의 실전 예제에서 관련 사용법을 시연합니다.
테스트넷 환경은 메인넷과 몇 가지 측면에서 차이가 존재합니다. 주요 차이점을 간략히 정리합니다.
서브 계좌 자산 이체
메인넷은 subAccountNumber >= 128
인 서브 계좌에서 포지션이 없을 경우 자동으로 subAccountNumber 0 계좌로 자산을 이체하는 청소 메커니즘이 존재합니다.
테스트넷에서는 해당 메커니즘이 없거나(혹은 트리거 조건이 상이하여) 테스트 과정에서 작동하지 않았습니다.
일부 토큰 명칭
네이티브 토큰 dydx의 명칭 차이: 메인넷 DYDX
vs 테스트넷 Dv4TNT
주소 설정(체인 ID, 노드 주소, 인덱서 주소 등) 주요 설정값 예시:
메인넷:
인덱서 주소: https://indexer.dydx.trade
체인 ID: dydx-mainnet-1
REST 노드: https://dydx-dao-api.polkachu.com:443
테스트넷:
인덱서 주소: https://indexer.v4testnet.dydx.exchange
체인 ID: dydx-testnet-4
REST 노드: https://dydx-testnet-api.polkachu.com
dYdX v4 프로토콜은 Cosmos 생태계 기반으로 개발되었으며, dYdX v4 DEX 시스템의 거래 관련 구성 요소는 크게 2가지로 구분됩니다:
인덱서 서비스는 REST 프로토콜과 WebSocket 프로토콜을 제공합니다.
REST 프로토콜 REST 인터페이스를 통해 시장 정보 조회, 계좌 정보, 포지션 정보, 주문 정보 확인이 가능하며 FMZ 플랫폼에서는 표준화된 API 인터페이스로 래핑되어 있습니다.
WebSocket 프로토콜 FMZ 플랫폼에서는 Dial 함수를 이용해 WebSocket 연결을 생성하고 시장 정보 등을 구독할 수 있습니다.
dydx v4 인덱서는 중앙화 거래소와 유사한 데이터 갱신 지연 이슈가 존재합니다. 예를 들어 주문 후 즉시 조회 시 주문 정보가 나타나지 않을 수 있으므로, 특정 작업 후 Sleep(n)
함수를 이용해 수 초간 대기할 것을 권장합니다.
다음은 Dial 함수를 이용해 WebSocket API 연결을 생성하고 오더북 데이터를 구독하는 예시 코드입니다:
function dYdXIndexerWSconnManager(streamingPoint) {
var self = {}
self.base = streamingPoint
self.wsThread = null
// 구독
self.CreateWsThread = function (msgSubscribe) {
self.wsThread = threading.Thread(function (streamingPoint, msgSubscribe) {
// 오더북
var orderBook = null
// 오더북 업데이트
var updateOrderbook = function(orderbook, update) {
// bids 업데이트
if (update.bids) {
update.bids.forEach(([price, size]) => {
const priceFloat = parseFloat(price)
const sizeFloat = parseFloat(size)
if (sizeFloat === 0) {
// price에 해당하는 매수 주문 삭제
orderbook.bids = orderbook.bids.filter(bid => parseFloat(bid.price) !== priceFloat)
} else {
// 매수 주문 업데이트 또는 추가
orderbook.bids = orderbook.bids.filter(bid => parseFloat(bid.price) !== priceFloat)
orderbook.bids.push({price: price, size: size})
// 가격 기준 내림차순 정렬
orderbook.bids.sort((a, b) => parseFloat(b.price) - parseFloat(a.price))
}
})
}
// asks 업데이트
if (update.asks) {
update.asks.forEach(([price, size]) => {
const priceFloat = parseFloat(price)
const sizeFloat = parseFloat(size)
if (sizeFloat === 0) {
// 가격이 price인 매도 주문 삭제
orderbook.asks = orderbook.asks.filter(ask => parseFloat(ask.price) !== priceFloat)
} else {
// 매도 주문 업데이트 또는 추가
orderbook.asks = orderbook.asks.filter(ask => parseFloat(ask.price) !== priceFloat)
orderbook.asks.push({price: price, size: size})
// 가격 기준 오름차순 정렬
orderbook.asks.sort((a, b) => parseFloat(a.price) - parseFloat(b.price))
}
})
}
return orderbook
}
var conn = Dial(`${streamingPoint}|reconnect=true&payload=${JSON.stringify(msgSubscribe)}`)
if (!conn) {
Log("createWsThread failed.")
return
}
while (true) {
var data = conn.read()
if (data) {
var msg = null
try {
msg = JSON.parse(data)
if (msg["type"] == "subscribed") {
orderBook = msg["contents"]
threading.currentThread().postMessage(orderBook)
} else if (msg["type"] == "channel_data") {
orderBook = updateOrderbook(orderBook, msg["contents"])
threading.currentThread().postMessage(orderBook)
}
} catch (e) {
Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message)
}
}
}
}, streamingPoint, msgSubscribe)
}
// 리스닝
self.Peek = function () {
return self.wsThread.peekMessage()
}
return self
}
function main() {
// 실서버 : wss://indexer.dydx.trade/v4/ws
// 테스트넷 : wss://indexer.v4testnet.dydx.exchange/v4/ws
var symbol = "ETH-USD"
var manager = dYdXIndexerWSconnManager("wss://indexer.dydx.trade/v4/ws")
매니저.크리에이트Ws스레드({"type": "subscribe", "channel": "v4_orderbook", "id": 심볼})
var 레드코드 = "#FF0000"
var 그린코드 = "#006400"
while (true) {
var 뎁스테이블 = {type: "table", title: 심볼 + " / 뎁스", cols: ["레벨", "가격", "수량"], rows: []}
var 뎁스 = 매니저.픽()
if (뎁스) {
for (var i = 0; i < 뎁스.asks.length; i++) {
if (i > 9) {
break
}
var ask = 뎁스.asks[i]
뎁스테이블.rows.push(["매도호가 " + (i + 1) + 그린코드, ask.price + 그린코드, ask.size + 그린코드])
}
뎁스테이블.rows.reverse()
for (var i = 0; i < 뎁스.bids.length; i++) {
if (i > 9) {
break
}
var bid = 뎁스.bids[i]
뎁스테이블.rows.push(["매수호가 " + (i + 1) + 레드코드, bid.price + 레드코드, bid.size + 레드코드])
}
}
로그상태(_D(), "\n`" + JSON.stringify(뎁스테이블) + "`")
}
}
거래에서 가장 빈번히 사용되는 메시지 유형은 주문 메시지, 주문 취소 메시지, 자금 이체 메시지입니다.
주문 메시지 요약
{
"@type": "/dydxprotocol.clob.MsgPlaceOrder",
"order": {
"orderId": {
"subaccountId": {
"owner": "xxx"
},
"clientId": xxx,
"orderFlags": 64,
"clobPairId": 1
},
"side": "SIDE_BUY",
"quantums": "2000000",
"subticks": "3500000000",
"goodTilBlockTime": 1742295981
}
}
ORDER_FLAGS_LONG_TERM = 64 # 롱텀 오더
로 설정됩니다. dydx v4 프로토콜 제약에 따라 최대 유효기간(90일)이 적용되며, 모든 dydx v4 오더 유형은 유효기간을 갖습니다.ORDER_FLAGS_SHORT_TERM = 0 # 숏텀 오더
로 설정됩니다. dydx v4 프로토콜 권장사항:// 매도의 경우 오라클 가격 -5%, 매수의 경우 오라클 가격 +5%로 설정 권장
진정한 의미의 마켓 오더가 아니므로 오라클 가격에 5% 슬리피지를 적용합니다. 숏텀 오더의 유효기간 설정은 블록 높이 기반으로, 현재 블록 +10 블록 높이로 설정됩니다.
오더 ID: 체인에서 직접 실행되는 주문 특성상 인덱서 생성 오더 ID가 존재하지 않습니다. 고유성 보장 및 조회 정확성을 위해 다음 요소들을 영문 콤마로 구분하여 구성합니다:
주문 취소 메시지 요약
{
"@type": "/dydxprotocol.clob.MsgCancelOrder",
"orderId": {
"subaccountId": {
"owner": "xxx"
},
"clientId": 2585872024,
"orderFlags": 64,
"clobPairId": 1
},
"goodTilBlockTime": 1742295981
}
FMZ 플랫폼 주문 인터페이스에서 반환된 오더 ID를 입력해야 합니다.
{
"@type": "/dydxprotocol.sending.MsgCreateTransfer",
"transfer": {
"sender": {
"owner": "xxx"
},
"recipient": {
"owner": "xxx",
"number": 128
},
"amount": "10000000"
}
}
dydx v4 주소 하위에 다수 서브어카운트 생성 가능하며, subAccountNumber 0은 최초 생성된 메인 어카운트입니다. 128 이상의 서브어카운트는 격리 마진 거래에 사용되며 최소 20 USDC 자금이 필요합니다. 예시: subAccountNumber 0 -> 128 이체 또는 128 -> 0 이체 가능. 이체 시 가스비 발생하며 USDC 또는 dydx 토큰으로 결제 가능합니다.
앞서 설명한 래핑 세부사항을 바탕으로 구체적인 사용 방법을 실습해 보겠습니다. 본 가이드에서는 dYdX v4 테스트넷을 데모 환경으로 사용하며, 테스트넷은 메인넷과 거의 동일한 구조를 가지며 자동 화이트 기능으로 테스트 자산을 획득할 수 있습니다. 호스팅 서비스 배포 과정은 생략하고 FMZ 플랫폼에서 실제 거래 테스트를 진행합니다.
암호화폐 지갑(본 예시에서는 imToken 지갑 사용)으로 dYdX v4 앱에 성공적으로 연결한 후 테스트 자산을 수령하고, 현재 dYdX v4 계정(지갑에서 파생된)의 니모닉 구문을 추출합니다.
추출한 니모닉 구문을 FMZ 플랫폼에 설정합니다. 본 예시에서는 로컬 파일 방식을 사용하며(직접 입력도 가능, 플랫폼에 암호화된 형태로 설정되므로 평문 노출 없음):
호스팅 서비스 디렉토리 내 실제 거래 ID 폴더에 위치시킵니다(다른 디렉토리 사용 시 경로 지정 필요).
니모닉 구문 입력란에 file:///mnemonic.txt
작성. 실제 경로: 호스팅_서비스_디렉토리/logs/storage/594291
.
function main() {
// 테스트넷 체인의 인덱서 주소로 전환
exchange.SetBase("https://indexer.v4testnet.dydx.exchange")
// 테스트넷 체인의 ChainId 설정
exchange.IO("chainId", "dydx-testnet-4")
// 테스트넷 체인의 REST 노드 주소 전환
exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")
// 계정 정보 읽기 테스트
Log(exchange.GetAccount())
}
테스트넷 계정 정보 확인 결과:
{
"Info": {
"subaccounts": [{
"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez",
"subaccountNumber": 0,
"equity": "300.386228",
"latestProcessedBlockHeight": "28193227",
"freeCollateral": "300.386228",
"openPerpetualPositions": {},
"assetPositions": {
"USDC": {
"subaccountNumber": 0,
"size": "300.386228",
"symbol": "USDC",
"side": "LONG",
"assetId": "0"
}
},
"marginEnabled": true,
"updatedAtHeight": "28063818"
}, {
"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez",
"equity": "0",
"freeCollateral": "0",
"openPerpetualPositions": {},
"marginEnabled": true,
"subaccountNumber": 1,
"assetPositions": {},
"updatedAtHeight": "27770289",
"latestProcessedBlockHeight": "28193227"
}, {
"equity": "0",
"openPerpetualPositions": {},
"marginEnabled": true,
"updatedAtHeight": "28063818",
"latestProcessedBlockHeight": "28193227",
"subaccountNumber": 128,
"freeCollateral": "0",
"assetPositions": {},
"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez"
}],
"totalTradingRewards": "0.021744179376211564"
},
"Stocks": 0,
"FrozenStocks": 0,
"Balance": 300.386228,
"FrozenBalance": 0,
"Equity": 300.386228,
"UPnL": 0
}
메인넷 환경에서 테스트 수행 (테스트넷 미전환 상태)
function main() {
var markets = exchange.GetMarkets()
if (!markets) {
throw "get markets error"
}
var tbl = {type: "table", title: "test markets", cols: ["key", "Symbol", "BaseAsset", "QuoteAsset", "TickSize", "AmountSize", "PricePrecision", "AmountPrecision", "MinQty", "MaxQty", "MinNotional", "MaxNotional", "CtVal"], rows: []}
for (var symbol in markets) {
var market = markets[symbol]
tbl.rows.push([symbol, market.Symbol, market.BaseAsset, market.QuoteAsset, market.TickSize, market.AmountSize, market.PricePrecision, market.AmountPrecision, market.MinQty, market.MaxQty, market.MinNotional, market.MaxNotional, market.CtVal])
}
```js
function main() {
// 테스트넷 인덱서 주소 변경
exchange.SetBase("https://indexer.v4testnet.dydx.exchange")
// 테스트넷 ChainId 설정
exchange.IO("chainId", "dydx-testnet-4")
// 테스트넷 REST 노드 주소 변경
exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")
// 지정가 주문, 매도 주문
var idSell = exchange.CreateOrder("ETH_USD.swap", "sell", 4000, 0.002)
var idBuy = exchange.CreateOrder("ETH_USD.swap", "buy", 3000, 0.003)
// 시장가 주문
var idMarket = exchange.CreateOrder("ETH_USD.swap", "buy", -1, 0.01)
Log("idSell:", idSell)
Log("idBuy:", idBuy)
Log("idMarket:", idMarket)
}
dYdX v4 앱 페이지:
테스트넷에 미체결 주문 조회 및 취소 테스트:
function main() {
// 테스트넷 인덱서 주소 변경
exchange.SetBase("https://indexer.v4testnet.dydx.exchange")
// 테스트넷 ChainId 설정
exchange.IO("chainId", "dydx-testnet-4")
// 테스트넷 REST 노드 주소 변경
exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")
var orders = exchange.GetOrders()
Log("orders:", orders)
for (var order of orders) {
exchange.CancelOrder(order.Id, order)
Sleep(2000)
}
var tbl = {type: "table", title: "GetOrders 테스트", cols: ["주문ID", "가격", "수량", "체결수량", "평균가", "상태", "유형", "오프셋", "계약유형"], rows: []}
for (var order of orders) {
tbl.rows.push([order.Id, order.Price, order.Amount, order.DealAmount, order.AvgPrice, order.Status, order.Type, order.Offset, order.ContractType])
}
LogStatus("`" + JSON.stringify(tbl) + "`")
}
function main() {
// 테스트넷 인덱서 주소 변경
exchange.SetBase("https://indexer.v4testnet.dydx.exchange")
// 테스트넷 ChainId 설정
exchange.IO("chainId", "dydx-testnet-4")
// 테스트넷 REST 노드 주소 변경
exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")
var p1 = exchange.GetPositions("USD.swap")
var p2 = exchange.GetPositions("ETH_USD.swap")
var p3 = exchange.GetPositions()
var p4 = exchange.GetPositions("SOL_USD.swap")
var tbls = []
for (var positions of [p1, p2, p3, p4]) {
var tbl = {type: "table", title: "GetPosition/GetPositions 테스트", cols: ["심볼", "수량", "가격", "동결수량", "유형", "수익", "증거금", "계약유형", "증거금률"], rows: []}
for (var p of positions) {
tbl.rows.push([p.Symbol, p.Amount, p.Price, p.FrozenAmount, p.Type, p.Profit, p.Margin, p.ContractType, p.MarginLevel])
}
tbls.push(tbl)
}
LogStatus("`" + JSON.stringify(tbls) + "`")
}
function main() {
// 테스트넷 인덱서 주소 변경
exchange.SetBase("https://indexer.v4testnet.dydx.exchange")
// 테스트넷 ChainId 설정
exchange.IO("chainId", "dydx-testnet-4")
// 테스트넷 REST 노드 주소 변경
exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")
// 서브 계정 번호 0 -> 128 : 20 USDC 전송, Gas Fee는 adv4tnt (dydx 토큰)
var ret = exchange.IO("transferUSDCToSubaccount", 0, 128, "adv4tnt", 20)
Log("ret:", ret)
// 서브 계정 번호 128로 전환 후 계좌 정보 조회
exchange.IO("subAccountNumber", 128)
var account = exchange.GetAccount()
Log("account:", account)
}
subAccountNumber 128 서브 계정으로 전환 후 GetAccount 반환 데이터:
{
"Info": {
"subaccounts": [{
"subaccountNumber": 0,
"assetPositions": {
"USDC": {
"size": "245.696892",
"symbol": "USDC",
"side": "LONG",
"assetId": "0",
"subaccountNumber": 0
}
},
"updatedAtHeight": "28194977",
"latestProcessedBlockHeight": "28195008",
"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez",
"freeCollateral": "279.5022142346",
"openPerpetualPositions": {
"ETH-USD": {
"closedAt": null,
"size": "0.01",
"maxSize": "0.01",
"exitPrice": null,
"unrealizedPnl": "-0.17677323",
"subaccountNumber": 0,
"status": "OPEN",
"createdAt": "2024-12-26T03:36:09.264Z",
"createdAtHeight": "28194494",
"sumClose": "0",
"netFunding": "0",
"market": "ETH-USD",
"side": "LONG",
"entryPrice": "3467.2",
"realizedPnl": "0",
"sumOpen": "0.01"
}
},
"marginEnabled": true,
"equity": "280.19211877"
}, {
"openPerpetualPositions": {},
"assetPositions": {},
"marginEnabled": true,
"latestProcessedBlockHeight": "28195008",
"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez",
"subaccountNumber": 1,
"equity": "0",
"freeCollateral": "0",
"updatedAtHeight": "27770289"
}, {
"openPerpetualPositions": {},
"updatedAtHeight": "28194977",
"latestProcessedBlockHeight": "28195008",
"address": "dydx1fzsndj35a26maujxff88q2ge5jr4nzfeljn2ez",
"subaccountNumber": 128,
"assetPositions": {
"USDC": {
"assetId": "0",
"subaccountNumber": 128,
"size": "20",
"symbol": "USDC",
"side": "LONG"
}
},
"marginEnabled": true,
"equity": "20",
"freeCollateral": "20"
}],
"totalTradingRewards": "0.021886899964446858"
},
"Stocks": 0,
"FrozenStocks": 0,
"Balance": 20,
"FrozenBalance": 0,
"Equity": 20,
"UPnL": 0
}
subAccountNumber 128 서브 계정에 20 USDC가 입금된 것을 확인할 수 있습니다.
주문 기준 TxHash 획득 방법, IO를 통한 REST 노드 호출 테스트
주문의 TxHash 획득 방법: 거래소 객체 dydx는 TxHash를 캐시하며 주문 ID로 조회 가능합니다. 단 전략 중단 시 캐시된 주문 tx 해시 매핑은 초기화됩니다.
function main() {
// 테스트넷 인덱서 주소 변경
exchange.SetBase("https://indexer.v4testnet.dydx.exchange")
// 테스트넷 ChainId 설정
exchange.IO("chainId", "dydx-testnet-4")
// 테스트넷 REST 노드 주소 변경
exchange.IO("restApiBase", "https://dydx-testnet-api.polkachu.com")
var id1 = exchange.CreateOrder("ETH_USD.swap", "buy", 3000, 0.002)
var hash1 = exchange.IO("getTxHash", id1)
Log("id1:", id1, "hash1:", hash1)
var id2 = exchange.CreateOrder("ETH_USD.swap", "buy", 2900, 0.003)
var hash2 = exchange.IO("getTxHash", id2)
Log("id2:", id2, "hash2:", hash2)
// 매핑 테이블 초기화는 다음을 사용: exchange.IO("getTxHash", "")
var arr = [hash1, hash2]
Sleep(10000)
for (var txHash of arr) {
// GET https://docs.cosmos.network /cosmos/tx/v1beta1/txs/{hash}
var ret = exchange.IO("api", "GET", "/cosmos/tx/v1beta1/txs/" + txHash)
Log("ret:", ret)
}
}
TxHash 조회를 통해 확인된 메시지:
var ret = exchange.IO(“api”, “GET”, “/cosmos/tx/v1beta1/txs/” + txHash)
내용이 너무 길어 일부만 발췌하여 예시:
{
"tx_response": {
"codespace": "",
"code": 0,
"logs": [],
"info": "",
"height": "28195603",
"data": "xxx",
"raw_log": "",
"gas_wanted": "-1",
"gas_used": "0",
"tx": {
"@type": "/cosmos.tx.v1beta1.Tx",
"body": {
"messages": [{
"@type": "/dydxprotocol.clob.MsgPlaceOrder",
"order": {
"good_til_block_time": 1742961542,
"condition_type": "CONDITION_TYPE_UNSPECIFIED",
"order_id": {
"clob_pair_id": 1,
"subaccount_id": {
"owner": "xxx",
"number": 0
},
"client_id": 2999181974,
"order_flags": 64
},
"side": "SIDE_BUY",
"quantums": "3000000",
"client_metadata": 0,
"conditional_order_trigger_subticks": "0",
"subticks": "2900000000",
"time_in_force": "TIME_IN_FORCE_UNSPECIFIED",
"reduce_only": false
}
}],
"memo": "FMZ",
"timeout_height": "0",
"extension_options": [],
"non_critical_extension_options": []
},
...
상기 테스트는 최신 호스팅 버전을 기반으로 수행되었으며, dYdX v4 DEX 지원을 위해 최신 호스팅 프로그램 다운로드가 필요합니다
지원해 주셔서 감사합니다. 감사합니다.