agent

package module
v0.8.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 20, 2026 License: Apache-2.0 Imports: 26 Imported by: 31

README

Go Agent for the Internet Computer

Go Version GoDoc Reference

go get github.com/aviate-labs/agent-go

Getting Started

The agent is a library that allows you to talk to the Internet Computer.

package main

import (
	"github.com/aviate-labs/agent-go"
	"log"
)

type (
	Account struct {
		Account string `ic:"account"`
	}

	Balance struct {
		E8S uint64 `ic:"e8s"`
	}
)

func main() {
	a, _ := agent.New(agent.DefaultConfig)

	var balance Balance
	if err := a.Query(
		principal.MustDecode("ryjl3-tyaaa-aaaaa-aaaba-cai"), "account_balance_dfx",
		[]any{Account{"9523dc824aa062dcd9c91b98f4594ff9c6af661ac96747daef2090b7fe87037d"}},
		[]any{&balance},
	); err != nil {
		log.Fatal(err)
	}

	_ = balance // Balance{E8S: 0}
}

Using an Identity

Supported identities are Ed25519, Secp256k1, and Prime256v1. By default, the agent uses the anonymous identity.

id, _ := identity.NewEd25519Identity(publicKey, privateKey)
config := agent.Config{
    Identity: id,
}
Using the Local Replica

If you are running a local replica, you can use the FetchRootKey option to fetch the root key from the replica.

u, _ := url.Parse("http://localhost:8000")
config := agent.Config{
    ClientConfig: []agent.ClientOption{agent.WithHostURL(u)},
    FetchRootKey: true,
    DisableSignedQueryVerification: true,
}

Packages

You can find the documentation for each package in the links below. Examples can be found throughout the documentation.

Package Name Links Description
agent README DOC A library to talk directly to the Replica.
candid DOC A Candid library for Golang.
certification DOC A Certification library for Golang.
gen DOC A library to generate Golang clients.
identity DOC A library that creates/manages identities.
principal DOC Generic Identifiers for the Internet Computer
----------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------
ic-go DOC Multiple auto-generated sub-modules to talk to the Internet Computer services
pocketic-go DOC A client library to talk to the PocketIC Server.

More dependencies in the go.mod file.

CLI

go install github.com/aviate-labs/agent-go/cmd/goic@latest

Read more here

Testing

This repository contains two types of tests: standard Go tests and PocketIC -dependent tests. The test suite runs a local PocketIC server using the installed pocket-ic-server to execute some end-to-end (e2e) tests. If pocket-ic-server is not installed, those specific tests will be skipped.

go test -v ./...

Reference Implementations

Documentation

Overview

Example (Anonymous_call)
a, _ := agent.New(agent.DefaultConfig)
var balance struct {
	E8S uint64 `ic:"e8s"`
}

accountID, _ := hex.DecodeString("9523dc824aa062dcd9c91b98f4594ff9c6af661ac96747daef2090b7fe87037d")
err := a.Call(LEDGER_PRINCIPAL, "account_balance", []any{
	struct {
		Account []byte `ic:"account"`
	}{Account: accountID},
}, []any{&balance})
fmt.Println(balance.E8S, err)
Output:
0 <nil>
Example (Anonymous_query)
a, _ := agent.New(agent.DefaultConfig)
var balance struct {
	E8S uint64 `ic:"e8s"`
}

accountID, _ := hex.DecodeString("9523dc824aa062dcd9c91b98f4594ff9c6af661ac96747daef2090b7fe87037d")
err := a.Query(LEDGER_PRINCIPAL, "account_balance", []any{
	struct {
		Account []byte `ic:"account"`
	}{Account: accountID},
}, []any{&balance})
fmt.Println(balance.E8S, err)
Output:
0 <nil>
Example (Json)
raw := `{"e8s":1}`
var balance struct {
	// Tags can be combined with json tags.
	E8S uint64 `ic:"e8s" json:"e8s"`
}
_ = json.Unmarshal([]byte(raw), &balance)
fmt.Println(balance.E8S)

