Instantiating a Contract
Using getContract()
The getContract() factory function is the recommended approach for creating type-safe contract instances. This function binds ABI methods to the contract address and configures the underlying RPC provider for network communication.
function getContract<T extends BaseContractProperties>(
address: string | Address,
abi: BitcoinInterface | BitcoinInterfaceAbi,
provider: AbstractRpcProvider,
network: Network,
sender?: Address
): BaseContract<T> & Omit<T, keyof BaseContract<T>>The table below describes the function parameters.
| Parameter | Type | Required | Description |
|---|---|---|---|
| address | string | Address | Yes | Contract address in P2OP or hex format |
| abi | BitcoinInterface | BitcoinInterfaceAbi | Yes | Contract ABI definition |
| provider | AbstractRpcProvider | Yes | RPC provider instance |
| network | Network | Yes | Bitcoin network configuration |
| sender | Address | No | Default sender address for contract calls |
Type-Safe Contract Interactions
Using generics with getContract() provides full TypeScript support, including IntelliSense and compile-time validation.
// Create type-safe OP-20 contract
const token = getContract<IOP20Contract>(
address,
OP_20_ABI,
provider,
network
);
// TypeScript knows all available methods and return types
const name = await token.name(); // CallResult with name property
const balance = await token.balanceOf(addr); // CallResult with balance property
const decimals = await token.decimals(); // CallResult with decimals property
Setting Sender Address
The sender address identifies who is calling the contract during simulations. The contract uses this address to determine the caller's identity for authorization checks, verify token balances and allowances, evaluate access control rules, and resolve message sender within the contract logic.
Providing an accurate sender address is essential. Without it, simulations that depend on caller-specific state (such as balance lookups or ownership checks) will return incorrect results or revert unexpectedly.
Setting Sender at Construction
The most common approach is to provide the sender address when instantiating the contract through the getContract() function. This sets the default sender for all subsequent calls on that contract instance.
import {
Address,
AddressTypes,
Mnemonic,
MLDSASecurityLevel,
} from '@btc-vision/transaction';
const mnemonic = new Mnemonic(
'your seed phrase here ...',
'',
network,
MLDSASecurityLevel.LEVEL2
);
const wallet = mnemonic.deriveOPWallet(AddressTypes.P2TR, 0);
// Set sender at construction
const token = getContract<IOP20Contract>(
tokenAddress,
OP_20_ABI,
provider,
network,
wallet.address // Sender address
);Setting Sender After Construction
To change the sender after instantiation, call setSender() on the contract instance. This updates the caller identity for all subsequent simulations without requiring a new contract instance.
// Create without sender
const token = getContract<IOP20Contract>(
tokenAddress,
OP_20_ABI,
provider,
network
);
// Set sender later
token.setSender(wallet.address);
Address Formats
OP_NET contracts support multiple address representations. The contract instance exposes properties to access the address in both P2OP format (the OP_NET-native address encoding) and standard hexadecimal format.
// Get P2OP format address
const p2opAddress = token.p2op;
console.log('P2OP:', p2opAddress);
// Get full Address object
const contractAddress = await token.contractAddress;
console.log('Hex:', contractAddress.toHex());| Format | Example | Use Case |
|---|---|---|
| P2OP | bcrt1p... | Display and configuration |
| Hex | 0x1234... | Internal operations |
| Address object | Address | Type-safe operations |
Examples
OP-20 Token Contract
import {
getContract,
IOP20Contract,
JSONRpcProvider,
OP_20_ABI,
} from 'opnet';
import {
Address,
AddressTypes,
Mnemonic,
MLDSASecurityLevel,
} from '@btc-vision/transaction';
import { networks } from '@btc-vision/bitcoin';
async function main() {
const network = networks.regtest;
const provider = new JSONRpcProvider('https://regtest.opnet.org', network);
const mnemonic = new Mnemonic(
'your seed phrase here ...',
'',
network,
MLDSASecurityLevel.LEVEL2
);
const wallet = mnemonic.deriveOPWallet(AddressTypes.P2TR, 0);
const tokenAddress = Address.fromString('0x...');
const token = getContract<IOP20Contract>(
tokenAddress,
OP_20_ABI,
provider,
network,
wallet.address
);
// Read token info
const [name, symbol, decimals] = await Promise.all([
token.name(),
token.symbol(),
token.decimals(),
]);
console.log(`Token: ${name.properties.name} (${symbol.properties.symbol})`);
console.log(`Decimals: ${decimals.properties.decimals}`);
// Check balance
const balance = await token.balanceOf(wallet.address);
console.log(`Balance: ${balance.properties.balance}`);
await provider.close();
}OP-721 NFT Contract
import {
getContract,
IExtendedOP721Contract,
OP_721_ABI,
} from 'opnet';
const nft = getContract<IExtendedOP721Contract>(
nftAddress,
OP_721_ABI,
provider,
network,
wallet.address
);
// Read NFT info
const name = await nft.name();
const symbol = await nft.symbol();
// Check ownership
const owner = await nft.ownerOf(tokenId);
console.log('Owner:', owner.properties.owner);DEX Router Contract
import {
getContract,
IMotoswapRouterContract,
MOTOSWAP_ROUTER_ABI,
} from 'opnet';
const router = getContract<IMotoswapRouterContract>(
routerAddress,
MOTOSWAP_ROUTER_ABI,
provider,
network,
wallet.address
);
// Get quote
const quote = await router.getAmountOut(
amountIn,
reserveIn,
reserveOut
);
console.log('Expected output:', quote.properties.amountOut);