Useful Contracts
MultiSend
This contract allows you to send identical amounts of TNT to multiple addresses in a single transaction. Useful for batching transactions for airdrop distributions.
Explorer: 0x55E25dF92f6a7384844964a6e2a85fa182f8abfa
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MultiSend {
constructor() {}
function multiSend(address[] calldata recipients, uint256[] calldata amounts) external payable {
require(recipients.length == amounts.length, "Arrays must be same length");
require(recipients.length > 0, "Must provide at least one recipient");
uint256 total = 0;
for(uint256 i = 0; i < amounts.length; i++) {
total += amounts[i];
}
require(msg.value >= total, "Insufficient funds sent");
for(uint256 i = 0; i < recipients.length; i++) {
(bool success,) = recipients[i].call{value: amounts[i]}("");
require(success, "Transfer failed");
}
// Return excess ETH if any
uint256 remaining = msg.value - total;
if (remaining > 0) {
(bool success,) = msg.sender.call{value: remaining}("");
require(success, "Failed to return remaining ETH");
}
}
}
Multicall3
Multicall3 has two main use cases:
- Aggregate results from multiple contract reads into a single JSON-RPC request.
- Execute multiple state-changing calls in a single transaction.
Read more about Multicall3 here.
Testnet Explorer: 0xcA11bde05977b3631167028862bE2a173976CA11
Mainnet Explorer: 0xcA11bde05977b3631167028862bE2a173976CA11
import assert from 'node:assert';
import { createPublicClient, defineChain, erc20Abi, http } from 'viem';
assert(process.env.RPC_URL, 'RPC_URL is not set');
const TANGLE_TESTNET = defineChain({
id: 3799,
name: 'Tangle EVM Testnet',
nativeCurrency: {
name: 'Tangle Native Token',
symbol: 'tTNT',
decimals: 18,
},
rpcUrls: {
default: {
http: [process.env.RPC_URL],
},
},
contracts: {
multicall3: {
address: '0xcA11bde05977b3631167028862bE2a173976CA11',
blockCreated: 776767,
},
},
});
// Setup the client.
const client = createPublicClient({
chain: TANGLE_TESTNET,
transport: http(process.env.RPC_URL),
});
const ERC20_ADDRESS = '0x87d95f134221D9c2b3dE15aCe58BACe4121c07B0';
async function example1() {
// Execute the multicall and get the erc20 metadata (name, symbol, decimals). None of these calls can fail so we set
// `allowFailure` to false. This results in each return value's type matching the type of the
// corresponding call, e.g. `0x${string}` for addresses, `bigint` for uint256, etc. If we set
// `allowFailure` to true then the returns types are of the following shape, using the example of
// the address return type:
// {
// error: Error;
// result?: undefined;
// status: "error";
// } | {
// error?: undefined;
// result: `0x${string}`;
// status: "success";
// }
const [name, symbol, decimals] = await client.multicall({
contracts: [
{
address: ERC20_ADDRESS,
abi: erc20Abi,
functionName: 'name',
},
{
address: ERC20_ADDRESS,
abi: erc20Abi,
functionName: 'symbol',
},
{
address: ERC20_ADDRESS,
abi: erc20Abi,
functionName: 'decimals',
},
],
allowFailure: false,
});
console.log(
`Token ${name} has a symbol of ${symbol} and ${decimals} decimals at address ${ERC20_ADDRESS} on ${TANGLE_TESTNET.name}`,
);
}
example1().catch(console.error);