Portal’s Android SDK provides token delegation capabilities through the portal.delegations API. This enables approving token spending, revoking approvals, checking delegation status, and transferring tokens as a delegate on both EVM and Solana chains.
Overview
The delegations functionality allows you to:
- Approve other addresses to spend tokens on behalf of your wallet
- Revoke existing delegations to remove spending permissions
- Check status of active delegations and balances
- Transfer tokens as a delegate from another address
Prerequisites
Before using delegation operations, ensure you have:
Approving Delegations
Use approve to grant another address permission to spend tokens on your behalf. This method works for both EVM and Solana chains.
EVM Approval
lifecycleScope.launch {
try {
val request = ApproveDelegationRequest(
chain = "eip155:11155111", // Sepolia testnet
token = "USDC",
delegateAddress = "0x1a3eda7eb7d13e60e638711c580490c19e164fee",
amount = "0.01"
)
val result = portal.delegations.approve(request)
result.onSuccess { response ->
// Sign and send EVM transactions sequentially
response.transactions?.let { transactions ->
for ((index, tx) in transactions.withIndex()) {
val txDict = mutableMapOf<String, String>(
"from" to tx.from,
"to" to tx.to
)
tx.data?.let { txDict["data"] = it }
tx.value?.let { txDict["value"] = it }
val txResponse = portal.request(
chainId = "eip155:11155111",
method = PortalRequestMethod.eth_sendTransaction,
params = listOf(txDict)
)
println("Tx ${index + 1} hash: ${txResponse.result as? String}")
}
}
}.onFailure { error ->
println("Approve (EVM) failed: ${error.message}")
}
} catch (e: Exception) {
println("Error approving EVM delegation: ${e.message}")
}
}
Solana Approval
lifecycleScope.launch {
try {
val request = ApproveDelegationRequest(
chain = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1", // Solana Devnet
token = "USDC",
delegateAddress = "8uzXpjP9zXRHqo6KGZaE6XrxnjarBsKTufVya7jHtyt5",
amount = "0.01"
)
val result = portal.delegations.approve(request)
result.onSuccess { response ->
// Sign and send Solana transactions sequentially
response.encodedTransactions?.let { encodedTxs ->
for ((index, encodedTx) in encodedTxs.withIndex()) {
val txResponse = portal.request(
chainId = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
method = PortalRequestMethod.sol_signAndSendTransaction,
params = listOf(encodedTx)
)
println("Tx ${index + 1} hash: ${txResponse.result as? String}")
}
}
}.onFailure { error ->
println("Approve (SOL) failed: ${error.message}")
}
} catch (e: Exception) {
println("Error approving Solana delegation: ${e.message}")
}
}
Checking Delegation Status
Use getStatus to check current delegations and token balances for a specific delegate address.
EVM Status Check
lifecycleScope.launch {
try {
val request = GetDelegationStatusRequest(
chain = "eip155:11155111",
token = "USDC",
delegateAddress = "0x1a3eda7eb7d13e60e638711c580490c19e164fee"
)
val result = portal.delegations.getStatus(request)
result.onSuccess { response ->
println("Chain ID: ${response.chainId}")
println("Token: ${response.token}")
println("Token Address: ${response.tokenAddress}")
response.balance?.let { println("Balance: $it") }
println("Delegations: ${response.delegations.size}")
response.delegations.forEach { delegation ->
println(" - Address: ${delegation.address}, Amount: ${delegation.delegateAmount}")
}
}.onFailure { error ->
println("Get Status (EVM) failed: ${error.message}")
}
} catch (e: Exception) {
println("Error getting EVM delegation status: ${e.message}")
}
}
Solana Status Check
lifecycleScope.launch {
try {
val request = GetDelegationStatusRequest(
chain = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
token = "USDC",
delegateAddress = "8uzXpjP9zXRHqo6KGZaE6XrxnjarBsKTufVya7jHtyt5"
)
val result = portal.delegations.getStatus(request)
result.onSuccess { response ->
println("Chain ID: ${response.chainId}")
println("Token: ${response.token}")
println("Delegations: ${response.delegations.size}")
response.delegations.forEach { delegation ->
println(" - Address: ${delegation.address}, Amount: ${delegation.delegateAmount}")
}
}.onFailure { error ->
println("Get Status (SOL) failed: ${error.message}")
}
} catch (e: Exception) {
println("Error getting Solana delegation status: ${e.message}")
}
}
Revoking Delegations
Use revoke to remove spending permissions from a delegate address.
EVM Revoke
lifecycleScope.launch {
try {
val request = RevokeDelegationRequest(
chain = "eip155:11155111",
token = "USDC",
delegateAddress = "0x1a3eda7eb7d13e60e638711c580490c19e164fee"
)
val result = portal.delegations.revoke(request)
result.onSuccess { response ->
// Sign and send EVM transactions sequentially
response.transactions?.let { transactions ->
for ((index, tx) in transactions.withIndex()) {
val txDict = mutableMapOf<String, String>(
"from" to tx.from,
"to" to tx.to
)
tx.data?.let { txDict["data"] = it }
tx.value?.let { txDict["value"] = it }
val txResponse = portal.request(
chainId = "eip155:11155111",
method = PortalRequestMethod.eth_sendTransaction,
params = listOf(txDict)
)
println("Tx ${index + 1} hash: ${txResponse.result as? String}")
}
}
}.onFailure { error ->
println("Revoke (EVM) failed: ${error.message}")
}
} catch (e: Exception) {
println("Error revoking EVM delegation: ${e.message}")
}
}
Solana Revoke
lifecycleScope.launch {
try {
val request = RevokeDelegationRequest(
chain = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
token = "USDC",
delegateAddress = "8uzXpjP9zXRHqo6KGZaE6XrxnjarBsKTufVya7jHtyt5"
)
val result = portal.delegations.revoke(request)
result.onSuccess { response ->
// Sign and send Solana transactions sequentially
response.encodedTransactions?.let { encodedTxs ->
for ((index, encodedTx) in encodedTxs.withIndex()) {
val txResponse = portal.request(
chainId = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
method = PortalRequestMethod.sol_signAndSendTransaction,
params = listOf(encodedTx)
)
println("Tx ${index + 1} hash: ${txResponse.result as? String}")
}
}
}.onFailure { error ->
println("Revoke (SOL) failed: ${error.message}")
}
} catch (e: Exception) {
println("Error revoking Solana delegation: ${e.message}")
}
}
Always revoke unused delegations after completing operations to minimize security risks.
Transferring as a Delegate
Use transferFrom to transfer tokens from another address that has delegated spending permission to you.
EVM Transfer From
lifecycleScope.launch {
try {
val request = TransferFromRequest(
chain = "eip155:11155111",
token = "USDC",
fromAddress = "0x06ccd61bc37775140b0b039b392aa823c7cbeedd", // Token owner
toAddress = "0x5bd098a9368d142126e8b53a058b5c563714bc76", // Recipient
amount = "0.01"
)
val result = portal.delegations.transferFrom(request)
result.onSuccess { response ->
// Sign and send EVM transactions sequentially
response.transactions?.let { transactions ->
for ((index, tx) in transactions.withIndex()) {
val txDict = mutableMapOf<String, String>(
"from" to tx.from,
"to" to tx.to
)
tx.data?.let { txDict["data"] = it }
tx.value?.let { txDict["value"] = it }
val txResponse = portal.request(
chainId = "eip155:11155111",
method = PortalRequestMethod.eth_sendTransaction,
params = listOf(txDict)
)
println("Tx ${index + 1} hash: ${txResponse.result as? String}")
}
}
}.onFailure { error ->
println("TransferFrom (EVM) failed: ${error.message}")
}
} catch (e: Exception) {
println("Error transferring EVM delegation: ${e.message}")
}
}
Solana Transfer From
lifecycleScope.launch {
try {
val request = TransferFromRequest(
chain = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
token = "USDC",
fromAddress = "5qf7h6aJ47nfYkmtDW52LtCgBKEppf8CU1CmNXUJvPDD", // Token owner
toAddress = "CaeuusKjRDw2NctShW2gMhEcMxFYfHLUUGHh3Hui3Mae", // Recipient
amount = "0.01"
)
val result = portal.delegations.transferFrom(request)
result.onSuccess { response ->
// Sign and send Solana transactions sequentially
response.encodedTransactions?.let { encodedTxs ->
for ((index, encodedTx) in encodedTxs.withIndex()) {
val txResponse = portal.request(
chainId = "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
method = PortalRequestMethod.sol_signAndSendTransaction,
params = listOf(encodedTx)
)
println("Tx ${index + 1} hash: ${txResponse.result as? String}")
}
}
}.onFailure { error ->
println("TransferFrom (SOL) failed: ${error.message}")
}
} catch (e: Exception) {
println("Error transferring Solana delegation: ${e.message}")
}
}
Delegation Roles: fromAddress is the token owner who approved the delegation. Your wallet (the delegate) signs the transaction to transfer tokens from the owner to the toAddress recipient.
Supported Networks
Delegations work on all Portal-supported EVM and Solana chains:
- EVM: Ethereum, Polygon, Base, Arbitrum, Optimism, and all other EVM-compatible chains
- Solana: Solana Mainnet and Devnet
For a complete list, see Blockchain Support.
Next Steps