Packages
forge_sdk
0.27.1
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.7
1.0.6
1.0.4
1.0.4-p1
1.0.4-p0
1.0.3
1.0.2
1.0.2-p1
1.0.1
1.0.1-p1
1.0.0
0.40.6
0.40.5
0.40.4
0.40.3
0.40.2
0.40.1
0.40.0
0.39.1
0.39.0
0.38.6
0.38.5
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.5
0.37.4
0.37.3
0.37.2
0.37.1
0.37.0
0.34.0
0.33.2
0.33.1
0.33.0
0.32.2
0.32.1
0.32.0
0.31.1
0.31.0
0.30.0
0.29.1
0.29.0
0.28.3
0.28.2
0.28.1
0.28.0
0.27.4
0.27.3
0.27.2
0.27.1
0.27.0
0.26.6
0.26.5
0.26.4
0.26.3
0.26.1
0.26.0
Elixir / Erlang version of the SDK for Forge framework.
Current section
Files
Jump to
Current section
Files
lib/forge_sdk.ex
defmodule ForgeSdk do
@moduledoc """
Forge is a full fledge blockchain framework for developers to build decentralized applications easily. Forge gives the developers / operators the freedom to launch their own customized chains with their own application logic.
This is the Elixir / Erlang version of the SDK for Forge framework. To develop applications on top of the forge, you shall pick up a SDK. Forge SDK is intended to make the interaction with the chain built by Forge as easy as possible. All SDK APIs are organized into the following categories:
- chain APIs: provide the client wrapper for `chain` related gRPC
- wallet APIs: provide the client wrapper for `wallet` related gRPC
- state APIs: provide the client wrapper for `state` related gRPC
- subscription APIs: provide the client wrapper for `subscription` related gRPC
- transaction APIs: the gRPC for transaction is `send_tx`, this set of APIs provide helper functions to make building and sending a tx easy.
- misc APIs: parsing `configuration`, initialize sdk and more.
"""
alias ForgeAbi.{
# other
AccountState,
AssetState,
BlockInfo,
BlockInfoSimple,
ChainInfo,
ForgeState,
NetInfo,
NodeInfo,
PageInfo,
ProtocolState,
Transaction,
TransactionInfo,
ValidatorsInfo,
WalletInfo,
# request response
RequestCreateTx,
RequestCreateWallet,
RequestGetAccountState,
RequestGetAssetState,
RequestGetBlock,
RequestGetBlocks,
RequestGetTx,
RequestLoadWallet,
RequestMultisig,
RequestGetProtocolState,
RequestRecoverWallet,
RequestRemoveWallet,
RequestSendTx,
RequestSubscribe,
RequestUnsubscribe,
ResponseSubscribe
}
alias ForgeSdk.{Configuration.Helper, Display, File, Loader, Rpc, Util, Wallet}
alias GRPC.Channel
@doc """
Migrate a `wallet` from old address (as well as pk, sk) to a new address.
## Example
old_wallet = ForgeSdk.create_wallet()
declare_tx = ForgeAbi.DeclareTx.new(moniker: "sisyphus")
ForgeSdk.declare(declare_tx, wallet: old_wallet)
new_wallet = ForgeSdk.create_wallet()
itx = ForgeAbi.AccountMigrateTx.new(pk: new_wallet.pk, address: new_wallet.address)
ForgeSdk.account_migrate(itx, wallet: old_wallet)
"""
@spec account_migrate(map(), Keyword.t()) :: String.t() | {:error, term()}
defdelegate account_migrate(itx, opts), to: Rpc
@doc """
Acquire an `asset` from an existing asset factory.
## Example
w = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "theater"), wallet: w)
w1 = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "tyr"), wallet: w)
# Note application shall already registered `Ticket` into Forge via `deploy_protocol`.
factory = %{
description: "movie ticket factory",
limit: 5,
price: ForgeAbi.token_to_unit(1),
template: ~s({
"row": "{{ row }}",
"seat": "{{ seat }}",
"time": "11:00am 04/30/2019",
"room": "4"
}),
allowed_spec_args: ["row", "seat"],
asset_name: "Ticket",
attributes: %ForgeAbi.AssetAttributes{
transferrable: true,
ttl: 3600 * 3
}
}
ForgeSdk.create_asset_factory("Avenerages: Endgame", factory, wallet: w)
specs =
Enum.map(["0", "2"], fn seat ->
apply(ForgeAbi.AssetSpec, :new, [%{data: ~s({"row": "15", "seat": "\#{seat}"})}])
end)
itx = ForgeAbi.AcquireAssetTx.new(to: address, specs: specs)
ForgeSdk.acquire_asset(itx, wallet: w1)
"""
@spec acquire_asset(map(), Keyword.t()) :: String.t() | {:error, term()}
defdelegate acquire_asset(itx, opts), to: Rpc
# defdelegate consensus_upgrade(itx, opts), to: Rpc
@spec prepare_consume_asset(map(), Keyword.t()) :: Transaction.t() | {:error, term()}
defdelegate prepare_consume_asset(itx, opts), to: Rpc
@spec finalize_consume_asset(Transaction.t(), String.t(), WalletInfo.t()) ::
{:error, any()} | Transaction.t()
defdelegate finalize_consume_asset(tx, asset_address, wallet), to: Rpc
@doc """
Create a new `asset`.
## Example
wallet = ForgeSdk.create_wallet()
declare_tx = ForgeAbi.DeclareTx.new(moniker: "sisyphus")
ForgeSdk.declare(declare_tx, wallet: wallet)
ticket = ForgeAbi.Ticket.new(row: "K", seat: "22", room: "3A", time: "03/04/2019 11:00am PST",
name: "Avengers: Endgame")
itx = ForgeAbi.CreateAsset.new(data: ForgeSdk.encode_any!(ticket), readonly: true,
transferrable: true, ttl: 7200)
ForgeSdk.create_asset(itx, wallet: wallet)
"""
@spec create_asset(map(), Keyword.t()) :: String.t() | {:error, term()}
defdelegate create_asset(itx, opts), to: Rpc
@doc """
Create a new `asset factory`.
## Example
w = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "theater"), wallet: w)
w1 = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "tyr"), wallet: w)
# Note application shall already registered `Ticket` into Forge via `deploy_protocol`.
factory = %{
description: "movie ticket factory",
limit: 5,
price: ForgeAbi.token_to_unit(1),
template: ~s({
"row": "{{ row }}",
"seat": "{{ seat }}",
"time": "11:00am 04/30/2019",
"room": "4"
}),
allowed_spec_args: ["row", "seat"],
asset_name: "Ticket",
attributes: %ForgeAbi.AssetAttributes{
transferrable: true,
ttl: 3600 * 3
}
}
ForgeSdk.create_asset_factory("Avenerages: Endgame", factory, wallet: w)
"""
@spec create_asset_factory(String.t(), map(), Keyword.t()) :: String.t() | {:error, term()}
defdelegate create_asset_factory(moniker, factory, opts), to: Rpc
@doc """
Declare a `wallet` to the chain.
## Example
wallet = ForgeSdk.create_wallet()
declare_tx = ForgeAbi.DeclareTx.new(moniker: "sisyphus")
ForgeSdk.declare(declare_tx, wallet: wallet)
"""
@spec declare(map(), Keyword.t()) :: String.t() | {:error, term()}
defdelegate declare(itx, opts), to: Rpc
# defdelegate declare_file(itx, opts), to: Rpc
@doc """
Deploy a `new protocol` into the chain at a given `block height`.
## Example
itx = data |> Base.url_decode64!(padding: false) |> ForgeAbi.DeployProtocolTx.decode()
ForgeSdk.deploy_protocol(itx, wallet: wallet)
"""
@spec deploy_protocol(map(), Keyword.t()) :: String.t() | {:error, term()}
defdelegate deploy_protocol(itx, opts), to: Rpc
defdelegate deposit_tether(itx, opts), to: Rpc
@spec prepare_exchange(map(), Keyword.t()) :: Transaction.t() | {:error, term()}
defdelegate prepare_exchange(itx, opts), to: Rpc
@spec finalize_exchange(Transaction.t(), WalletInfo.t()) :: {:error, term()} | Transaction.t()
defdelegate finalize_exchange(tx, wallet), to: Rpc
defdelegate exchange_tether(itx, opts), to: Rpc
# defdelegate sys_upgrade(itx, opts), to: Rpc
@doc """
One wallet can poke in a **daily basis** to get some free tokens (for test chains only), `nonce` should be 0.
## Example
w = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "alice"), wallet: w)
itx = ForgeAbi.PokeTx.new(date: "2019-03-13", address: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz")
req = ForgeAbi.RequestCreateTx.new(from: w.address, itx: ForgeAbi.encode_any!(:poke, itx),
nonce: 0, token: t, wallet: w)
tx = ForgeSdk.create_tx(req)
hash = ForgeSdk.send_tx(tx: tx)
"""
defdelegate poke(itx, opts), to: Rpc
defdelegate stake(itx, opts), to: Rpc
# defdelegate sys_upgrade(itx, opts), to: Rpc
@doc """
Transfer `tokens or/and assets` from one wallet to another.
## Example
w1 = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "alice"), wallet: w1)
w2 = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "bob"), wallet: w2)
data = Google.Protobuf.Any.new(type_url: "test_asset", value: "hello world")
itx = ForgeSdk.encode_any!(TransferTx.new(to: w2.address, value: new_unit(100)))
ForgeSdk.transfer(req, wallet: w1)
"""
@spec transfer(map(), Keyword.t()) :: String.t() | {:error, term()}
defdelegate transfer(itx, opts), to: Rpc
@doc """
Update an existing `asset`.
## Example
wallet = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "alice"), wallet: wallet)
post = ForgeAbi.Post.new(title: "a new post", content: "hello world!")
itx = ForgeAbi.CreateAsset.new(itx: ForgeSdk.encode_any!(post))
hash = ForgeSdk.create_asset(itx, wallet: wallet)
address = ForgeSdk.get_address(hash)
new_post = ForgeAbi.Post.new(title: "a new post", content: "Yeah!")
itx = ForgeAbi.UpdateAssetTx.new(data: ForgeSdk.encode_any!(post), address: address)
ForgeSdk.get_asset_state(address: address)
"""
@spec update_asset(map(), Keyword.t()) :: String.t() | {:error, term()}
defdelegate update_asset(itx, opts), to: Rpc
@doc """
Upgrade the `node` to a new version at a given `block height`.
## Example
itx = ForgeAbi.UpgradeNodeTx.new(version: "0.26.0", height: 12000)
ForgeSdk.upgrade_node(itx, wallet: wallet)
"""
@spec upgrade_node(map(), Keyword.t()) :: String.t() | {:error, term()}
defdelegate upgrade_node(itx, opts), to: Rpc
# defdelegate upgrade_task(itx, opts), to: Rpc
defdelegate withdraw_tether(itx, opts), to: Rpc
defdelegate approve_tether(itx, opts), to: Rpc
defdelegate revoke_tether(itx, opts), to: Rpc
# extended tx helper
defdelegate stake_for_node(address, amount, opts), to: Rpc
# chain related
@doc """
One wallet can check in a daily basis to get some free tokens (for test chains only), `nonce` should be 0.
## Example
w = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "alice"), wallet: w)
ForgeSdk.checkin(wallet: w)
"""
@spec checkin(Keyword.t()) :: String.t() | {:error, term()}
defdelegate checkin(opts), to: Rpc
# RPC
# chain related
@doc """
Retrieve the current status of the chain.
## Example
ForgeSdk.get_chain_info()
"""
@spec get_chain_info(Channel.t() | nil) :: ChainInfo.t() | {:error, term()}
defdelegate get_chain_info(chan \\ nil), to: Rpc
@doc """
Retrive the current status of the node.
## Example
ForgeSdk.get_node_info()
"""
@spec get_node_info(Channel.t() | nil) :: NodeInfo.t() | {:error, term()}
defdelegate get_node_info(chan \\ nil), to: Rpc
@doc """
Retrieve the `network info`.
## Example
ForgeSdk.get_net_info()
"""
@spec get_net_info(Channel.t() | nil) :: NetInfo.t() | {:error, term()}
defdelegate get_net_info(chan \\ nil), to: Rpc
@doc """
Retrieve the current validator info.
## Example
ForgeSdk.get_validators_info()
"""
@spec get_validators_info(Channel.t() | nil) :: ValidatorsInfo.t() | {:error, term()}
defdelegate get_validators_info(chan \\ nil), to: Rpc
@doc """
Create tx.
## Example
w1 = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "alice"), wallet: w1)
w2 = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "bob"), wallet: w2)
data = Google.Protobuf.Any.new(type_url: "test_asset", value: "hello world")
itx = ForgeSdk.encode_any!(TransferTx.new(to: w2.address, value: new_unit(100)))
req = RequestCreateTx.new(itx: itx, from: w1.address, nonce: 2, walllet: w1)
tx = ForgeSdk.create_tx(req)
"""
@spec create_tx(RequestCreateTx.t() | Keyword.t(), Channel.t() | nil) ::
Transaction.t() | {:error, term()}
defdelegate create_tx(request, chan \\ nil), to: Rpc
@doc """
Forge we support `multisig` for a tx, you can use this to endorse an already signed tx.
**ExchangeTx, ConsumeAssetTx and some other txs** are using multisig technology.
## Example
w1 = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "alice"), wallet: w1)
w2 = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "bob"), wallet: w2)
data = Google.Protobuf.Any.new(type_url: "test_asset", value: "hello world")
hash = ForgeSdk.create_asset(ForgeAbi.CreateAssetTx.new(data: asset_data), wallet: w2)
asset_address = ForgeSdk.get_address(hash)
sender_info = ForgeAbi.ExchangeInfo.new(value: ForgeSdk.token_to_unit(1))
receiver_info = ForgeAbi.ExchangeInfo.new(assets: [asset_address])
itx = ForgeAbi.ExchangeTx.new(to: w2.address, sender: sender_info, receiver: receiver_info)
tx = ForgeSdk.prepare_exchange(itx, wallet: w1)
tx1 = ForgeSdk.multisig(tx, w2)
ForgeSdk.send_tx(tx: tx1)
"""
@spec multisig(RequestMultisig.t() | Keyword.t(), Channel.t() | nil) ::
Transaction.t() | {:error, term()}
defdelegate multisig(request, chan \\ nil), to: Rpc
@doc """
Send tx.
## Example
w1 = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "alice"), wallet: w1)
w2 = ForgeSdk.create_wallet()
ForgeSdk.declare(ForgeAbi.DeclareTx.new(moniker: "bob"), wallet: w2)
data = Google.Protobuf.Any.new(type_url: "test_asset", value: "hello world")
itx = ForgeSdk.encode_any!(TransferTx.new(to: w2.address, value: new_unit(100)))
req = RequestCreateTx.new(itx: itx, from: w1.address, nonce: 2, walllet: w1, token: t)
tx = ForgeSdk.create_tx(req)
hash = ForgeSdk.send_tx(tx: tx)
"""
@spec send_tx(RequestSendTx.t() | Keyword.t(), Channel.t() | nil) ::
String.t() | {:error, term()}
defdelegate send_tx(request, chan \\ nil), to: Rpc
@doc """
Return an already processed `transaction` by its `hash`. If this API returns `nil`, mostly your tx hasn't been.
## Example
hash = ForgeSdk.send_tx(tx: tx)
ForgeSdk.get_tx(hash: hash)
"""
@spec get_tx(
RequestGetTx.t() | [RequestGetTx.t()] | Keyword.t() | [Keyword.t()],
Channel.t() | nil
) :: TransactionInfo.t() | [TransactionInfo.t()] | {:error, term()}
defdelegate get_tx(requests, chan \\ nil), to: Rpc
defdelegate get_unconfirmed_txs(request, chan \\ nil), to: Rpc
@doc """
Get a block by its `height`. All txs included in this block will be returned.
## Example
req = ForgeAbi.RequestGetBlock.new(height: 1000)
ForgeSdk.get_block(req)
"""
@spec get_block(
RequestGetBlock.t() | [RequestGetBlock.t()] | Keyword.t() | [Keyword.t()],
Channel.t() | nil
) :: BlockInfo.t() | [BlockInfo.t()] | {:error, term()}
defdelegate get_block(requests, chan \\ nil), to: Rpc
@doc """
Get a `list` of blocks between a range.
## Example
page_info = ForgeAbi.PageInfo.new
range_filter = ForgeAbi.RangeFilter.new(from: 1000, to: 1015)
req = ForgeAbi.RequestGetBlocks.new(empty_excluded: true, height_filter: range_filter,
paging: page_info)
ForgeSdk.get_blocks(req)
"""
@spec get_blocks(RequestGetBlocks.t() | Keyword.t(), Channel.t() | nil) ::
{[BlockInfoSimple.t()], PageInfo.t()} | {:error, term()}
defdelegate get_blocks(request, chan \\ nil), to: Rpc
defdelegate search(request, chan \\ nil), to: Rpc
defdelegate get_config(chan \\ nil), to: Rpc
# wallet related
@doc """
This will generate a wallet with default DID type: public key type is `ED25519`, hash type is `sha3(256)`, and DID role type is account.
## Example
ForgeSdk.create_wallet()
"""
@spec create_wallet :: WalletInfo.t()
def create_wallet, do: Wallet.create(%Wallet.Type.Forge{})
@doc """
You can pass in your own `DID` type in a map once you want to create a wallet with different settings.
## Example
w1 = ForgeSdk.create_wallet()
ForgeSdk.create_wallet(moniker: "alice", passphrase: "abcd1234")
"""
@spec create_wallet(RequestCreateWallet.t() | Keyword.t(), Channel.t() | nil) ::
{WalletInfo.t(), String.t()} | {:error, term()}
defdelegate create_wallet(request, chan \\ nil), to: Rpc
@doc """
Load a node managed wallet by its `address` and `passphrase` from the keystore.
## Example
{w, t} = ForgeSdk.create_wallet(moniker: "alice", passphrase: "abcd1234")
req = ForgeAbi.RequestLoadWallet.new(address: w.address, passphrase: "abcd1234")
ForgeSdk.load_wallet(req)
"""
@spec load_wallet(RequestLoadWallet.t() | Keyword.t(), Channel.t() | nil) ::
String.t() | {:error, term()}
defdelegate load_wallet(request, chan \\ nil), to: Rpc
@doc """
If you know the `type` and the `secret key` of the wallet, you can recover it into the current forge node.
This is useful when you want to switch your wallet from one node to another.
This will generate a keystore file.
## Example
{w, t} = ForgeSdk.create_wallet(moniker: "alice", passphrase: "abcd1234")
request = RequestRecoverWallet.new(data: w.sk, type: w.type, passphrase: "abcd1234")
ForgeSdk.recover_wallet(req)
"""
@spec recover_wallet(RequestRecoverWallet.t(), Channel.t() | nil) ::
{WalletInfo.t(), String.t()} | {:error, term()}
defdelegate recover_wallet(request, chan \\ nil), to: Rpc
@doc """
Display the `wallet addresses` that current forge node hosts.
## Example
ForgeSdk.list_wallet()
"""
@spec list_wallet(Channel.t() | nil) :: String.t() | {:error, term()}
defdelegate list_wallet(chan \\ nil), to: Rpc
@doc """
Delete the `keystore` for a given `wallet address`. This is useful when you finished your work on the forge node and you'd remove the footprint for your wallet.
## Example
{w, t} = ForgeSdk.create_wallet(moniker: "alice", passphrase: "abcd1234")
request = RequestRemoveWallet.new(address: w.address)
ForgeSdk.remove_wallet(request)
"""
@spec remove_wallet(RequestRemoveWallet.t() | Keyword.t(), Channel.t() | nil) ::
:ok | {:error, term()}
defdelegate remove_wallet(request, chan \\ nil), to: Rpc
defdelegate declare_node(request, chan \\ nil), to: Rpc
# state related
@doc """
Return the `state` for an account, node, validator or application address.
## Example
req = ForgeAbi.RequestGetAccountState.new(address: "z1QNTPxDUCbh68q6ci6zUmtnT2Cj8nbLw75")
ForgeSdk.get_account_state(req)
"""
@spec get_account_state(
RequestGetAccountState.t() | [RequestGetAccountState.t()] | Keyword.t() | [Keyword.t()],
Channel.t() | nil
) :: AccountState.t() | nil | [AccountState.t()] | {:error, term()}
defdelegate get_account_state(request, chan \\ nil), to: Rpc
@doc """
Return the `state` for an asset.
## Example
req = ForgeAbi.RequestGetAssetState.new(address: "zjdjh65vHxvvWfj3xPrDoUDYp1aY6xUCV21b")
ForgeSdk.get_asset_state(req)
"""
@spec get_asset_state(
RequestGetAssetState.t() | [RequestGetAssetState.t()] | Keyword.t() | [Keyword.t()],
Channel.t() | nil
) :: AssetState.t() | [AssetState.t()] | {:error, term()}
defdelegate get_asset_state(request, chan \\ nil), to: Rpc
@doc """
Return global state for forge.
## Example
ForgeSdk.get_forge_state()
"""
@spec get_forge_state(Channel.t() | nil) :: ForgeState.t() | {:error, term()}
defdelegate get_forge_state(chan \\ nil), to: Rpc
@doc """
Return installed protocol state.
## Example
req = ForgeAbi.RequestGetProtocolState.new(address: "z2E3zCQTx5dPQeimQvJWz3vJvcDv9Ad6YgaPn")
ForgeSdk.get_protocol_state(req)
"""
@spec get_protocol_state(
RequestGetProtocolState.t()
| [RequestGetProtocolState.t()]
| Keyword.t()
| [Keyword.t()],
Channel.t() | nil
) :: ProtocolState.t() | [ProtocolState.t()] | {:error, term()}
defdelegate get_protocol_state(request, chan \\ nil), to: Rpc
defdelegate get_stake_state(request, chan \\ nil), to: Rpc
defdelegate get_tether_state(request, chan \\ nil), to: Rpc
# filesystem related
defdelegate store_file(request, chan \\ nil), to: File
defdelegate load_file(request, chan \\ nil), to: File
defdelegate pin_file(request, chan \\ nil), to: Rpc
# subscription related
@doc """
Subscribe to a `topic`. You can event set a filter for the event that you'd listen.
## Example
req = ForgeAbi.RequestSubscribe.new(topic: "fg:t:declare")
ForgeSdk.Rpc.subscribe(req)
"""
@spec subscribe(RequestSubscribe.t() | Keyword.t(), Channel.t() | nil, Keyword.t()) ::
[ResponseSubscribe.t()] | {:error, term()}
defdelegate subscribe(request, chan \\ nil, opts \\ []), to: Rpc
@doc """
Terminate the subscription by the topic `id`.
## Example
req = ForgeAbi.RequestSubscribe.new(topic: "fg:t:declare")
stream_declare = ForgeSdk.Rpc.subscribe(req)
[topic: topic] = Enum.take(stream_declare, 1)
req = ForgeAbi.RequestUnsubscribe.new(topic: topic)
ForgeSdk.Rpc.unsubscribe(req)
"""
@spec unsubscribe(RequestUnsubscribe.t(), Channel.t() | nil, Keyword.t()) ::
:ok | {:error, term()}
defdelegate unsubscribe(request, chan \\ nil, opts \\ []), to: Rpc
# extended
defdelegate get_nonce(address, chan \\ nil, app_hash \\ ""), to: Rpc
# display a data structure
@doc """
Provide a display friendly result for a data structure.
## Examples
req = ForgeAbi.RequestGetAccountState.new(address: "z1QNTPxDUCbh68q6ci6zUmtnT2Cj8nbLw75")
account_state = ForgeSdk.get_account_state(req)
ForgeSdk.display(account_state)
"""
@spec display(any(), boolean()) :: any()
defdelegate display(data, expand? \\ false), to: Display
@doc """
Parse the forge configuration.
## Example
ForgeSdk.parse_config(:forge, "~/.forge_cli/forge_release.toml")
"""
@spec parse_config(atom(), String.t()) :: map()
defdelegate parse_config(name, file \\ ""), to: Util, as: :parse
defdelegate find_config_file!, to: Util
defdelegate load_config_file!(filename \\ nil), to: Util
defdelegate gen_config(params), to: Util
defdelegate get_env(key), to: Helper
defdelegate put_env(key, value), to: Helper
defdelegate get_chan, to: Util
defdelegate datetime_to_proto(dt), to: Util
defdelegate proto_to_datetime(ts), to: Util
defdelegate update_config(forge_state), to: Util
defdelegate update_type_url(forge_state), to: Loader
defdelegate get_tx_protocols(forge_state, address), to: Loader
defdelegate get_address(hash), to: Rpc
defdelegate encode_any(data, type_url \\ nil), to: ForgeAbi
defdelegate encode_any!(data, type_url \\ nil), to: ForgeAbi
defdelegate decode_any(data), to: ForgeAbi
defdelegate decode_any!(data), to: ForgeAbi
defdelegate token_to_unit(tokens), to: ForgeAbi
defdelegate unit_to_token(units), to: ForgeAbi
# init sdk and handler registration
@doc """
Init forge SDK (it will setup client RPC socket for you).
## Examples
ForgeSdk.init(:forge)
"""
@spec init(atom(), String.t(), String.t() | nil) :: [module() | {module(), term()}]
defdelegate init(otp_app, app_hash \\ "", filename \\ nil), to: Util
# stats
defdelegate get_forge_stats(requests, chan \\ nil), to: Rpc
defdelegate list_transactions(request, chan \\ nil), to: Rpc
defdelegate list_assets(request, chan \\ nil), to: Rpc
defdelegate list_stakes(request, chan \\ nil), to: Rpc
defdelegate list_account(request, chan \\ nil), to: Rpc
defdelegate list_top_accounts(request, chan \\ nil), to: Rpc
defdelegate list_asset_transactions(request, chan \\ nil), to: Rpc
defdelegate list_blocks(request, chan \\ nil), to: Rpc
defdelegate get_health_status(request, chan \\ nil), to: Rpc
end