File size: 4,992 Bytes
711e9c6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
---
sidebar_position: 3
---

# Call Multiple Contracts in a Single Tx

Up to now, we have only shown how to call one smart contract in a transaction. That is, only one input of the tx spends a smart contract UTXO, and the other inputs, if any, spend Pay-to-Public-Key-Hash ([P2PKH](https://learnmeabitcoin.com/guide/p2pkh)) UTXOs, which are generally NOT regarded as smart contracts.

There are cases where it is desirable to spend multiple smart contract UTXOs in different inputs of a tx.

The main differences from [calling a single contract](../how-to-deploy-and-call-a-contract/how-to-deploy-and-call-a-contract.md#contract-call) are:

1. Set `multiContractCall = true` in `MethodCallOptions`
2. Each call may only return a partial/incomplete transaction, instead of a complete transaction
3. A partial tx has to be passed as `ContractTransaction` in `MethodCallOptions` in subsequent calls
4. Finally invoke `SmartContract.multiContractCall(partialContractTx: ContractTransaction, signer: Signer)` to sign and broadcast the complete transaction


The following is an [example code](https://github.com/sCrypt-Inc/boilerplate/blob/master/tests/testnet/multi_contracts_call.ts) of calling two contracts at the same time:


```ts
import { Counter } from '../../src/contracts/counter'
import { getDefaultSigner } from '../utils/helper'
import { HashPuzzle } from '../../src/contracts/hashPuzzle'

async function main() {
    await Counter.compile()
    await HashPuzzle.compile()

    const signer = getDefaultSigner()
    let counter = new Counter(1n)

    // connect to a signer
    await counter.connect(signer)

    // contract deployment
    const deployTx = await counter.deploy(1)
    console.log('Counter contract deployed: ', deployTx.id)

    counter.bindTxBuilder(
        'incrementOnChain',
        (
            current: Counter,
            options: MethodCallOptions<Counter>,
            ...args: any
        ): Promise<ContractTransaction> => {
            // create the next instance from the current
            const nextInstance = current.next()
            // apply updates on the next instance locally
            nextInstance.count++

            const tx = new bsv.Transaction()
            tx.addInput(current.buildContractInput(options.fromUTXO)).addOutput(
                new bsv.Transaction.Output({
                    script: nextInstance.lockingScript,
                    satoshis: current.balance,
                })
            )

            return Promise.resolve({
                tx: tx,
                atInputIndex: 0,
                nexts: [
                    {
                        instance: nextInstance,
                        balance: current.balance,
                        atOutputIndex: 0,
                    },
                ],
            })
        }
    )

    const plainText = 'abc'
    const byteString = toByteString(plainText, true)
    const sha256Data = sha256(byteString)

    const hashPuzzle = new HashPuzzle(sha256Data)

    // connect to a signer
    await hashPuzzle.connect(signer)

    const deployTx1 = await hashPuzzle.deploy(1)
    console.log('HashPuzzle contract deployed: ', deployTx1.id)

    hashPuzzle.bindTxBuilder(
        'unlock',
        (
            current: HashPuzzle,
            options: MethodCallOptions<HashPuzzle>,
            ...args: any
        ): Promise<ContractTransaction> => {
            if (options.partialContractTx) {
                const unSignedTx = options.partialContractTx.tx
                unSignedTx.addInput(
                    current.buildContractInput(options.fromUTXO)
                )

                return Promise.resolve({
                    tx: unSignedTx,
                    atInputIndex: 1,
                    nexts: [],
                })
            }

            throw new Error('no partialContractTx found')
        }
    )

    const partialTx = await counter.methods.incrementOnChain({
        multiContractCall: true,
    } as MethodCallOptions<Counter>)

    const finalTx = await hashPuzzle.methods.unlock(
        byteString,
        {
            multiContractCall: true,
            partialContractTx: partialTx,
        } as MethodCallOptions<HashPuzzle>
    )

    const { tx: callTx, nexts } = await SmartContract.multiContractCall(
        finalTx,
        signer
    )

    console.log('Counter, HashPuzzle contract `unlock` called: ', callTx.id)

    // hashPuzzle has terminated, but counter can still be called
    counter = nexts[0].instance
}

await main()

```



:::note
- You must bind a [transition builder](../how-to-deploy-and-call-a-contract/how-to-deploy-and-call-a-contract.md#tx-builders) to each contract instance, since [the default](../how-to-deploy-and-call-a-contract/how-to-customize-a-contract-tx.md#customize-1) only spends a single contract UTXO.
- If the called contracts need signatures from different private keys to be called, the signer passed to `multiContractCall` must have all private keys.
:::