a, _ := agent.New(agent.DefaultConfig)
accountID, _ := hex.DecodeString("9523dc824aa062dcd9c91b98f4594ff9c6af661ac96747daef2090b7fe87037d")
if err := a.Query(LEDGER_PRINCIPAL, "account_balance", []any{struct {
	Account []byte `json:"account"`
}{
	Account: accountID,
}}, []any{&balance}); err != nil {
	fmt.Println(err)
}
rawJSON, _ := json.Marshal(balance)
fmt.Println(string(rawJSON))
Output:
1
{"e8s":0}
Example (Query_ed25519)
id, _ := identity.NewRandomEd25519Identity()
ledgerID := principal.MustDecode("ryjl3-tyaaa-aaaaa-aaaba-cai")
a, _ := agent.New(agent.Config{Identity: id})
var balance struct {
	E8S uint64 `ic:"e8s"`
}

accountID, _ := hex.DecodeString("9523dc824aa062dcd9c91b98f4594ff9c6af661ac96747daef2090b7fe87037d")
_ = a.Query(ledgerID, "account_balance", []any{map[string]any{
	"account": accountID,
}}, []any{&balance})
fmt.Println(balance.E8S)
Output:
0
Example (Query_prime256v1)
id, _ := identity.NewRandomPrime256v1Identity()
a, _ := agent.New(agent.Config{Identity: id})
var balance struct {
	E8S uint64 `ic:"e8s"`
}

accountID, _ := hex.DecodeString("9523dc824aa062dcd9c91b98f4594ff9c6af661ac96747daef2090b7fe87037d")
_ = a.Query(LEDGER_PRINCIPAL, "account_balance", []any{map[string]any{
	"account": accountID,
}}, []any{&balance})
fmt.Println(balance.E8S)
Output:
0
Example (Query_secp256k1)
id, _ := identity.NewRandomSecp256k1Identity()
a, _ := agent.New(agent.Config{Identity: id})
var balance struct {
	E8S uint64 `ic:"e8s"`
}

accountID, _ := hex.DecodeString("9523dc824aa062dcd9c91b98f4594ff9c6af661ac96747daef2090b7fe87037d")
_ = a.Query(LEDGER_PRINCIPAL, "account_balance", []any{map[string]any{
	"account": accountID,
}}, []any{&balance})
fmt.Println(balance.E8S)
Output:
0

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultConfig = Config{}

DefaultConfig is the default configuration for an Agent.

Functions

func DiscoverRoutes added in v0.8.0

func DiscoverRoutes(a *Agent) ([]*url.URL, error)

DiscoverRoutes enumerates API boundary nodes on-chain and returns their https://<domain> URLs. Pair with RoundRobinRoute or RandomRoute to build a RouteProvider, then call Agent.Client().SetRouteProvider to use it.

Example:

a, _ := agent.New(agent.Config{})
hosts, _ := agent.DiscoverRoutes(a)
rp, _ := agent.RoundRobinRoute(hosts)
a.Client().SetRouteProvider(rp)

Types

type APIBoundaryNode added in v0.8.0

type APIBoundaryNode struct {
	NodeID      principal.Principal
	Domain      string
	IPv4Address string
	IPv6Address string
}

APIBoundaryNode describes a single API boundary node as published in the /api_boundary_nodes/<node_id> sub-tree of the IC state.

IPv4Address and IPv6Address are UTF-8 strings ("192.168.10.150" / "3002:0bd6:..."). Either may be empty if the node does not publish that address family.

type APIRequest added in v0.5.0

type APIRequest[In, Out any] struct {
	// contains filtered or unexported fields
}

func CreateAPIRequest added in v0.8.0

func CreateAPIRequest[In, Out any](
	a *Agent,
	marshal func(In) ([]byte, error),
	unmarshal func([]byte, Out) error,
	typ RequestType,
	canisterID principal.Principal,
	effectiveCanisterID principal.Principal,
	methodName string,
	in In,
) (*APIRequest[In, Out], error)

CreateAPIRequest creates a new api request to the given canister and method using a caller-supplied codec. Use this when neither Candid nor Protobuf fits and the pre-built CallRaw/QueryRaw helpers force an unwanted extra []byte hop.

marshal encodes the typed argument to wire bytes; unmarshal decodes the reply into the caller-owned out value. effectiveCanisterID is the principal used for routing (usually equal to canisterID; differs only for management-canister calls).

