Spaces:
Runtime error
Runtime error
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.
:::
|