Search code examples
nearprotocol

Are transactions in a batch serialized?


if I send a transaction with 2 contract fn calls, does the 2nd gets executed if the 1st fails?

sample code:

const actions = [
  TX.functionCall("select_staking_pool", { staking_pool_account_id: stakingPool }, this.BASE_GAS.muln(3), new BN(0)),
  TX.functionCall("deposit_and_stake", { amount: near.ntoy(amountNear) }, this.BASE_GAS.muln(5), new BN(0))
]

return near.broadcast_tx_commit_actions(actions, sender, lockupContract, privateKey)

I did execute this batch, but the result gives me no clear answer: https://explorer.testnet.near.org/transactions/DcchgGdzAVEvNqRReHkjsmrTr5PoaMoibs3fyKoKBYSe

The 1st fn call code uses cross-contract calls and a callback code to check if the contract is whitelisted, but I can't see the callback's log code in the receipt logs. I assume the 1st call fails because the second fails with the error: "staking pool is not selected"

Here's the full result:

{
  "status": {
    "Failure": {
      "ActionError": {
        "index": 1,
        "kind": {
          "FunctionCallError": {
            "HostError": {
              "GuestPanic": {
                "panic_msg": "panicked at 'Staking pool is not selected', src/internal.rs:90:9"
              }
            }
          }
        }
      }
    }
  },
  "transaction": {
    "signer_id": "lucio.testnet",
    "public_key": "ed25519:Cvqie7SJ6xmLNA5KoTAYoUAkhD25KaJLG6N9oSmzT9FK",
    "nonce": 33,
    "receiver_id": "274e981786efcabbe87794f20348c1b2af6e7963.lockupy.testnet",
    "actions": [
      {
        "FunctionCall": {
          "method_name": "select_staking_pool",
          "args": "eyJzdGFraW5nX3Bvb2xfYWNjb3VudF9pZCI6Im5vcnRoZXJubGlnaHRzLnN0YWtpbmdwb29sIn0=",
          "gas": 75000000000000,
          "deposit": "0"
        }
      },
      {
        "FunctionCall": {
          "method_name": "deposit_and_stake",
          "args": "eyJhbW91bnQiOiIxMDk0OTkwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn0=",
          "gas": 125000000000000,
          "deposit": "0"
        }
      }
    ],
    "signature": "ed25519:gzCsrcobVtjuD6FE4qDhgo9zdk2feBp6dfa66NZYLPTnwypF7g8mDM7yubcnkVcF1sPzEFeEfJtQ4hwwnhrxTa6",
    "hash": "EjiQBjzF6Ea74wSHkjmZMkhTGwgYxbUdeVKUvzaA8X7k"
  },
  "transaction_outcome": {
    "proof": [],
    "block_hash": "3xEuw43E1W4FP2q1Un6XLsDSbhRc7c7FMtGWWL6i3KJj",
    "id": "EjiQBjzF6Ea74wSHkjmZMkhTGwgYxbUdeVKUvzaA8X7k",
    "outcome": {
      "logs": [],
      "receipt_ids": [
        "75pJCVuheYCjX82RspQzoKm5v7NSG1QNUaPK7DRzZYNt"
      ],
      "gas_burnt": 4748079879222,
      "tokens_burnt": "474807987922200000000",
      "executor_id": "lucio.testnet",
      "status": {
        "SuccessReceiptId": "75pJCVuheYCjX82RspQzoKm5v7NSG1QNUaPK7DRzZYNt"
      }
    }
  },
  "receipts_outcome": [
    {
      "proof": [
        {
          "hash": "55YXLhqDc63f8n4MdZNwi6TdmtFkaYp18wKwm7qFmDo5",
          "direction": "Left"
        }
      ],
      "block_hash": "DB3p49tiSBfRLv5diDUStajtDiMNs2KroKfRcr2cQDnV",
      "id": "75pJCVuheYCjX82RspQzoKm5v7NSG1QNUaPK7DRzZYNt",
      "outcome": {
        "logs": [
          "Selecting staking pool @northernlights.stakingpool. Going to check whitelist first."
        ],
        "receipt_ids": [
          "HXStMQuf5aWKVpWDM1Hf4EdA3S4FLTGjmYaF2WAYRcUz"
        ],
        "gas_burnt": 20291176638173,
        "tokens_burnt": "2029117663817300000000",
        "executor_id": "274e981786efcabbe87794f20348c1b2af6e7963.lockupy.testnet",
        "status": {
          "Failure": {
            "ActionError": {
              "index": 1,
              "kind": {
                "FunctionCallError": {
                  "HostError": {
                    "GuestPanic": {
                      "panic_msg": "panicked at 'Staking pool is not selected', src/internal.rs:90:9"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    {
      "proof": [],
      "block_hash": "2kua8s7vjix2MiMHZ7Rzbrhyv8UbGFEpvWeT67H2qCYM",
      "id": "HXStMQuf5aWKVpWDM1Hf4EdA3S4FLTGjmYaF2WAYRcUz",
      "outcome": {
        "logs": [],
        "receipt_ids": [],
        "gas_burnt": 0,
        "tokens_burnt": "0",
        "executor_id": "lucio.testnet",
        "status": {
          "SuccessValue": ""
        }
      }
    }
  ]
}

Edit: PS. It looks like the 1st function call was failing silently (regarding Tx result). I tested the same calls as 2 separate transactions and the 1st one (select_staking_pool) succeeded.


Solution

  • All later actions after the first failed action are not executed. And their execution fees are refunded. All the changes that were successfully executed before, will be reverted and all the promises will not be scheduled and executed either.

    Your case is more complicated, because the first action succeeds by returning a promise. The resulting promise later will fails in the callback, but the second action fails immediately, because the staking pool is not selected yet due to async execution. So the first promise doesn't get scheduled.

    EDIT 1.

    Once a transaction or a receipt succeeds (finishes all actions) it doesn't rollback anything. So if any future promises fail they are going to be independently executing from each other. Also only the last action in a batch of actions returns the result for the entire receipt.