curl --request POST \
--url https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"calls": [
{
"to": "0xd2b58253741d22bc530b214f8fe81bf1a78a72b1",
"value": "0"
},
{
"to": "0x9533731C8224CEc3E53D76B72b590D347d9257eC",
"value": "0",
"data": "0x"
}
]
}
'import requests
url = "https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation"
payload = { "calls": [
{
"to": "0xd2b58253741d22bc530b214f8fe81bf1a78a72b1",
"value": "0"
},
{
"to": "0x9533731C8224CEc3E53D76B72b590D347d9257eC",
"value": "0",
"data": "0x"
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
calls: [
{to: '0xd2b58253741d22bc530b214f8fe81bf1a78a72b1', value: '0'},
{to: '0x9533731C8224CEc3E53D76B72b590D347d9257eC', value: '0', data: '0x'}
]
})
};
fetch('https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'calls' => [
[
'to' => '0xd2b58253741d22bc530b214f8fe81bf1a78a72b1',
'value' => '0'
],
[
'to' => '0x9533731C8224CEc3E53D76B72b590D347d9257eC',
'value' => '0',
'data' => '0x'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation"
payload := strings.NewReader("{\n \"calls\": [\n {\n \"to\": \"0xd2b58253741d22bc530b214f8fe81bf1a78a72b1\",\n \"value\": \"0\"\n },\n {\n \"to\": \"0x9533731C8224CEc3E53D76B72b590D347d9257eC\",\n \"value\": \"0\",\n \"data\": \"0x\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"calls\": [\n {\n \"to\": \"0xd2b58253741d22bc530b214f8fe81bf1a78a72b1\",\n \"value\": \"0\"\n },\n {\n \"to\": \"0x9533731C8224CEc3E53D76B72b590D347d9257eC\",\n \"value\": \"0\",\n \"data\": \"0x\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"calls\": [\n {\n \"to\": \"0xd2b58253741d22bc530b214f8fe81bf1a78a72b1\",\n \"value\": \"0\"\n },\n {\n \"to\": \"0x9533731C8224CEc3E53D76B72b590D347d9257eC\",\n \"value\": \"0\",\n \"data\": \"0x\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"data": {
"userOperation": "{\"sender\":\"0xF86e3fAe8443e4494a689fcDd4C8e6C76c1b2C96\",\"nonce\":\"0x5\",\"initCode\":\"0x\",\"callData\":\"0x34fcd5be...\",\"paymasterAndData\":\"0x\",\"signature\":\"0x00000000ffff...\",\"maxFeePerGas\":\"0x58556415\",\"maxPriorityFeePerGas\":\"0x15d0ea4\",\"callGasLimit\":\"0x3f9b\",\"verificationGasLimit\":\"0x13d75\",\"preVerificationGas\":\"0xc808\"}",
"userOpHash": "0xba1f6f1fe76b7a39d660bd02f54a3de5eae99ab413a95246e394c06786003847"
},
"metadata": {
"chainId": "eip155:11155111",
"totalGas": "148760",
"maxFeePerGas": "1481991189",
"estimatedGasCostWei": "220461009275640"
}
}{
"error": "Account Abstraction is not enabled, contact support to enable it"
}{
"error": "<string>"
}{
"error": "Policy violation: amount exceeds limit (Max transfer)",
"policyViolation": {
"policyId": "policyId",
"policyName": "Max transfer",
"reason": "amount exceeds limit (Max transfer)",
"ruleId": "ruleId"
}
}Build a user operation
Builds an ERC-4337 user operation (EntryPoint v0.6) for an Account Abstraction
client from one or more calls, which the smart account executes in order.
Returns the user operation and the hash to sign. Sign userOpHash with the
Enclave MPC API POST /v1/raw/sign/SECP256K1 (or rawSign in the SDKs), then
submit both with broadcast-user-operation.
Gas is sponsored when the environment has gas sponsorship configured for the
chain; otherwise paymasterAndData is 0x and the smart account pays for gas.
The environment’s transaction policies are evaluated on every call.
curl --request POST \
--url https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"calls": [
{
"to": "0xd2b58253741d22bc530b214f8fe81bf1a78a72b1",
"value": "0"
},
{
"to": "0x9533731C8224CEc3E53D76B72b590D347d9257eC",
"value": "0",
"data": "0x"
}
]
}
'import requests
url = "https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation"
payload = { "calls": [
{
"to": "0xd2b58253741d22bc530b214f8fe81bf1a78a72b1",
"value": "0"
},
{
"to": "0x9533731C8224CEc3E53D76B72b590D347d9257eC",
"value": "0",
"data": "0x"
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
calls: [
{to: '0xd2b58253741d22bc530b214f8fe81bf1a78a72b1', value: '0'},
{to: '0x9533731C8224CEc3E53D76B72b590D347d9257eC', value: '0', data: '0x'}
]
})
};
fetch('https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'calls' => [
[
'to' => '0xd2b58253741d22bc530b214f8fe81bf1a78a72b1',
'value' => '0'
],
[
'to' => '0x9533731C8224CEc3E53D76B72b590D347d9257eC',
'value' => '0',
'data' => '0x'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation"
payload := strings.NewReader("{\n \"calls\": [\n {\n \"to\": \"0xd2b58253741d22bc530b214f8fe81bf1a78a72b1\",\n \"value\": \"0\"\n },\n {\n \"to\": \"0x9533731C8224CEc3E53D76B72b590D347d9257eC\",\n \"value\": \"0\",\n \"data\": \"0x\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"calls\": [\n {\n \"to\": \"0xd2b58253741d22bc530b214f8fe81bf1a78a72b1\",\n \"value\": \"0\"\n },\n {\n \"to\": \"0x9533731C8224CEc3E53D76B72b590D347d9257eC\",\n \"value\": \"0\",\n \"data\": \"0x\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.portalhq.io/api/v3/clients/me/chains/{chain}/assets/send/build-user-operation")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"calls\": [\n {\n \"to\": \"0xd2b58253741d22bc530b214f8fe81bf1a78a72b1\",\n \"value\": \"0\"\n },\n {\n \"to\": \"0x9533731C8224CEc3E53D76B72b590D347d9257eC\",\n \"value\": \"0\",\n \"data\": \"0x\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"data": {
"userOperation": "{\"sender\":\"0xF86e3fAe8443e4494a689fcDd4C8e6C76c1b2C96\",\"nonce\":\"0x5\",\"initCode\":\"0x\",\"callData\":\"0x34fcd5be...\",\"paymasterAndData\":\"0x\",\"signature\":\"0x00000000ffff...\",\"maxFeePerGas\":\"0x58556415\",\"maxPriorityFeePerGas\":\"0x15d0ea4\",\"callGasLimit\":\"0x3f9b\",\"verificationGasLimit\":\"0x13d75\",\"preVerificationGas\":\"0xc808\"}",
"userOpHash": "0xba1f6f1fe76b7a39d660bd02f54a3de5eae99ab413a95246e394c06786003847"
},
"metadata": {
"chainId": "eip155:11155111",
"totalGas": "148760",
"maxFeePerGas": "1481991189",
"estimatedGasCostWei": "220461009275640"
}
}{
"error": "Account Abstraction is not enabled, contact support to enable it"
}{
"error": "<string>"
}{
"error": "Policy violation: amount exceeds limit (Max transfer)",
"policyViolation": {
"policyId": "policyId",
"policyName": "Max transfer",
"reason": "amount exceeds limit (Max transfer)",
"ruleId": "ruleId"
}
}Authorizations
Client API Key or Client Session Token (CST). Pass as a Bearer token in the Authorization header.
Path Parameters
The blockchain chain identifier. Use either a friendly name or CAIP-2 format.
When using CAIP-2 format in URLs, ensure the colon (:) is URI-encoded as %3A.
Supported chains:
ethereum(eip155:1)sepolia(eip155:11155111)base(eip155:8453)base-sepolia(eip155:84532)polygon(eip155:137)polygon-mumbai(eip155:80001)celo(eip155:42220)celo-alfajores(eip155:44787)monad(eip155:143)monad-testnet(eip155:10143)solana(solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp)solana-devnet(solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1)tron(tron:mainnet)tron-nile(tron:nile)tron-shasta(tron:shasta)stellar(stellar:pubnet)stellar-testnet(stellar:testnet)bitcoin-segwit(bip122:000000000019d6689c085ae165831e93-p2wpkh)bitcoin-segwit-testnet(bip122:000000000933ea01ad0ee984209779ba-p2wpkh)bitcoin-p2wpkh(bip122:000000000019d6689c085ae165831e93-p2wpkh)bitcoin-p2wpkh-testnet(bip122:000000000933ea01ad0ee984209779ba-p2wpkh)
Body
The calls the smart account makes, in order.
1Show child attributes
Show child attributes
Was this page helpful?