The ATEN token contract follows the standard ERC-20 specification, including the use of 18 decimal places. The contract also includes an additional transferAndCall feature, which allows direct smart contract interactions without relying on the typical approve/transferFrom mechanism.
TransferAndCall
Besides the standard ERC20 token features (transfer(), balanceOf(), allowance(), etc), the following transferAndCall feature is also available:
function transferAndCall(
address to,
uint256 amount,
bytes calldata data
) public returns (bool)
Send tokens to a contract address along with call data
Parameters:
Name
Type
Description
to
address
destination address for the transfer
amount
uint256
amount to be sent
data
bytes
supplementary data to be provided to the receiving contract
The receiving contract must implement the ITokenCallReceiver interface in order to receive the tokens and execute the desired call.
interface ITokenCallReceiver {
function onTokenTransfer(
address from,
uint256 amount,
bytes calldata data
) external returns (bool);
}
Example
The following example demonstrates how to use the transferAndCall function to send tokens to a contract and execute a specific function:
import { ethers } from 'ethers';
// Placeholder values
const providerUrl = 'YOUR_RPC_PROVIDER_URL';
const privateKey = 'YOUR_PRIVATE_KEY';
const contractAddress = '0xYourContractAddress';
const recipientAddress = '0xRecipientAddress';
const transferAmount = ethers.BigNumber.from('1000000000000000000'); // 1 ETH
const stakeAmount = ethers.BigNumber.from('500000000000000000'); // 0.5 ETH
// Setup provider and wallet
const provider = new ethers.providers.JsonRpcProvider(providerUrl);
const wallet = new ethers.Wallet(privateKey, provider);
// ABI definitions
const transferAndCallAbi = [
"function transferAndCall(address to, uint256 amount, bytes calldata data) public returns (bool)"
];
const stakeAbi = [
"function stake(uint256 amount) public"
];
// Encode stake data
const stakeInterface = new ethers.utils.Interface(stakeAbi);
const stakeData = stakeInterface.encodeFunctionData('stake', [stakeAmount]);
// Encode transferAndCall data
const transferAndCallInterface = new ethers.utils.Interface(transferAndCallAbi);
const calldata = transferAndCallInterface.encodeFunctionData('transferAndCall', [recipientAddress, transferAmount, stakeData]);
// Create contract instance
const contract = new ethers.Contract(contractAddress, transferAndCallAbi, wallet);
async function sendTransaction() {
try {
const tx = await contract.transferAndCall(recipientAddress, transferAmount, stakeData);
const receipt = await tx.wait();
console.log("Transaction confirmed");
} catch (error) {
console.error('Transaction failed:', error);
}
}
// Send the transaction
sendTransaction();
On the receiving contract side, the onTokenTransfer function should be implemented to handle the incoming tokens and execute the desired logic:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
interface ITokenCallReceiver {
function onTokenTransfer(
address from,
uint256 amount,
bytes calldata data
) external returns (bool);
}
contract TokenReceiver is ITokenCallReceiver {
// Define function selectors as constants
bytes4 private constant STAKE_SELECTOR = bytes4(keccak256("stake(uint256)"));
// Implement the onTokenTransfer function
function onTokenTransfer(
address from,
uint256 amount,
bytes calldata data
) external override returns (bool) {
// Extract the function selector
bytes4 selector;
assembly {
selector := calldataload(data.offset)
}
// Decode the function call data based on the selector
if (selector == STAKE_SELECTOR) {
// Extract the parameters for the stake function
(uint256 stakeAmount) = abi.decode(data[4:], (uint256)); // Skip the 4-byte function selector
// Handle the stake function logic
stake(stakeAmount);
} else {
revert("Unsupported function selector");
}
// Return success
return true;
}
// Example stake function that could be called
function stake(uint256 amount) public {
// Handle the staking logic here
}
}