func (APIRequest[_, Out]) CallAndWait added in v0.5.0

func (c APIRequest[_, Out]) CallAndWait(out Out) error

CallAndWait calls a method on a canister and waits for the result.

func (APIRequest[In, Out]) Query added in v0.5.0

func (q APIRequest[In, Out]) Query(out Out, skipVerification bool) error

Query calls a method on a canister and unmarshals the result into the given values.

func (*APIRequest[In, Out]) WithEffectiveCanisterID added in v0.5.0

func (c *APIRequest[In, Out]) WithEffectiveCanisterID(canisterID principal.Principal) *APIRequest[In, Out]

WithEffectiveCanisterID sets the effective canister ID for the Call.

type Agent

type Agent struct {
	// contains filtered or unexported fields
}

Agent is a client for the Internet Computer.

func New

func New(cfg Config) (*Agent, error)

New returns a new Agent based on the given configuration.

func (Agent) Call

func (a Agent) Call(canisterID principal.Principal, methodName string, in []any, out []any) error

Call calls a method on a canister and unmarshals the result into the given values.

func (Agent) CallProto added in v0.4.5

func (a Agent) CallProto(canisterID principal.Principal, methodName string, in, out proto.Message) error

CallProto calls a method on a canister and unmarshals the result into the given proto message.

func (Agent) CallRaw added in v0.2.0

func (a Agent) CallRaw(canisterID principal.Principal, methodName string, arg []byte) ([]byte, error)

CallRaw submits an update call with an opaque argument and returns the raw reply bytes. Neither the argument nor the reply is interpreted.

Example:

reply, err := a.CallRaw(canisterID, "ingest", cborBytes)

func (Agent) CallWithEffectiveCanisterID added in v0.8.2

func (a Agent) CallWithEffectiveCanisterID(canisterID, effectiveCanisterID principal.Principal, methodName string, in, out []any) error

CallWithEffectiveCanisterID is like Call but lets the caller supply the effective canister ID. Needed for management-canister methods whose args carry no canister_id (create_canister, provisional_create_canister_with_cycles).

func (Agent) Client added in v0.4.4

func (a Agent) Client() *Client

Client returns the underlying Client of the Agent.

func (*Agent) CreateCandidAPIRequest added in v0.5.0

func (a *Agent) CreateCandidAPIRequest(typ RequestType, canisterID principal.Principal, methodName string, args ...any) (*CandidAPIRequest, error)

CreateCandidAPIRequest creates a new api request to the given canister and method.

func (*Agent) CreateProtoAPIRequest added in v0.5.0

func (a *Agent) CreateProtoAPIRequest(typ RequestType, canisterID principal.Principal, methodName string, message proto.Message) (*ProtoAPIRequest, error)

CreateProtoAPIRequest creates a new api request to the given canister and method.

func (*Agent) CreateRawAPIRequest added in v0.8.0

func (a *Agent) CreateRawAPIRequest(typ RequestType, canisterID principal.Principal, methodName string, arg []byte) (*RawAPIRequest, error)

CreateRawAPIRequest creates a new api request to the given canister and method without applying any codec to the argument or reply. Callers that wire their own encoding (CBOR, MessagePack, ...) and skip Candid use this.

Example:

req, _ := a.CreateRawAPIRequest(agent.RequestTypeCall, canisterID, "ingest", cborBytes)
var reply []byte
_ = req.CallAndWait(&reply)

func (Agent) GetAPIBoundaryNodes added in v0.8.0

func (a Agent) GetAPIBoundaryNodes() ([]APIBoundaryNode, error)

GetAPIBoundaryNodes enumerates the API boundary nodes published on-chain. This is the authoritative way to discover boundary nodes; hardcoding icp0.io/ic0.app is a fallback for bootstrapping only.

func (Agent) GetCanisterControllers

func (a Agent) GetCanisterControllers(canisterID principal.Principal) ([]principal.Principal, error)

GetCanisterControllers returns the list of principals that can control the given canister.

func (Agent) GetCanisterInfo

func (a Agent) GetCanisterInfo(canisterID principal.Principal, subPath string) ([]byte, error)

GetCanisterInfo returns the raw certificate for the given canister based on the given sub-path.

