Interagindo com o Batch Precompile¶
Introdução¶
O Batch Precompile em redes EVM com tecnologia Tanssi permite que desenvolvedores agrupem várias chamadas EVM em uma única transação.
Sem o precompile, um usuário precisaria confirmar várias transações (por exemplo, aprovar um token e logo em seguida transferi-lo). Com o Batch Precompile, a experiência melhora porque o usuário confirma apenas uma transação, reduzindo também o gás pago por taxas básicas múltiplas.
O precompile interage diretamente com o pallet EVM do Substrate. A conta que chama a função em lote atua como msg.sender para todas as subtransações e cada contrato de destino altera o seu próprio armazenamento (diferente de delegatecall).
O Batch Precompile está localizado no seguinte endereço:
0x0000000000000000000000000000000000000801
Note
O uso de precompiladas pode trazer consequências inesperadas. As precompiladas do Tanssi são derivadas das do Moonbeam; portanto, familiarize-se com as considerações de segurança das precompiladas do Moonbeam.
A interface Solidity em lote¶
Batch.sol é a interface Solidity para os três métodos do precompile.
Batch.sol
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.3;
/// @dev The Batch contract's address.
address constant BATCH_ADDRESS = 0x0000000000000000000000000000000000000801;
/// @dev The Batch contract's instance.
Batch constant BATCH_CONTRACT = Batch(BATCH_ADDRESS);
/// @author The Moonbeam Team
/// @title Batch precompile
/// @dev Allows to perform multiple calls throught one call to the precompile.
/// Can be used by EOA to do multiple calls in a single transaction.
/// @custom:address 0x0000000000000000000000000000000000000801
interface Batch {
/// @dev Batch multiple calls into a single transaction.
/// All calls are performed from the address calling this precompile.
///
/// In case of one subcall reverting following subcalls will still be attempted.
///
/// @param to List of addresses to call.
/// @param value List of values for each subcall. If array is shorter than "to" then additional
/// calls will be performed with a value of 0.
/// @param callData Call data for each `to` address. If array is shorter than "to" then
/// additional calls will be performed with an empty call data.
/// @param gasLimit Gas limit for each `to` address. Use 0 to forward all the remaining gas.
/// If array is shorter than "to" then the remaining gas available will be used.
/// @custom:selector 79df4b9c
function batchSome(
address[] memory to,
uint256[] memory value,
bytes[] memory callData,
uint64[] memory gasLimit
) external;
/// @dev Batch multiple calls into a single transaction.
/// All calls are performed from the address calling this precompile.
///
/// In case of one subcall reverting, no more subcalls will be executed but
/// the batch transaction will succeed. Use batchAll to revert on any subcall revert.
///
/// @param to List of addresses to call.
/// @param value List of values for each subcall. If array is shorter than "to" then additional
/// calls will be performed with a value of 0.
/// @param callData Call data for each `to` address. If array is shorter than "to" then
/// additional calls will be performed with an empty call data.
/// @param gasLimit Gas limit for each `to` address. Use 0 to forward all the remaining gas.
/// If array is shorter than "to" then the remaining gas available will be used.
/// @custom:selector cf0491c7
function batchSomeUntilFailure(
address[] memory to,
uint256[] memory value,
bytes[] memory callData,
uint64[] memory gasLimit
) external;
/// @dev Batch multiple calls into a single transaction.
/// All calls are performed from the address calling this precompile.
///
/// In case of one subcall reverting, the entire batch will revert.
///
/// @param to List of addresses to call.
/// @param value List of values for each subcall. If array is shorter than "to" then additional
/// calls will be performed with a value of 0.
/// @param callData Call data for each `to` address. If array is shorter than "to" then
/// additional calls will be performed with an empty call data.
/// @param gasLimit Gas limit for each `to` address. Use 0 to forward all the remaining gas.
/// If array is shorter than "to" then the remaining gas available will be used.
/// @custom:selector 96e292b8
function batchAll(
address[] memory to,
uint256[] memory value,
bytes[] memory callData,
uint64[] memory gasLimit
) external;
/// Emitted when a subcall succeeds.
event SubcallSucceeded(uint256 index);
/// Emitted when a subcall fails.
event SubcallFailed(uint256 index);
}
A interface inclui as funções:
batchSome(address[] to, uint256[] value, bytes[] callData, uint64[] gasLimit) — executa várias chamadas. Cada índice dos arrays compõe uma subchamada. Se uma subchamada reverter, as seguintes ainda serão tentadas
to- lista de endereços das subtransaçõesvalue- valores em moeda nativa para cada subtransação; se a lista for menor queto, os demais valores serão 0callData- dados de chamada para cada subtransação; se a lista for menor queto, as restantes não terão dadosgasLimit- limite de gás de cada subtransação; 0 encaminha todo o gás restante. Se a lista for menor queto, as seguintes receberão todo o gás restante
batchSomeUntilFailure(... ) — igual ao anterior, porém interrompe ao primeiro erro
batchAll(... ) — executa várias chamadas de forma atômica. Se uma subchamada reverter, todas revertem
Eventos emitidos:
- SubcallSucceeded(uint256 index) — emitido quando uma subchamada com o índice informado é bem-sucedida
- SubcallFailed(uint256 index) — emitido quando uma subchamada com o índice informado falha
Interaja com a interface Solidity¶
Verifique os pré-requisitos¶
- Carteira compatível com EVM configurada para sua rede (por exemplo, MetaMask)
- Conta com tokens nativos suficientes
- Opcional: rede de demonstração EVM no Tanssi dApp
Contrato de exemplo¶
Usaremos SimpleContract.sol para demonstrar interações em lote:
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.0;
contract SimpleContract {
mapping(uint256 => string) public messages;
function setMessage(uint256 id, string calldata message) external {
messages[id] = message;
}
}
Configuração do Remix¶
- Abra Remix.
- Crie os arquivos Batch.sol e SimpleContract.sol com o conteúdo correspondente.
Compile os contratos¶
- Abra Batch.sol e clique em Compile Batch.sol (aba Compile).
- Repita para SimpleContract.sol.
Acesse o precompile¶
- Na aba Deploy and Run, escolha Injected Provider - MetaMask em ENVIRONMENT.
- Selecione Batch.sol em CONTRACT.
- No campo At Address, insira
0x0000000000000000000000000000000000000801e clique em At Address.
O contrato BATCH aparecerá em Deployed Contracts.
Implemente o contrato de exemplo¶
- Ainda na aba Deploy and Run, selecione SimpleContract em CONTRACT.
- Clique em Deploy e confirme no MetaMask.
O SIMPLECONTRACT aparecerá em Deployed Contracts.
Envie moeda nativa via precompile¶
Para usar batchAll e enviar tokens nativos de forma atômica:
- Expanda o contrato BATCH em Deployed Contracts.
- Abra batchAll.
- Preencha:
- to:
[\"ENDERECO_1\",\"ENDERECO_2\"] - value:
[\"1000000000000000000\",\"2000000000000000000\"](exemplo: 1 e 2 tokens) - callData:
[] - gasLimit:
[] - Clique em transact e confirme no MetaMask.
Obtenha o call data de uma interação de contrato¶
- Em SIMPLECONTRACT, expanda setMessage.
- Preencha id (ex.:
1) e message (ex.:"tanssi"). - Clique no ícone de copiar ao lado de transact para obter o call data codificado.
Interaja com funções via precompile¶
Para chamar duas vezes setMessage de forma atômica:
- Copie o endereço de SIMPLECONTRACT.
- Em batchAll, preencha:
- to:
[\"ENDERECO_CONTRATO\",\"ENDERECO_CONTRATO\"] - value:
[0,0] - callData:
[\"CALLDATA_1\",\"CALLDATA_2\"](obtidos no passo anterior) - gasLimit:
[](ou limites específicos) - Clique em transact e confirme no MetaMask.
Após a execução, consulte a função messages do SimpleContract.sol para verificar os valores definidos.
Combinando subtransações¶
Você pode mesclar transferências nativas e chamadas de contrato na mesma transação em lote. Forneça arrays alinhados (mesmo tamanho ou valores padrão) para to, value, callData e gasLimit. O precompile cuida do roteamento e garante a execução conforme a função escolhida (batchSome, batchSomeUntilFailure ou batchAll).
Bibliotecas de desenvolvimento Ethereum¶
As funções do precompile também podem ser usadas por bibliotecas:
// Import the contract ABI
const { abi } = require('./INSERT_ABI_PATH');
// Use ABI to create an interface
const yourContractInterface = new ethers.Interface(abi);
// Find call data for the setMessage function
const callData = yourContractInterface.encodeFunctionData(
'INSERT_FUNCTION_NAME',
[
'INSERT_INPUT_1',
'INSERT_INPUT_2',
// ...
]
);
// Import the contract ABI
const { abi } = require('./INSERT_ABI_PATH');
// Find call data for the setMessage function
const callData = web3.eth.abi.encodeFunctionCall(abi, [
'INSERT_INPUT_1',
'INSERT_INPUT_2',
// ...
]);
# Import the ABI and bytecode
from compile import abi, bytecode
# Create contract instance
your_contract = web3.eth.contract(abi=abi, bytecode=bytecode)
# Encode the contract call
call_data = your_contract.encodeABI(
fn_name="INSERT_FUNCTION_NAME", args=["INSERT_INPUT_1", "INSERT_INPUT_2", ...]
)
Nota
Adapte os exemplos para produção e garanta validação de entradas, controle de gás e tratamento de erros adequados.
| Criada: 9 de dezembro de 2025