BitGo / BitGoJS

BitGo JavaScript SDK
https://developers.bitgo.com/
Apache License 2.0
346 stars 265 forks source link

Is there a way to move crypto funds out of the trading wallet? #3948

Open gndelia opened 9 months ago

gndelia commented 9 months ago

The guides and the docs are very misleading about this, providing different paths and responses, and I can't get any of them to work. Is there a way to move funds out of the Trading wallet? Or only Fiat USD can be moved out?

For example:

According to the wallets overview page, these are the steps we need to follow

image

When going to the build API pages and the build guides docs, we find different expected responses.

{
  "keyDerivationPath": "string"
}

I do not know what to do with it

According to the second one (The guide to initiate transactions), the response should

image

The next step, according to this guide, should be sending the transactions (Guide about it here)

However, when actually calling the /tx/build endpoint, I get none of the responses above ☝🏽 , but a different one.

This is the Body I sent (Trying to withdraw some USDC from the trading account)

{
  "recipients": [
      {
      "address": "<public-address-of-my-bitgo-wallet>",
      "amount": "12061478"
      }
    ]
 }

and this is the response I get

{
  "payload": '{"coin":"ofcusdc","recipients":[{"address":"<public-address>","amount":"12061478"}],"fromAccount":"<walletID>","nonce":"<nonce>","timestamp":"2023-09-19T21:03:04.345Z","feeString":"0","shortCircuitBlockchainTransfer":false,"isIntraJXTransfer":false}',
  "feeInfo": { feeString: '0' },
  "coin": "ofc",
  "token": "ofcusdc"
}

This ☝🏽 is not documented anywhere. What should I do with it? There's a guide to sign transactions but there's no option that documents how to sign with the Trading Wallet

image

Unless the trading wallet is considered a Multisig cold wallet. In that case, should I use the offline Vault console? There's no mention in the OVC pages about the trading wallet

If we check the /send endpoint (linked above), there's no mention of any payload or anything. However, the screenshot with the "Transaction flow" mentions that /tx/build creates an unsigned transaction, and then in /send the user must send a half-signed transaction. How can we sign this payload? Is there a way? Just converting the payload to HEx does not work (at least for me). I just convert the payload to Hex, and send it as the txHex property the guides specify, I just get

{
  error: 'missing tx payload',
  name: 'Invalid',
  requestId: '<request-id>,
  context: {}
}

I'm testing all of this on mainnet because Bitgo support has told me that withdrawals from the Trading Account in testnet are not enabled.

I've exchanged several emails with support, with no response. I've also tried in their Discord, with no response either.

Thanks in Advance

gndelia commented 9 months ago

I finally figured it out by downloading the code and running the signing docker image on my local machine. Their support centre was barely helpful, so I'm leaving this for anyone who needs it in the future. In order to move funds out of the trading wallet, to a given public address, you can follow these steps:

Build TX

Call the build tx endpoint.

fetch(`${bitgoUrl}/api/v2/${coin}/wallet/${tradingWalletId}/tx/build`, {
  body: {
    recipients: [
          {
            address: destinationPublicAddress, // 0x...
            amount // (for 1 usdc, use 1000000)
          }
        ]
  },
  method: 'POST'
})

This will return an object with a payload string, among other properties

{
  "payload": "<json-object-stringified>"
}

Sign the payload object

This was the step that I was missing when I created this issue. I don't know how to get the key to sign with Bitgo's endpoint, but their UI is doing so. However, what I did, and you can do too if you're using the bitgo express docker image is to call a non-documented endpoint that does all the work for you. You can see the code of this endpoint here.

fetch(`${bitgoExpressUrl}/api/v2/ofc/signPayload', {
  body: {
    payload: JSON.parse(payload), // payload is the string object we got from the previous call
    walletId: tradingWalletId
  },
  method: 'POST'
})

This will return an object with the payload (again) and the signature

{
  "payload": "<json-object-stringified>",
  "signature": "payload-signature-in-hex" 
}

This endpoint IS NOT DOCUMENTED (so use it at your own risk), but it was the key for me to solve this problem without having to load and decrypt keys.

You also keep in mind that the Bitgo Express container must be run in production mode (as withdrawals from testing env are not enabled - See how to run in production mode here. In addition to that, keep in mind that even though the docs do not mention it, you must set BITGO_ENV=production rather than NODE_ENV=production) and the passphrase for the Trading wallet must be set as an env variable. See this on how to set the passphrase (you may need to use the Bitgo login account if it does not work.. awful, but it is what it is)

Send the payload and signature

This step was documented, but incorrectly . We need to call the tx/send endpoint, but following the example below 👇🏽 because the docs are incorrect as of 10/2023

fetch(`${bitgoUrl}/api/v2/ofcusdc/wallet/${tradingWalletId}/tx/send`, {
  body: {
    halfSigned: {
      payload: '<json-object-stringified>',
      signature: 'signature-from-previous-call'
    }
  },
  method: 'POST'
})

The docs INCORRECTLY tell you to send a txHex property, either as part of the body object or inside the halfSigned property. But that did not work for me.

This will create the transfer, and if required you will get an email invitation for a video call.

Important notes

Keep in mind you may also need to whitelist the destination address as a withdrawal address - you can do so from the UI

You also be aware that all requests above require the accessToken as a Bearer authorization header, with the proper spending limits set - omitted here for simplicity