Skip to content
Prividium

Contract Interaction

Now your token contract is deployed and the permissions are configured. The next step is to try interacting with it using a script. Our script will transfer some of the tokens to another wallet address.

Because our token contract automatically mints some tokens to the contract deployer in the constructor function, the sender address should have some tokens to transfer already.

This guide assumes you still have the Prividium proxy running and are using the same hardhat.config.ts file set up in the contract deployment guide.

With the proxy running, the only Prividium-specific configuration you need to use Hardhat scripts is to define the chain and configure the clients to use that chain. The proxy will handle the authentication and transaction authorization for you.

Creating the script

Create a new file in the scripts folder called send-tokens.ts.

touch scripts/send-tokens.ts

Copy and paste the script file below.

send-tokens.ts
import { artifacts, network } from 'hardhat';
import {
    type Abi,
    type Account,
    type Address,
    defineChain,
    formatUnits,
    isAddress,
    type PublicClient,
    parseUnits
} from 'viem';

// Replace with your deployed token address
const TOKEN_CONTRACT_ADDRESS = (process.env.TOKEN_CONTRACT_ADDRESS as Address) ?? '0x...';

// Update this to change the recipient of the tokens
const recipientAddress = (process.env.RECIPIENT_ADDRESS as Address) ?? '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC';

// Update this to change the amount of tokens to send or the token decimals
// This sends 20 tokens and each token has 18 decimals
const amount = parseUnits('20', 18);

type FeeData = {
    gasPrice?: bigint;
    maxFeePerGas?: bigint;
    maxPriorityFeePerGas?: bigint;
};

type FeeOverrides = { gasPrice: bigint } | { maxFeePerGas: bigint; maxPriorityFeePerGas: bigint };

async function main() {
    if (!isAddress(TOKEN_CONTRACT_ADDRESS) || !isAddress(recipientAddress) || amount === 0n) {
        throw new Error('Invalid TOKEN_CONTRACT_ADDRESS, recipientAddress, or amount.');
    }

    const { viem } = await network.create('localPrividium');

    const localPrividium = defineChain({
        id: 6565,
        name: 'Local Prividium',
        network: 'localPrividium',
        nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
        rpcUrls: { default: { http: ['http://127.0.0.1:24101/rpc'] } }
    });

    const publicClient = await viem.getPublicClient({ chain: localPrividium });
    const [senderClient] = await viem.getWalletClients({ chain: localPrividium });
    if (!senderClient) throw new Error('No wallet client.');

    const abi = (await artifacts.readArtifact('QuickstartToken')).abi;

    const balanceBefore = await getRecipientBalance(publicClient, abi);
    console.log('balance before: ', formatUnits(balanceBefore as bigint, 18));

    const txOptions = await estimateWriteTransactionOptions(publicClient, {
        address: TOKEN_CONTRACT_ADDRESS,
        abi,
        functionName: 'transfer',
        args: [recipientAddress, amount],
        account: senderClient.account
    });

    const tx = await senderClient.writeContract({
        address: TOKEN_CONTRACT_ADDRESS,
        abi,
        functionName: 'transfer',
        args: [recipientAddress, amount],
        account: senderClient.account,
        ...txOptions
    });

    await publicClient.waitForTransactionReceipt({ hash: tx });
    console.log(`Transferred ${amount.toString()} token units to ${recipientAddress}.`);

    const balanceAfter = await getRecipientBalance(publicClient, abi);
    console.log('balance after: ', formatUnits(balanceAfter as bigint, 18));
}

async function estimateWriteTransactionOptions(
    publicClient: PublicClient,
    params: {
        address: Address;
        abi: Abi;
        functionName: string;
        args?: readonly unknown[];
        account: Account;
        value?: bigint;
    }
) {
    const gas = await publicClient.estimateContractGas({
        address: params.address,
        abi: params.abi,
        functionName: params.functionName,
        args: params.args,
        account: params.account,
        ...(params.value !== undefined ? { value: params.value } : {})
    });
    const feeOverrides = await resolveFeeOverrides(publicClient);
    return { gas, ...feeOverrides };
}

async function resolveFeeOverrides(publicClient: {
    estimateFeesPerGas: () => Promise<FeeData>;
}): Promise<FeeOverrides> {
    const DEFAULT_MAX_FEE_PER_GAS = 1_000_000_000n;
    const DEFAULT_MAX_PRIORITY_FEE_PER_GAS = 100_000_000n;

    const feeData = await publicClient.estimateFeesPerGas().catch(() => undefined);
    const gasPrice = feeData?.gasPrice !== undefined && feeData.gasPrice > 0n ? feeData.gasPrice : undefined;
    const maxFeePerGas =
        feeData?.maxFeePerGas !== undefined && feeData.maxFeePerGas > 0n
            ? feeData.maxFeePerGas
            : DEFAULT_MAX_FEE_PER_GAS;
    const maxPriorityFeePerGas =
        feeData?.maxPriorityFeePerGas !== undefined && feeData.maxPriorityFeePerGas > 0n
            ? feeData.maxPriorityFeePerGas
            : DEFAULT_MAX_PRIORITY_FEE_PER_GAS;

    return gasPrice !== undefined ? { gasPrice } : { maxFeePerGas, maxPriorityFeePerGas };
}

async function getRecipientBalance(publicClient: PublicClient, abi: Abi) {
    const balance = await publicClient.readContract({
        address: TOKEN_CONTRACT_ADDRESS,
        abi,
        functionName: 'balanceOf',
        args: [recipientAddress]
    });
    return balance;
}

main().catch((error) => {
    console.error(error);
    process.exitCode = 1;
});

This script will:

  1. Check the token balance of the recipient before the transfer.
  2. Transfer the defined amount of tokens from the default sender defined in hardhat.config.ts to the recipient address.
  3. Check the final token balance of the recipient after the transfer.

Configuring the token transfer

Replace the TOKEN_CONTRACT_ADDRESS at the top of the file with your deployed token contract address.

You can also change the recipientAddress and amount if you need.

Running the script

To run the token transfer script, use one of the commands below. Choose either localPrividium or testnetPrividium.

npx hardhat run scripts/send-tokens.ts --network localPrividium
npx hardhat run scripts/send-tokens.ts --network testnetPrividium

After running the script, you should see that the balance of the tokens has increased for the recipient address.