func (Agent) GetCanisterMetadata added in v0.3.1

func (a Agent) GetCanisterMetadata(canisterID principal.Principal, subPath string) ([]byte, error)

func (Agent) GetCanisterModuleHash

func (a Agent) GetCanisterModuleHash(canisterID principal.Principal) ([]byte, error)

GetCanisterModuleHash returns the module hash for the given canister.

func (Agent) GetRootKey added in v0.3.1

func (a Agent) GetRootKey() []byte

GetRootKey returns the root key of the host.

func (Agent) GetSubnetMetrics added in v0.5.0

func (a Agent) GetSubnetMetrics(subnetID principal.Principal) (*SubnetMetrics, error)

func (Agent) GetSubnets added in v0.5.0

func (a Agent) GetSubnets() ([]principal.Principal, error)

func (Agent) GetSubnetsInfo added in v0.5.0

func (a Agent) GetSubnetsInfo() ([]SubnetInfo, error)

func (Agent) GetTime added in v0.8.0

func (a Agent) GetTime(canisterID principal.Principal) (time.Time, error)

GetTime returns the certified IC time as reported by the subnet that serves the given canister.

Note: /time is a per-subnet value (the subnet's wall clock at the moment it produced the read_state certificate), not a single global IC clock. Different subnets may disagree by a few seconds; callers that need a specific subnet's view should pass a canister hosted on it.

func (Agent) Query

func (a Agent) Query(canisterID principal.Principal, methodName string, in, out []any) error

Query calls a method on a canister and unmarshals the result into the given values.

func (Agent) QueryProto added in v0.4.5

func (a Agent) QueryProto(canisterID principal.Principal, methodName string, in, out proto.Message) error

QueryProto calls a method on a canister and unmarshals the result into the given proto message. Verifies query signatures by default; set Config.DisableSignedQueryVerification to opt out.

func (Agent) QueryRaw added in v0.2.0

func (a Agent) QueryRaw(canisterID principal.Principal, methodName string, arg []byte) ([]byte, error)

QueryRaw is the query-call counterpart of CallRaw. Neither the argument nor the reply is interpreted.

Example:

reply, err := a.QueryRaw(canisterID, "lookup", cborBytes)

func (Agent) QueryWithEffectiveCanisterID added in v0.8.2

func (a Agent) QueryWithEffectiveCanisterID(canisterID, effectiveCanisterID principal.Principal, methodName string, in, out []any) error

QueryWithEffectiveCanisterID is like Query but lets the caller supply the effective canister ID. Symmetric with CallWithEffectiveCanisterID.

func (Agent) ReadStateCertificate added in v0.4.4

func (a Agent) ReadStateCertificate(canisterID principal.Principal, path [][]hashtree.Label) (hashtree.Node, error)

ReadStateCertificate reads the certificate state of the given canister at the given path.

func (Agent) RequestStatus

func (a Agent) RequestStatus(ecID principal.Principal, requestID RequestID) ([]byte, hashtree.Node, error)

RequestStatus returns the status of the request with the given ID.

func (Agent) Sender

func (a Agent) Sender() principal.Principal

Sender returns the principal that is sending the requests.

type CandidAPIRequest added in v0.5.0

type CandidAPIRequest = APIRequest[[]any, []any]

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a client for the IC agent.

func NewClient

func NewClient(options ...ClientOption) Client

NewClient creates a new client based on the given configuration.

func (Client) Call added in v0.4.4

func (c Client) Call(ctx context.Context, canisterID principal.Principal, data []byte) ([]byte, error)

func (Client) Query added in v0.4.4

func (c Client) Query(ctx context.Context, canisterID principal.Principal, data []byte) ([]byte, error)

func (Client) ReadState added in v0.4.4

func (c Client) ReadState(ctx context.Context, canisterID principal.Principal, data []byte) ([]byte, error)

func (Client) ReadSubnetState added in v0.5.0

func (c Client) ReadSubnetState(ctx context.Context, subnetID principal.Principal, data []byte) ([]byte, error)

func (*Client) SetRouteProvider added in v0.8.0

func (c *Client) SetRouteProvider(rp RouteProvider)

SetRouteProvider replaces the route provider used to pick a host URL for each outgoing request. Intended for runtime boundary-node selection (e.g. via DiscoverRoutes + RoundRobinRoute); not safe to call concurrently with in-flight requests.

func (Client) Status

func (c Client) Status() (*Status, error)

Status returns the status of the IC.

Example
package main

import (
	"fmt"
	"net/url"

	"github.com/aviate-labs/agent-go"
)

var ic0URL, _ = url.Parse("https://icp-api.io")

func main() {
	c := agent.NewClient(agent.WithHostURL(ic0URL))
	status, _ := c.Status()
	fmt.Printf("%x...%x\n", status.RootKey[:4], status.RootKey[len(status.RootKey)-4:])
}
Output:
30818230...1a0baaae

type ClientOption added in v0.7.0

type ClientOption func(c *Client)

func WithHostURL added in v0.7.0

func WithHostURL(host *url.URL) ClientOption

func WithHttpClient added in v0.7.0

func WithHttpClient(client *http.Client) ClientOption

func WithLogger added in v0.7.0

func WithLogger(logger Logger) ClientOption

type Config added in v0.2.0

type Config struct {
	// Identity is the identity used by the Agent.
	Identity identity.Identity
	// IngressExpiry is the duration for which an ingress message is valid.
	// The default is set to 5 minutes.
	IngressExpiry time.Duration
	// ClientConfig is the configuration for the underlying Client.
	ClientConfig []ClientOption
	// FetchRootKey determines whether the root key should be fetched from the IC.
	FetchRootKey bool
	// Logger is the logger used by the Agent.
	Logger Logger
	// PollDelay is the delay between polling for a response.
	PollDelay time.Duration
	// PollTimeout is the timeout for polling for a response.
	PollTimeout time.Duration
	// DisableSignedQueryVerification disables the verification of signed queries.
	DisableSignedQueryVerification bool
	// RouteProvider, if non-nil, replaces the per-request host-URL provider
	// configured by ClientConfig. Use StaticRoute, RoundRobinRoute, or
	// RandomRoute for the built-in policies, or implement RouteProvider for
	// a custom one. To use on-chain discovery, call DiscoverRoutes against
	// a freshly constructed agent and pass the result to a RouteProvider.
	RouteProvider RouteProvider
}

Config is the configuration for an Agent.

type Envelope

type Envelope struct {
	Content      Request `cbor:"content,omitempty"`
	SenderPubKey []byte  `cbor:"sender_pubkey,omitempty"`
	SenderSig    []byte  `cbor:"sender_sig,omitempty"`
}

Envelope is a wrapper for a Request that includes the sender's public key and signature.

type Logger added in v0.4.0

type Logger interface {
	Printf(format string, v ...any)
}

type NodeInfo added in v0.5.0

type NodeInfo struct {
	NodeID    principal.Principal
	PublicKey []byte
}

type NoopLogger added in v0.4.3

type NoopLogger struct{}

func (NoopLogger) Printf added in v0.4.3

func (l NoopLogger) Printf(format string, v ...any)

type ProtoAPIRequest added in v0.5.0

type ProtoAPIRequest = APIRequest[proto.Message, proto.Message]

type RawAPIRequest added in v0.8.0

type RawAPIRequest = APIRequest[[]byte, *[]byte]

RawAPIRequest is an APIRequest with no codec applied to its argument or reply. Out is *[]byte (not []byte) because the unmarshal function replaces the slice header itself (`*out = raw`) rather than writing through it, which requires indirection. CallRaw/QueryRaw hide that detail and return []byte.

type Request

type Request struct {
	// The type of the request. This is used to distinguish between query, call and read_state requests.
	Type RequestType
	// The user who issued the request.
	Sender principal.Principal
	// Arbitrary user-provided data, typically randomly generated. This can be
	// used to create distinct requests with otherwise identical fields.
	Nonce []byte
	// An upper limit on the validity of the request, expressed in nanoseconds
	// since 1970-01-01 (like ic0.time()).
	IngressExpiry uint64
	// The principal of the canister to call.
	CanisterID principal.Principal
	// Name of the canister method to call.
	MethodName string
	// Argument to pass to the canister method.
	Arguments []byte
	// A list of paths, where a path is itself a sequence of blobs.
	Paths [][]hashtree.Label
}

Request is the request to the agent. DOCS: https://smartcontracts.org/docs/interface-spec/index.html#http-call

func (*Request) MarshalCBOR added in v0.2.0

func (r *Request) MarshalCBOR() ([]byte, error)

MarshalCBOR implements the CBOR marshaler interface.

type RequestID

type RequestID [32]byte

RequestID is the request ID.

func NewRequestID

func NewRequestID(req Request) RequestID

NewRequestID creates a new request ID. DOCS: https://smartcontracts.org/docs/interface-spec/index.html#request-id

func (RequestID) Sign

func (r RequestID) Sign(id identity.Identity) ([]byte, error)

Sign signs the request ID with the given identity.

type RequestType

type RequestType = string

RequestType is the type of request.

const (
	// RequestTypeCall is a call request.
	RequestTypeCall RequestType = "call"
	// RequestTypeQuery is a query request.
	RequestTypeQuery RequestType = "query"
	// RequestTypeReadState is a read state request.
	RequestTypeReadState RequestType = "read_state"
)

type Response

type Response struct {
	Status     string              `cbor:"status"`
	Reply      cbor.RawMessage     `cbor:"reply"`
	RejectCode uint64              `cbor:"reject_code"`
	RejectMsg  string              `cbor:"reject_message"`
	ErrorCode  string              `cbor:"error_code"`
	Signatures []ResponseSignature `cbor:"signatures"`
}

Response is the response from the agent.

type ResponseSignature added in v0.5.0

type ResponseSignature struct {
	Timestamp int64               `cbor:"timestamp"`
	Signature []byte              `cbor:"signature"`
	Identity  principal.Principal `cbor:"identity"`
}

type RouteProvider added in v0.8.0

type RouteProvider interface {
	// Route returns the host URL (scheme + host, no path) for the next request.
	Route() (*url.URL, error)
}

RouteProvider supplies the boundary-node URL for each outgoing request. Implementations are called once per HTTP request and must be safe for concurrent use.

func RandomRoute added in v0.8.0

func RandomRoute(hosts []*url.URL) (RouteProvider, error)

RandomRoute returns a RouteProvider that picks a uniformly random host on each call using crypto/rand.

func RoundRobinRoute added in v0.8.0

func RoundRobinRoute(hosts []*url.URL) (RouteProvider, error)

RoundRobinRoute returns a RouteProvider that cycles through the given hosts in order, wrapping at the end. Safe for concurrent use.

func StaticRoute added in v0.8.0

func StaticRoute(host *url.URL) RouteProvider

StaticRoute returns a RouteProvider that always serves the given URL.

type Status

type Status struct {
	// The public key (a DER-encoded BLS key) of the root key of this Internet Computer instance.
	RootKey []byte `cbor:"root_key"`
}

Status describes various status fields of the Internet Computer.

type SubnetInfo added in v0.5.0

type SubnetInfo struct {
	SubnetID       principal.Principal
	PublicKey      []byte
	CanisterRanges certification.CanisterRanges
	Nodes          []NodeInfo
}

type SubnetMetrics added in v0.5.0

type SubnetMetrics struct {
	NumCanisters            uint64
	CanisterStateBytes      uint64
	ConsumedCyclesTotal     big.Int
	UpdateTransactionsTotal uint64
}

func (*SubnetMetrics) UnmarshalCBOR added in v0.5.0

func (m *SubnetMetrics) UnmarshalCBOR(bytes []byte) error

Directories

Path Synopsis
did
idl
internal/candid
Package candid is autogenerated by https://github.com/0x51-dev/upeg.
Package candid is autogenerated by https://github.com/0x51-dev/upeg.
internal/ctest
Package ctest is autogenerated by https://github.com/0x51-dev/upeg.
Package ctest is autogenerated by https://github.com/0x51-dev/upeg.
internal/cvalue
Package cvalue is autogenerated by https://github.com/0x51-dev/upeg.
Package cvalue is autogenerated by https://github.com/0x51-dev/upeg.
bls
http/certexp
Package certexp is autogenerated by https://github.com/0x51-dev/upeg.
Package certexp is autogenerated by https://github.com/0x51-dev/upeg.
ii
clients
cmd
goic command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL