[TOC]

Con el rápido aumento de los intercambios descentralizados (DEX) en el campo del comercio de criptomonedas, los comerciantes cuantitativos han comenzado gradualmente a recurrir a estas plataformas para realizar operaciones automatizadas eficientes. Como una de las plataformas de negociación descentralizadas más populares, dYdX ofrece potentes funciones de negociación y admite la negociación de contratos perpetuos de futuros. Su última versión v4 optimiza el rendimiento y la experiencia del usuario, lo que la convierte en la primera opción para muchos operadores cuantitativos.
Este artículo presentará cómo practicar el trading cuantitativo en dYdX v4, incluido cómo usar su API para operar, obtener datos de mercado y administrar cuentas.

dYdX v3De la misma manera, las transacciones generan recompensas, recompensasdYdXFichas.
El intercambio DEX del protocolo dYdX v3 anterior estuvo fuera de línea. La dirección actual de la aplicación dYdX v4 es:
Después de abrir la página de la aplicación, hay un botón para conectarse a la billetera en la esquina superior derecha. Escanee el código QR para conectarse a la billetera.
Si desea probar y familiarizarse primero con el entorno de red de prueba, puede utilizar la red de prueba:
Además, haga clic en el botón conectar billetera en la esquina superior derecha, escanee el código QR para conectarse a la billetera y verificar la firma. Una vez que la billetera se haya conectado correctamente, se generará automáticamente una dirección dydx v4. Esta dirección se mostrará en la esquina superior derecha de la página de la aplicación. Aparecerá un menú después de hacer clic en él. Estas incluyen operaciones como recarga, retiro y transferencia. Una de las diferencias entre la red principal de dYdX (entorno de producción) y la red de prueba es que cuando hace clic en el botón de recarga en la red de prueba, se depositarán automáticamente 300 activos USDC en la faucet para realizar pruebas. Si desea realizar transacciones reales en dYdX, debe depositar activos en USDC. La recarga también es muy conveniente y compatible con múltiples activos y cadenas.
Dirección de cuenta de dYdX v4
La dirección de la cuenta de dYdX v4 se deriva de la dirección de la billetera. La dirección de la cuenta de dYdX v4 se ve así:dydx1xxxxxxxxxxxxxxxxxxxxq2ge5jr4nzfeljxxxx, es la dirección que comienza con dydx1. Esta dirección se puede consultar en los exploradores de blockchain.
Mnemotécnica Puede exportar el mnemónico de la cuenta de dirección dYdX actual haciendo clic en el botón “Exportar contraseña” en el menú de la esquina superior derecha. Al agregar un intercambio en la plataforma FMZ, debe configurar este mnemónico.
Los mnemónicos se pueden configurar directamente en la plataforma FMZ o guardar localmente en el custodio. Al utilizar el objeto de intercambio dydx v4, se leerá el contenido del archivo que registra los mnemónicos, lo que se demostrará en la parte práctica de este artículo.
El entorno de la red de prueba es diferente del entorno de la red principal en algunos aspectos. A continuación se indican algunas diferencias simples.
Transferencia de activos de subcuenta.
La red principal tiene un mecanismo de limpieza de subcuentas.subAccountNumber >= 128Si la subcuenta con este ID no tiene posiciones, los activos se transferirán automáticamente a la subcuenta con subAccountNumber 0.
Durante las pruebas, se descubrió que la red de prueba no tenía dicho mecanismo (o las condiciones de activación eran diferentes y no se había activado en la red de prueba).
Algunos nombres simbólicos.
El token nativo dydx se llama de manera diferente: MainnetDYDX, red de pruebaDv4TNT
Configuración de dirección, como ID de cadena, dirección de nodo, dirección del indexador, etc. Hay muchos nodos y configuraciones, aquí está uno de ellos:
Red principal:
Dirección del indexador:https://indexer.dydx.trade
Identificación de la cadena:dydx-mainnet-1
Nodo REST:https://dydx-dao-api.polkachu.com:443
Red de prueba:
Dirección del indexador:https://indexer.v4testnet.dydx.exchange
Identificación de la cadena:dydx-testnet-4
Nodo REST:https://dydx-testnet-api.polkachu.com
El protocolo dYdX v4 se desarrolló sobre la base del ecosistema cosmos. El contenido relacionado con las transacciones del sistema DEX dYdX v4 consta principalmente de dos partes:
El servicio de indexación proporciona protocolos REST y Websocket.
Protocolo REST La interfaz del protocolo REST admite consultas de información de mercado, información de cuenta, información de posición, información de pedido, etc. y se ha encapsulado como una interfaz API unificada en la plataforma FMZ.
Protocolo WebSocket En la plataforma FMZ, puede utilizar la función Dial para crear una conexión Websocket y suscribirse a información del mercado.
Cabe señalar que el indexador dydx v4 tiene el mismo problema que el intercambio centralizado, es decir, las actualizaciones de datos no son tan oportunas. Por ejemplo, a veces es posible que no se encuentre el pedido al realizar una consulta inmediatamente después de realizarlo. Se recomienda que después de ciertas operaciones (Sleep(n)) Espere unos segundos antes de volver a consultar.
A continuación se muestra un ejemplo de cómo utilizar la función Dial para crear una conexión API de Websocket y suscribirse a los datos del libro de pedidos:
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() {
// real : wss://indexer.dydx.trade/v4/ws
// simulate : wss://indexer.v4testnet.dydx.exchange/v4/ws
var symbol = "ETH-USD"
var manager = dYdXIndexerWSconnManager("wss://indexer.dydx.trade/v4/ws")
manager.CreateWsThread({"type": "subscribe", "channel": "v4_orderbook", "id": symbol})
var redCode = "#FF0000"
var greenCode = "#006400"
while (true) {
var depthTbl = {type: "table", title: symbol + " / depth", cols: ["level", "price", "amount"], rows: []}
var depth = manager.Peek()
if (depth) {
for (var i = 0; i < depth.asks.length; i++) {
if (i > 9) {
break
}
var ask = depth.asks[i]
depthTbl.rows.push(["asks " + (i + 1) + greenCode, ask.price + greenCode, ask.size + greenCode])
}
depthTbl.rows.reverse()
for (var i = 0; i < depth.bids.length; i++) {
if (i > 9) {
break
}
var bid = depth.bids[i]
depthTbl.rows.push(["bids " + (i + 1) + redCode, bid.price + redCode, bid.size + redCode])
}
}
LogStatus(_D(), "\n`" + JSON.stringify(depthTbl) + "`")
}
}
Los mensajes más utilizados en las transacciones son los mensajes de pedido, los mensajes de cancelación de pedido y los mensajes de transferencia.
{
"@type": "/dydxprotocol.clob.MsgPlaceOrder",
"order": {
"orderId": {
"subaccountId": {
"owner": "xxx"
},
"clientId": xxx,
"orderFlags": 64,
"clobPairId": 1
},
"side": "SIDE_BUY",
"quantums": "2000000",
"subticks": "3500000000",
"goodTilBlockTime": 1742295981
}
}
Orden límite:
En la función de orden encapsulada en la plataforma FMZ, el valor orderFlags utilizado para órdenes limitadas es:ORDER_FLAGS_LONG_TERM = 64 # 长期订单De acuerdo con las limitaciones del protocolo DYDX v4, se utiliza el período de validez de pedido más largo, que es de 90 días (todos los tipos de pedidos en DYDX v4 tienen un período de validez).
Orden de mercado:
En la función de orden encapsulada en la plataforma FMZ, el valor orderFlags utilizado por la orden de mercado es:ORDER_FLAGS_SHORT_TERM = 0 # 短期订单, según las recomendaciones del protocolo DYDX v4:
// Recommend set to oracle price - 5% or lower for SELL, oracle price + 5% for BUY
Dado que no es una verdadera orden de mercado, se utiliza el precio del oráculo, más o menos un deslizamiento del 5% como orden de mercado. La configuración del período de validez de las órdenes a corto plazo también es diferente de la de las órdenes a largo plazo. Las órdenes a corto plazo utilizan el período de validez de la altura del bloque. De acuerdo con la recomendación de dydx v4, se establece en el bloque actual + 10 alturas de bloque. antes de que expire.
ID del pedido: Dado que la operación de pedido se realiza directamente en la cadena, el indexador no generará ningún ID de pedido después de que se transmita el mensaje, y el pedido del indexador no se puede usar como valor de retorno de la función de pedido de la plataforma. Para garantizar la unicidad En función del ID del pedido y de la precisión de la consulta del pedido, se devuelve el orden del indexador. El ID del pedido consta de la siguiente información (separada por comas):
Resumen del mensaje de cancelación de pedido
{
"@type": "/dydxprotocol.clob.MsgCancelOrder",
"orderId": {
"subaccountId": {
"owner": "xxx"
},
"clientId": 2585872024,
"orderFlags": 64,
"clobPairId": 1
},
"goodTilBlockTime": 1742295981
}
Es necesario pasar el ID de pedido devuelto por la interfaz de pedido de la plataforma FMZ.
{
"@type": "/dydxprotocol.sending.MsgCreateTransfer",
"transfer": {
"sender": {
"owner": "xxx"
},
"recipient": {
"owner": "xxx",
"number": 128
},
"amount": "10000000"
}
}
Se pueden crear muchas subcuentas con la dirección actual de dydx v4. La subcuenta con subAccountNumber 0 es la primera subcuenta creada automáticamente. El ID de subcuenta con subAccountNumber mayor o igual a 128 se utiliza para operaciones de posición aisladas. que requiere al menos 20 activos USDC. Por ejemplo, puede ir de subAccountNumber 0 -> 128, o de subAccountNumber 128 -> 0. La transferencia requiere el consumo de Tarifa de Gas. La tarifa de gas puede utilizar tokens USDC y dydx.
El contenido anterior explica brevemente algunos detalles del empaquetado. A continuación, practiquemos el uso específico. Aquí usamos la red de prueba dYdX v4 para la demostración. La red de prueba es básicamente la misma que la red principal y hay una faucet automática para recibir activos de prueba. . El custodio se despliega No entraré en detalles sobre la operación y crearé una prueba real en FMZ.
Después de conectarse exitosamente a la aplicación dYdX v4 usando una billetera de criptomonedas (yo uso la billetera imToken aquí), reclame sus activos de prueba y luego exporte el mnemónico para su cuenta dYdX v4 actual (derivada de su billetera).

Configurar el mnemónico en la plataforma FMZ. Aquí usamos el método de archivo local para configurarlo (también puede completarlo directamente y configurarlo en la plataforma. El mnemónico se configura después del cifrado, no en texto plano).

Colóquelo en el directorio de la carpeta de ID de disco real, debajo del directorio de custodia. Por supuesto, también se puede colocar en otros directorios (la ruta específica se debe escribir durante la configuración).
Complete el cuadro de edición mnemotécnica:file:///mnemonic.txt, la ruta real correspondiente es:托管者所在目录/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())
}
Lea la información de la cuenta de red de prueba:
{
"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
}
No cambié a la red de prueba, probé con la red principal
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])
}
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 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)
}

Página de la aplicación dYdX v4:

La red de prueba coloca dos pedidos por adelantado, prueba a obtener los pedidos pendientes actuales y cancela los pedidos.
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: "test GetOrders", cols: ["Id", "Price", "Amount", "DealAmount", "AvgPrice", "Status", "Type", "Offset", "ContractType"], 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: "test GetPosition/GetPositions", cols: ["Symbol", "Amount", "Price", "FrozenAmount", "Type", "Profit", "Margin", "ContractType", "MarginLevel"], 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")
// subAccountNumber 0 -> 128 : 20 USDC , Gas Fee 为 adv4tnt 即 dydx token
var ret = exchange.IO("transferUSDCToSubaccount", 0, 128, "adv4tnt", 20)
Log("ret:", ret)
// 切换到子账号subAccountNumber 128 ,读取账户信息检查
exchange.IO("subAccountNumber", 128)
var account = exchange.GetAccount()
Log("account:", account)
}

Cambie a la subcuenta cuyo subAccountNumber es 128 y los datos devueltos por GetAccount son:
{
"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
}
Se puede ver que la subcuenta con subAccountNumber 128 ha transferido 20 USDC.
De acuerdo con el orden, obtenga TxHash y pruebe el método de IO que llama al nodo REST
¿Cómo obtener el TxHash de un pedido? El objeto de intercambio dydx almacenará en caché el TxHash, que se puede consultar mediante el ID del pedido. Sin embargo, una vez que la estrategia se detiene, el mapa hash de la transacción de órdenes almacenada en caché se borrará.
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)
}
}

Mensajes consultados a través de TxHash:
var ret = exchange.IO(“api”, “GET”, “/cosmos/tx/v1beta1/txs/” + txHash)
El contenido es demasiado largo, por lo que a continuación se presentan algunos extractos a modo de demostración:
{
"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": []
},
...
Las pruebas anteriores se basan en el custodio más reciente. Debe descargar el custodio más reciente para admitir dYdX v4 DEX
Gracias por su apoyo y gracias por leer.