The Wallet API is in Beta. Endpoints and response formats may change.
Overview
The Wallet Funding Source endpoint identifies who originally funded a Solana wallet by analyzing its first incoming SOL transfer. It is valuable for attribution, compliance, understanding wallet relationships, and identifying exchange-funded wallets.
The funder’s name and category come from the same identity system used by the Identity endpoint, so when the funder is a known entity you get a human-readable label and category directly in the response.
When to use this
Use the Wallet Funding Source API for:
Wallet attribution : track where new wallets are being funded from.
Exchange detection : identify wallets funded directly from centralized exchanges.
Compliance and AML : flag wallets funded by known entities for compliance checks.
Bot detection : identify bot farms funded from the same source.
Airdrop analysis : track which wallets received initial funding from a project.
Sybil detection : find clusters of wallets funded by the same address.
Quickstart
Basic funding lookup
Find out who funded a wallet:
const getWalletFundingSource = async ( address ) => {
const url = `https://api.helius.xyz/v1/wallet/ ${ address } /funded-by?api-key=YOUR_API_KEY` ;
const response = await fetch ( url );
if ( response . status === 404 ) {
console . log ( 'No funding transaction found for this wallet' );
return null ;
}
if ( ! response . ok ) {
throw new Error ( `HTTP error! status: ${ response . status } ` );
}
const funding = await response . json ();
console . log ( `Funding Source: ${ funding . funderName || funding . funder } ` );
console . log ( `Funder Type: ${ funding . funderType || 'Unknown' } ` );
console . log ( `Initial Amount: ${ funding . amount } SOL` );
console . log ( `Date: ${ new Date ( funding . timestamp * 1000 ). toLocaleString () } ` );
console . log ( `Transaction: ${ funding . explorerUrl } ` );
return funding ;
};
getWalletFundingSource ( "86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY" );
import requests
from datetime import datetime
def get_wallet_funding_source ( address : str ):
url = f "https://api.helius.xyz/v1/wallet/ { address } /funded-by"
headers = { "X-Api-Key" : "YOUR_API_KEY" }
response = requests.get(url, headers = headers)
if response.status_code == 404 :
print ( 'No funding transaction found for this wallet' )
return None
response.raise_for_status()
funding = response.json()
print ( f "Funding Source: { funding.get( 'funderName' ) or funding[ 'funder' ] } " )
print ( f "Funder Type: { funding.get( 'funderType' , 'Unknown' ) } " )
print ( f "Initial Amount: { funding[ 'amount' ] } SOL" )
print ( f "Date: { datetime.fromtimestamp(funding[ 'timestamp' ]).strftime( '%Y-%m- %d %H:%M:%S' ) } " )
print ( f "Transaction: { funding[ 'explorerUrl' ] } " )
return funding
get_wallet_funding_source( "86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY" )
curl "https://api.helius.xyz/v1/wallet/86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY/funded-by?api-key=YOUR_API_KEY"
A successful response describes the wallet’s first incoming SOL transfer:
{
"funder" : "26MAyPNpK4At8LgRECMMbgiKQuJyg3oACtw1Q9FRyuba" ,
"funderName" : null ,
"funderType" : null ,
"mint" : "So11111111111111111111111111111111111111111" ,
"symbol" : "SOL" ,
"amount" : 0.09811972 ,
"amountRaw" : "98119720" ,
"decimals" : 9 ,
"date" : "2022-01-19T20:46:34.000Z" ,
"signature" : "5WX9C5kCQNULGGrSHJBR1WDFyetVyekbUpe1KQ45p3zEBe6jVgSsJuMqLWijjTDcnaAK2518ZriktRMCNycnsNAG" ,
"timestamp" : 1642625194 ,
"slot" : 116984883 ,
"explorerUrl" : "https://orbmarkets.io/tx/5WX9C5kCQNULGGrSHJBR1WDFyetVyekbUpe1KQ45p3zEBe6jVgSsJuMqLWijjTDcnaAK2518ZriktRMCNycnsNAG?tab=summary"
}
If a wallet has never received SOL, the API returns a 404:
{
"error" : "No funding transaction found" ,
"code" : 404
}
Field notes
funder : the address that sent the first SOL transfer to this wallet.
funderName : human-readable name if the funder is a known entity (e.g., exchange, protocol); null otherwise.
funderType : category of the funder (e.g., exchange, defi-protocol); null if not in the identity database.
mint : token mint address (So11111111111111111111111111111111111111111 for SOL).
symbol : token symbol (always SOL for funding transactions).
amount : initial SOL amount received (human-readable, e.g., 0.05 SOL).
amountRaw : raw amount in lamports as a string (e.g., "50000000" for 0.05 SOL).
decimals : number of decimal places for the token (9 for SOL).
date : ISO 8601 formatted date string (e.g., "2024-01-01T00:00:00.000Z").
signature : transaction signature of the funding transfer.
timestamp : Unix timestamp (in seconds) when the wallet was funded.
slot : Solana slot number when the funding transaction was confirmed.
explorerUrl : direct link to view the transaction on Orb.
Use cases
Detect exchange-funded wallets
Identify wallets funded directly from centralized exchanges:
const isExchangeFunded = async ( address ) => {
try {
const funding = await getWalletFundingSource ( address );
if ( ! funding ) {
console . log ( 'Wallet has no funding transaction' );
return false ;
}
if ( funding . funderType === 'exchange' ) {
console . log ( `Wallet was funded by ${ funding . funderName } ` );
console . log ( `This is likely a retail user withdrawing from an exchange` );
return true ;
}
console . log ( `Wallet was not funded by an exchange` );
return false ;
} catch ( error ) {
console . error ( 'Error checking funding source:' , error );
return false ;
}
};
isExchangeFunded ( "86xCnPeV69n6t3DnyGvkKobf9FdN2H9oiVDdaMpo2MMY" );
Find wallet clusters (Sybil detection)
Identify groups of wallets funded by the same source:
const findWalletClusters = async ( walletAddresses ) => {
const fundingData = await Promise . all (
walletAddresses . map ( async address => {
try {
const funding = await getWalletFundingSource ( address );
return { address , funder: funding ?. funder };
} catch {
return { address , funder: null };
}
})
);
// Group by funder
const clusters = {};
fundingData . forEach (({ address , funder }) => {
if ( funder ) {
if ( ! clusters [ funder ]) {
clusters [ funder ] = [];
}
clusters [ funder ]. push ( address );
}
});
// Report clusters
Object . entries ( clusters ). forEach (([ funder , wallets ]) => {
if ( wallets . length > 1 ) {
console . log ( ` \n Found cluster: ${ wallets . length } wallets funded by ${ funder . slice ( 0 , 8 ) } ...` );
wallets . forEach ( wallet => console . log ( ` - ${ wallet } ` ));
}
});
return clusters ;
};
// Example: Check list of wallets for clusters
const suspiciousWallets = [
"Wallet1..." ,
"Wallet2..." ,
"Wallet3..."
];
findWalletClusters ( suspiciousWallets );
Track airdrop recipients
Analyze where airdrop recipients came from:
const analyzeAirdropRecipients = async ( airdropWallets ) => {
const fundingSources = await Promise . all (
airdropWallets . map ( async address => {
try {
return await getWalletFundingSource ( address );
} catch {
return null ;
}
})
);
const stats = {
total: airdropWallets . length ,
exchangeFunded: 0 ,
unknown: 0 ,
byExchange: {}
};
fundingSources . forEach ( funding => {
if ( ! funding ) {
stats . unknown ++ ;
return ;
}
if ( funding . funderType === 'exchange' ) {
stats . exchangeFunded ++ ;
const exchange = funding . funderName || 'Unknown Exchange' ;
stats . byExchange [ exchange ] = ( stats . byExchange [ exchange ] || 0 ) + 1 ;
}
});
console . log ( 'Airdrop Recipient Analysis:' );
console . log ( `Total Recipients: ${ stats . total } ` );
console . log ( `Exchange-Funded: ${ stats . exchangeFunded } ( ${ ( stats . exchangeFunded / stats . total * 100 ). toFixed ( 1 ) } %)` );
console . log ( `Unknown Source: ${ stats . unknown } ` );
console . log ( ' \n By Exchange:' );
Object . entries ( stats . byExchange ). forEach (([ exchange , count ]) => {
console . log ( ` ${ exchange } : ${ count } ` );
});
return stats ;
};
Build a wallet timeline
Create a timeline starting from the wallet’s creation:
const buildWalletTimeline = async ( address ) => {
const funding = await getWalletFundingSource ( address );
if ( ! funding ) {
console . log ( 'No funding data available' );
return null ;
}
const creationDate = new Date ( funding . timestamp * 1000 );
const ageInDays = Math . floor (( Date . now () - creationDate . getTime ()) / ( 1000 * 60 * 60 * 24 ));
console . log ( 'Wallet Timeline:' );
console . log ( `Created: ${ creationDate . toLocaleString () } ( ${ ageInDays } days ago)` );
console . log ( `Initial Funding: ${ funding . amount } SOL` );
console . log ( `Funded By: ${ funding . funderName || funding . funder . slice ( 0 , 8 ) + '...' } ` );
if ( funding . funderType === 'exchange' ) {
console . log ( `This wallet was likely created by withdrawing from ${ funding . funderName } ` );
}
return {
creationDate ,
ageInDays ,
initialFunding: funding . amount ,
fundedBy: funding . funderName || funding . funder
};
};
Compliance risk scoring
Assign risk scores based on funding source:
const assessWalletRisk = async ( address ) => {
const funding = await getWalletFundingSource ( address );
if ( ! funding ) {
return { riskLevel: 'UNKNOWN' , score: 50 , reasons: [ 'No funding data available' ] };
}
let score = 0 ;
let reasons = [];
// Low risk: Funded by known exchange
if ( funding . funderType === 'exchange' ) {
score = 20 ;
reasons . push ( `Funded by known exchange ( ${ funding . funderName } )` );
}
// Medium risk: Unknown funder
else if ( ! funding . funderName ) {
score = 50 ;
reasons . push ( 'Funded by unknown wallet' );
}
// High risk: Funded by flagged address
else if ( funding . funderType === 'flagged' ) {
score = 90 ;
reasons . push ( 'Funded by flagged address' );
}
// Age factor: New wallets are higher risk
const ageInDays = ( Date . now () / 1000 - funding . timestamp ) / ( 60 * 60 * 24 );
if ( ageInDays < 7 ) {
score += 20 ;
reasons . push ( 'Wallet is less than 7 days old' );
}
// Amount factor: Very small initial funding is suspicious
if ( funding . amount < 0.01 ) {
score += 10 ;
reasons . push ( 'Very small initial funding amount' );
}
const riskLevel = score < 30 ? 'LOW' : score < 60 ? 'MEDIUM' : 'HIGH' ;
console . log ( `Risk Assessment for ${ address } :` );
console . log ( `Risk Level: ${ riskLevel } (Score: ${ score } /100)` );
reasons . forEach ( reason => console . log ( ` - ${ reason } ` ));
return { riskLevel , score , reasons };
};
Attribution tracking
Track which sources are creating the most new wallets:
const trackNewWalletSources = async ( recentWallets ) => {
const fundingSources = await Promise . all (
recentWallets . map ( async address => {
try {
const funding = await getWalletFundingSource ( address );
return {
address ,
funder: funding ?. funder ,
funderName: funding ?. funderName ,
funderType: funding ?. funderType
};
} catch {
return { address , funder: null };
}
})
);
// Count by source
const sourceStats = {};
fundingSources . forEach (({ funderName , funderType }) => {
const sourceName = funderName || funderType || 'Unknown' ;
sourceStats [ sourceName ] = ( sourceStats [ sourceName ] || 0 ) + 1 ;
});
// Sort by count
const sorted = Object . entries ( sourceStats )
. sort (([, a ], [, b ]) => b - a )
. slice ( 0 , 10 );
console . log ( 'Top Wallet Funding Sources:' );
sorted . forEach (([ source , count ]) => {
console . log ( ` ${ source } : ${ count } wallets` );
});
return sourceStats ;
};
Funder types
The funderType field indicates the category of the wallet that funded the address. All values from the Identity Categories are supported.
Common funder types: Type Description Examples Centralized Exchange CEX hot wallets Binance, Coinbase, Kraken, OKX DeFi DeFi protocol addresses Jupiter, Raydium, Marinade Market Maker Market making firms Jump Trading, Wintermute Trading Firm Proprietary trading firms Institutional traders Cross-chain Bridge Bridge protocol addresses Wormhole, AllBridge, Portal Validator Validator addresses Coinbase Validator, Jito Key Opinion Leader Notable individuals Influencers, founders Treasury Project treasuries Protocol treasuries Stake Pool Liquid staking pools Marinade, Jito null Unknown funder Regular wallet, not in identity database
The complete list includes: Airdrop, Authority, Cross-chain Bridge, Casino & Gambling, DAO, DeFi, DePIN, Centralized Exchange, Exploiter/Hackers/Scams, Fees, Fundraise, Game, Genesis Block Distribution, Governance, Hacker, Jito, Key Opinion Leader, Market Maker, Memecoin, Multisig, NFT, Non-Circulating Supply, Oracle, Other, Payments, Proprietary AMM, Restaking, Rugger, Scammer, Spam, Stake Pool, System, Tools, Trading App/Bot, Trading Firm, Transaction Sending, Treasury, Validator, Vault, and X402. See the Identity Categories section for the complete list with descriptions.
Best practices
Handle 404 responses. Wallets that have never received SOL return a 404. This is expected for newly created but unfunded wallets.
Combine with the Identity API. The response includes funderName and funderType, but you can call the Identity endpoint on the funder address for more detail.
Cache funding data. A wallet’s funding source never changes. Cache this data permanently to avoid repeated API calls.
Check age for context. The timestamp tells you when the wallet was first funded. Combine age with funding source for better context.
Common errors
Error Code Description Solution 400 Invalid wallet address format Verify the address is a valid base58 Solana address 401 Missing or invalid API key Check your API key is included in the request 404 No funding transaction found This wallet has never received SOL 429 Rate limit exceeded Reduce request frequency or upgrade your plan
Limitations
This endpoint only tracks the first SOL transfer to a wallet.
If a wallet was created via airdrop or program initialization without a SOL transfer, it won’t have funding data.
The funding source represents the immediate funder, not necessarily the ultimate source of funds.
Historical data is only available for wallets created after this feature was deployed.
Next steps
Wallet Identity Resolve the funder address to a full label, category, and tags.
Wallet API Overview All Wallet API endpoints and shared conventions.
API Reference Request and response schemas for funding source lookup.