Creating Transfers via API
This topic explains how to create transfers, handle different asset types, and monitor transfer status using the IBM Digital Asset Haven API and SDK.
Prerequisites
- Service account or authenticated user with
Wallets:Transfers:Createpermission. - IBM Digital Asset Haven SDK installed and configured.
- Wallet with sufficient balance for the transfer and gas fees.
Creating a native token transfer
Send native tokens (such as ETH, MATIC, or SOL):
import { DfnsApiClient } from '@dfns/sdk'
const dfns = new DfnsApiClient({
baseUrl: 'https://api.dfns.io',
// Your signer configuration
})
const transfer = await dfns.wallets.createTransfer({
walletId: 'wa-xxx-xxx',
body: {
kind: 'Native',
to: '0x1234...recipient',
amount: '1000000000000000000' // 1 ETH in wei
}
})
console.log('Transfer ID:', transfer.id)
console.log('Status:', transfer.status)
- Request parameters
-
Parameter Required Description kindYes Transfer type: Native,Erc20,Erc721, etc.toYes Recipient address amountYes Amount in base units (wei for ETH) externalIdNo Reference ID for tracking
Creating an ERC-20 token transfer
Send ERC-20 tokens:
const transfer = await dfns.wallets.createTransfer({
walletId: 'wa-xxx-xxx',
body: {
kind: 'Erc20',
contract: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
to: '0x1234...recipient',
amount: '1000000' // 1 USDC (6 decimals)
}
})
Note: Token amounts are in base units. USDC has 6 decimals, so 1 USDC = 1000000. ETH has 18 decimals, so 1 ETH = 1000000000000000000.
Creating an NFT transfer
Transfer ERC-721 NFTs:
const transfer = await dfns.wallets.createTransfer({
walletId: 'wa-xxx-xxx',
body: {
kind: 'Erc721',
contract: '0x...nft-contract',
to: '0x1234...recipient',
tokenId: '123'
}
})
- Transfer with external ID
- Use external IDs for tracking:
const transfer = await dfns.wallets.createTransfer({ walletId: 'wa-xxx-xxx', body: { kind: 'Native', to: '0x1234...recipient', amount: '1000000000000000000', externalId: 'payout-2024-001' // Your reference } }) - Checking transfer status
- Retrieve the status of a transfer:
const transfer = await dfns.wallets.getTransfer({ walletId: 'wa-xxx-xxx', transferId: 'tr-xxx-xxx' }) console.log('Status:', transfer.status) // 'Pending', 'Broadcasted', 'Confirmed', 'Failed' - Transfer statuses
-
Status Description PendingTransfer created; may be awaiting approval BroadcastedTransaction sent to the network ConfirmedTransaction confirmed on the blockchain FailedTransaction failed - Listing transfers
- List transfers for a wallet:
const transfers = await dfns.wallets.listTransfers({ walletId: 'wa-xxx-xxx' }) for (const transfer of transfers.items) { console.log(`${transfer.id}: ${transfer.status}`) }
Handling policy approvals
If a transfer triggers a policy requiring approval, it enters a
Pending state:
const transfer = await dfns.wallets.createTransfer({
walletId: 'wa-xxx-xxx',
body: {
kind: 'Native',
to: '0x1234...recipient',
amount: '100000000000000000000' // Large amount
}
})
if (transfer.status === 'Pending') {
console.log('Transfer requires approval')
// The transfer will execute automatically once approved
}
For more information on programmatic approvals, see managing policies.Monitoring with webhooks
Subscribe to transfer events for real-time updates:
// Subscribe to events
await dfns.webhooks.createWebhook({
body: {
url: 'https://your-app.com/webhooks/dfns',
events: [
'wallet.transfer.broadcasted',
'wallet.transfer.confirmed',
'wallet.transfer.failed'
],
status: 'Enabled'
}
})
Handle webhook events:
app.post('/webhooks/dfns', async (req, res) => {
const event = req.body
switch (event.kind) {
case 'wallet.transfer.confirmed':
await handleTransferConfirmed(event.data)
break
case 'wallet.transfer.failed':
await handleTransferFailed(event.data)
break
}
res.status(200).send('OK')
})
For more information on setting up webhooks, see the Webhooks guide.
Polling for status
Polling is an alternative to webhooks.
async function waitForConfirmation(walletId: string, transferId: string) {
while (true) {
const transfer = await dfns.wallets.getTransfer({
walletId,
transferId
})
if (transfer.status === 'Confirmed') {
return transfer
}
if (transfer.status === 'Failed') {
throw new Error(`Transfer failed: ${transfer.error}`)
}
// Wait before checking again
await new Promise(resolve => setTimeout(resolve, 5000))
}
}
For best practices on polling, see Transaction monitoring.
Error handling
Handle common errors:
try {
const transfer = await dfns.wallets.createTransfer({
walletId: 'wa-xxx-xxx',
body: {
kind: 'Native',
to: '0x1234...recipient',
amount: '1000000000000000000'
}
})
} catch (error) {
if (error.status === 403) {
console.error('Permission denied')
} else if (error.status === 400) {
console.error('Invalid request:', error.message)
} else if (error.message.includes('insufficient balance')) {
console.error('Insufficient balance for transfer')
} else {
console.error('Error creating transfer:', error)
}
}