opnet
Version:
The perfect library for building Bitcoin-based applications.
649 lines (537 loc) • 18.2 kB
Markdown
# OP721 ABI
This guide covers the OP721 NFT (Non-Fungible Token) standard ABI.
## Overview
OP721 is the standard interface for NFTs on OPNet. It defines methods for NFT ownership, transfers, approvals, and metadata management.
## Import
```typescript
import {
OP_721_ABI,
EXTENDED_OP721_ABI,
IOP721Contract,
getContract,
} from 'opnet';
```
## Interface: IOP721Contract
### Metadata Methods
| Method | Parameters | Returns | Description |
|--------|------------|---------|-------------|
| `name()` | - | `string` | Collection name |
| `symbol()` | - | `string` | Collection symbol |
| `maxSupply()` | - | `bigint` | Maximum supply |
| `tokenURI(tokenId)` | `bigint` | `string` | Token metadata URI |
| `metadata()` | - | Full metadata object | All metadata in one call |
### Supply Methods
| Method | Parameters | Returns | Description |
|--------|------------|---------|-------------|
| `totalSupply()` | - | `bigint` | Total tokens minted |
### Ownership Methods
| Method | Parameters | Returns | Description |
|--------|------------|---------|-------------|
| `ownerOf(tokenId)` | `bigint` | `Address` | Owner of token |
| `balanceOf(owner)` | `Address` | `bigint` | Token count for owner |
| `tokenOfOwnerByIndex(owner, index)` | `Address, bigint` | `bigint` | Token ID at owner's index |
### Transfer Methods
| Method | Parameters | Description |
|--------|------------|-------------|
| `safeTransfer(to, tokenId, data)` | `Address, bigint, Uint8Array` | Transfer NFT with callback data |
| `safeTransferFrom(from, to, tokenId, data)` | `Address, Address, bigint, Uint8Array` | Transfer from another address |
### Approval Methods
| Method | Parameters | Returns | Description |
|--------|------------|---------|-------------|
| `approve(operator, tokenId)` | `Address, bigint` | - | Approve single token |
| `getApproved(tokenId)` | `bigint` | - | Get approved address |
| `setApprovalForAll(operator, approved)` | `Address, boolean` | - | Approve all tokens |
| `isApprovedForAll(owner, operator)` | `Address, Address` | `boolean` | Check operator approval |
### Signature-Based Approval Methods
| Method | Parameters | Description |
|--------|------------|-------------|
| `approveBySignature(owner, ownerTweakedPublicKey, operator, tokenId, deadline, signature)` | `Uint8Array, Uint8Array, Address, bigint, bigint, Uint8Array` | Approve via signature |
| `setApprovalForAllBySignature(owner, ownerTweakedPublicKey, operator, approved, deadline, signature)` | `Uint8Array, Uint8Array, Address, boolean, bigint, Uint8Array` | Set approval via signature |
### Other Methods
| Method | Parameters | Returns | Description |
|--------|------------|---------|-------------|
| `burn(tokenId)` | `bigint` | - | Burn a token |
| `changeMetadata()` | - | - | Trigger metadata change |
| `setBaseURI(baseURI)` | `string` | - | Set base URI for tokens |
| `domainSeparator()` | - | `Uint8Array` | EIP-712 domain separator |
| `nonceOf(owner)` | `Address` | `bigint` | Get nonce for signatures |
## Events
### Transferred
Emitted when NFT is transferred.
```typescript
interface TransferredEventNFT {
operator: Address; // Who initiated the transfer
from: Address; // Sender
to: Address; // Recipient
tokenId: bigint; // Token ID
}
```
### Approved
Emitted when single token approval changes.
```typescript
interface ApprovedEventNFT {
owner: Address; // Token owner
operator: Address; // Approved operator
tokenId: bigint; // Token ID
}
```
### ApprovedForAll
Emitted when operator approval changes.
```typescript
interface ApprovedForAllEventNFT {
account: Address; // Token owner
operator: Address; // Operator address
approved: boolean; // Approval status
}
```
### Burned
Emitted when a token is burned.
```typescript
interface BurnedEventNFT {
from: Address; // Token owner
tokenId: bigint; // Token ID
}
```
### Minted
Emitted when a new token is minted.
```typescript
interface MintedEventNFT {
to: Address; // Recipient
tokenId: bigint; // Token ID
}
```
### URI
Emitted when token URI changes.
```typescript
interface URIEventNFT {
value: string; // New URI value
id: bigint; // Token ID
}
```
## Usage Examples
### Create Contract Instance
```typescript
import { getContract, OP_721_ABI, IOP721Contract } from 'opnet';
import { networks } from '@btc-vision/bitcoin';
const network = networks.regtest;
const provider = new JSONRpcProvider({ url: 'https://regtest.opnet.org', network });
const nft = getContract<IOP721Contract>(
'bc1p...nft-address...',
OP_721_ABI,
provider,
network
);
```
### Read Collection Info
```typescript
const name = await nft.name();
const symbol = await nft.symbol();
const maxSupply = await nft.maxSupply();
console.log('Collection:', name.properties.name);
console.log('Symbol:', symbol.properties.symbol);
console.log('Max Supply:', maxSupply.properties.maxSupply);
// Get full collection metadata in one call
const metadata = await nft.metadata();
console.log('Description:', metadata.properties.description);
console.log('Website:', metadata.properties.website);
```
### Check Ownership
```typescript
const tokenId = 1n;
const ownerResult = await nft.ownerOf(tokenId);
console.log('Owner of token', tokenId, ':', ownerResult.properties.owner.toHex());
// Get user's balance
const userAddress = Address.fromString('bc1p...');
const balance = await nft.balanceOf(userAddress);
console.log('NFTs owned:', balance.properties.balance);
```
### Enumerate Owner's Tokens
```typescript
const owner = Address.fromString('bc1p...');
const balance = await nft.balanceOf(owner);
for (let i = 0n; i < balance.properties.balance; i++) {
const tokenResult = await nft.tokenOfOwnerByIndex(owner, i);
console.log('Token ID:', tokenResult.properties.tokenId);
}
```
### Transfer NFT
```typescript
const to = Address.fromString('bc1p...recipient...');
const tokenId = 1n;
const data = new Uint8Array(); // Empty callback data
// Simulate first
const simulation = await nft.safeTransfer(to, tokenId, data);
if (simulation.revert) {
throw new Error(`Transfer would fail: ${simulation.revert}`);
}
// Send transaction
const result = await simulation.sendTransaction({
signer: wallet.keypair,
mldsaSigner: wallet.mldsaSigner,
refundTo: wallet.p2tr,
maximumAllowedSatToSpend: 50000n,
network: network,
feeRate: 10,
});
console.log('Transfer TX:', result.transactionId);
```
### Approve Operator
```typescript
const operator = Address.fromString('bc1p...marketplace...');
// Approve for all tokens
const simulation = await nft.setApprovalForAll(operator, true);
const result = await simulation.sendTransaction({
signer: wallet.keypair,
mldsaSigner: wallet.mldsaSigner,
refundTo: wallet.p2tr,
maximumAllowedSatToSpend: 50000n,
network: network,
feeRate: 10,
});
console.log('Approval TX:', result.transactionId);
```
### Check Approval Status
```typescript
const owner = Address.fromString('bc1p...owner...');
const operator = Address.fromString('bc1p...marketplace...');
const isApproved = await nft.isApprovedForAll(owner, operator);
console.log('Is approved for all:', isApproved.properties.approved);
```
### Get Full Metadata
```typescript
// Get all metadata in a single call
const metadata = await nft.metadata();
console.log('Name:', metadata.properties.name);
console.log('Symbol:', metadata.properties.symbol);
console.log('Icon:', metadata.properties.icon);
console.log('Banner:', metadata.properties.banner);
console.log('Description:', metadata.properties.description);
console.log('Website:', metadata.properties.website);
console.log('Total Supply:', metadata.properties.totalSupply);
console.log('Domain Separator:', metadata.properties.domainSeparator);
```
## Extended OP721 ABI
For mintable NFT collections with reservation system:
```typescript
import { EXTENDED_OP721_ABI } from 'opnet';
```
### Extended Methods
| Method | Parameters | Returns | Description |
|--------|------------|---------|-------------|
| `setMintEnabled(enabled)` | `boolean` | - | Enable/disable minting |
| `isMintEnabled()` | - | `boolean` | Check if minting enabled |
| `reserve(quantity)` | `bigint` | `{ remainingPayment, reservationBlock }` | Reserve tokens |
| `claim()` | - | - | Claim reserved tokens |
| `purgeExpired()` | - | - | Purge expired reservations |
| `getStatus()` | - | Status object | Get minting status |
| `airdrop(addresses, amounts)` | `Address[], Uint8Array[]` | - | Batch airdrop |
| `setTokenURI(tokenId, uri)` | `bigint, string` | - | Set individual token URI |
### Extended Events
```typescript
// Minting status changed
interface MintStatusChangedEvent {
enabled: boolean;
}
// Reservation created
interface ReservationCreatedEvent {
user: Address;
amount: bigint;
block: bigint;
feePaid: bigint;
}
// Reservation claimed
interface ReservationClaimedEvent {
user: Address;
amount: bigint;
firstTokenId: bigint;
}
// Reservations expired
interface ReservationExpiredEvent {
block: bigint;
amountRecovered: bigint;
}
```
### Get Status Example
```typescript
const status = await nft.getStatus();
console.log('Minted:', status.properties.minted);
console.log('Reserved:', status.properties.reserved);
console.log('Available:', status.properties.available);
console.log('Max Supply:', status.properties.maxSupply);
console.log('Price per Token:', status.properties.pricePerToken);
console.log('Reservation Fee %:', status.properties.reservationFeePercent);
```
## Full ABI Structure
```typescript
export const OP_721_ABI: BitcoinInterfaceAbi = [
// Metadata
{
name: 'name',
constant: true,
inputs: [],
outputs: [{ name: 'name', type: ABIDataTypes.STRING }],
type: BitcoinAbiTypes.Function,
},
{
name: 'symbol',
constant: true,
inputs: [],
outputs: [{ name: 'symbol', type: ABIDataTypes.STRING }],
type: BitcoinAbiTypes.Function,
},
{
name: 'maxSupply',
constant: true,
inputs: [],
outputs: [{ name: 'maxSupply', type: ABIDataTypes.UINT256 }],
type: BitcoinAbiTypes.Function,
},
{
name: 'tokenURI',
constant: true,
inputs: [{ name: 'tokenId', type: ABIDataTypes.UINT256 }],
outputs: [{ name: 'uri', type: ABIDataTypes.STRING }],
type: BitcoinAbiTypes.Function,
},
{
name: 'metadata',
constant: true,
inputs: [],
outputs: [
{ name: 'name', type: ABIDataTypes.STRING },
{ name: 'symbol', type: ABIDataTypes.STRING },
{ name: 'icon', type: ABIDataTypes.STRING },
{ name: 'banner', type: ABIDataTypes.STRING },
{ name: 'description', type: ABIDataTypes.STRING },
{ name: 'website', type: ABIDataTypes.STRING },
{ name: 'totalSupply', type: ABIDataTypes.UINT256 },
{ name: 'domainSeparator', type: ABIDataTypes.BYTES32 },
],
type: BitcoinAbiTypes.Function,
},
// Supply
{
name: 'totalSupply',
constant: true,
inputs: [],
outputs: [{ name: 'totalSupply', type: ABIDataTypes.UINT256 }],
type: BitcoinAbiTypes.Function,
},
// Ownership
{
name: 'balanceOf',
constant: true,
inputs: [{ name: 'owner', type: ABIDataTypes.ADDRESS }],
outputs: [{ name: 'balance', type: ABIDataTypes.UINT256 }],
type: BitcoinAbiTypes.Function,
},
{
name: 'ownerOf',
constant: true,
inputs: [{ name: 'tokenId', type: ABIDataTypes.UINT256 }],
outputs: [{ name: 'owner', type: ABIDataTypes.ADDRESS }],
type: BitcoinAbiTypes.Function,
},
{
name: 'tokenOfOwnerByIndex',
constant: true,
inputs: [
{ name: 'owner', type: ABIDataTypes.ADDRESS },
{ name: 'index', type: ABIDataTypes.UINT256 },
],
outputs: [{ name: 'tokenId', type: ABIDataTypes.UINT256 }],
type: BitcoinAbiTypes.Function,
},
// Transfers
{
name: 'safeTransfer',
inputs: [
{ name: 'to', type: ABIDataTypes.ADDRESS },
{ name: 'tokenId', type: ABIDataTypes.UINT256 },
{ name: 'data', type: ABIDataTypes.BYTES },
],
outputs: [],
type: BitcoinAbiTypes.Function,
},
{
name: 'safeTransferFrom',
inputs: [
{ name: 'from', type: ABIDataTypes.ADDRESS },
{ name: 'to', type: ABIDataTypes.ADDRESS },
{ name: 'tokenId', type: ABIDataTypes.UINT256 },
{ name: 'data', type: ABIDataTypes.BYTES },
],
outputs: [],
type: BitcoinAbiTypes.Function,
},
// Approvals
{
name: 'approve',
inputs: [
{ name: 'operator', type: ABIDataTypes.ADDRESS },
{ name: 'tokenId', type: ABIDataTypes.UINT256 },
],
outputs: [],
type: BitcoinAbiTypes.Function,
},
{
name: 'getApproved',
constant: true,
inputs: [{ name: 'tokenId', type: ABIDataTypes.UINT256 }],
outputs: [],
type: BitcoinAbiTypes.Function,
},
{
name: 'setApprovalForAll',
inputs: [
{ name: 'operator', type: ABIDataTypes.ADDRESS },
{ name: 'approved', type: ABIDataTypes.BOOL },
],
outputs: [],
type: BitcoinAbiTypes.Function,
},
{
name: 'isApprovedForAll',
constant: true,
inputs: [
{ name: 'owner', type: ABIDataTypes.ADDRESS },
{ name: 'operator', type: ABIDataTypes.ADDRESS },
],
outputs: [{ name: 'approved', type: ABIDataTypes.BOOL }],
type: BitcoinAbiTypes.Function,
},
// Signature-based approvals
{
name: 'approveBySignature',
inputs: [
{ name: 'owner', type: ABIDataTypes.BYTES32 },
{ name: 'ownerTweakedPublicKey', type: ABIDataTypes.BYTES32 },
{ name: 'operator', type: ABIDataTypes.ADDRESS },
{ name: 'tokenId', type: ABIDataTypes.UINT256 },
{ name: 'deadline', type: ABIDataTypes.UINT64 },
{ name: 'signature', type: ABIDataTypes.BYTES },
],
outputs: [],
type: BitcoinAbiTypes.Function,
},
{
name: 'setApprovalForAllBySignature',
inputs: [
{ name: 'owner', type: ABIDataTypes.BYTES32 },
{ name: 'ownerTweakedPublicKey', type: ABIDataTypes.BYTES32 },
{ name: 'operator', type: ABIDataTypes.ADDRESS },
{ name: 'approved', type: ABIDataTypes.BOOL },
{ name: 'deadline', type: ABIDataTypes.UINT64 },
{ name: 'signature', type: ABIDataTypes.BYTES },
],
outputs: [],
type: BitcoinAbiTypes.Function,
},
// Other
{
name: 'burn',
inputs: [{ name: 'tokenId', type: ABIDataTypes.UINT256 }],
outputs: [],
type: BitcoinAbiTypes.Function,
},
{
name: 'changeMetadata',
inputs: [],
outputs: [],
type: BitcoinAbiTypes.Function,
},
{
name: 'setBaseURI',
inputs: [{ name: 'baseURI', type: ABIDataTypes.STRING }],
outputs: [],
type: BitcoinAbiTypes.Function,
},
{
name: 'domainSeparator',
constant: true,
inputs: [],
outputs: [{ name: 'domainSeparator', type: ABIDataTypes.BYTES32 }],
type: BitcoinAbiTypes.Function,
},
{
name: 'nonceOf',
constant: true,
inputs: [{ name: 'owner', type: ABIDataTypes.ADDRESS }],
outputs: [{ name: 'nonce', type: ABIDataTypes.UINT256 }],
type: BitcoinAbiTypes.Function,
},
// Events
{
name: 'Transferred',
values: [
{ name: 'operator', type: ABIDataTypes.ADDRESS },
{ name: 'from', type: ABIDataTypes.ADDRESS },
{ name: 'to', type: ABIDataTypes.ADDRESS },
{ name: 'tokenId', type: ABIDataTypes.UINT256 },
],
type: BitcoinAbiTypes.Event,
},
{
name: 'Approved',
values: [
{ name: 'owner', type: ABIDataTypes.ADDRESS },
{ name: 'operator', type: ABIDataTypes.ADDRESS },
{ name: 'tokenId', type: ABIDataTypes.UINT256 },
],
type: BitcoinAbiTypes.Event,
},
{
name: 'ApprovedForAll',
values: [
{ name: 'account', type: ABIDataTypes.ADDRESS },
{ name: 'operator', type: ABIDataTypes.ADDRESS },
{ name: 'approved', type: ABIDataTypes.BOOL },
],
type: BitcoinAbiTypes.Event,
},
{
name: 'Burned',
values: [
{ name: 'from', type: ABIDataTypes.ADDRESS },
{ name: 'tokenId', type: ABIDataTypes.UINT256 },
],
type: BitcoinAbiTypes.Event,
},
{
name: 'Minted',
values: [
{ name: 'to', type: ABIDataTypes.ADDRESS },
{ name: 'tokenId', type: ABIDataTypes.UINT256 },
],
type: BitcoinAbiTypes.Event,
},
{
name: 'URI',
values: [
{ name: 'value', type: ABIDataTypes.STRING },
{ name: 'id', type: ABIDataTypes.UINT256 },
],
type: BitcoinAbiTypes.Event,
},
];
```
## Best Practices
1. **Check Ownership**: Verify ownership before transfers
2. **Use Safe Transfers**: `safeTransfer` ensures recipient can handle NFTs
3. **Approve Sparingly**: Only approve trusted operators
4. **Handle Reverts**: Always check `simulation.revert` before sending
5. **Batch Metadata**: Use `metadata()` for efficiency instead of multiple calls
## Next Steps
- [OP20 ABI](./op20-abi.md) - Token standard
- [OP721 Examples](../examples/op721-examples.md) - Code examples
- [MotoSwap ABIs](./motoswap-abis.md) - DEX interfaces
[← Previous: OP20S ABI](./op20s-abi.md) | [Next: MotoSwap ABIs →](./motoswap-abis.md)