Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
- **🌐 Multi-node Support** - Enable multiple nodes specifically for rollback testing scenarios
- **📊 Built-in Indexer** - Integrated Yaci Store with Blockfrost-compatible APIs
- **🎯 Developer Tools** - Browser-based viewer, CLI management, and extensive APIs
- **🔗 SDK Integration** - Seamless integration with popular Cardano SDKs (Mesh, CCL, Lucid Evolution)
- **🔗 SDK Integration** - Seamless integration with popular Cardano SDKs (Mesh, CCL, Lucid Evolution, Sutra)

## 🏗️ Architecture

Expand Down
84 changes: 84 additions & 0 deletions docs/pages/tutorials/sutra-elixir/overview.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
## Configuration

```elixir
config :sutra, :yaci,
yaci_general_api_url: "http://localhost:8080", # Defaults to http://localhost:8080
yaci_admin_api_url: "http://localhost:10000" # Defaults to http://localhost:10000
```

## Privnet Testing

Sutra SDK includes a dedicated testing module `Sutra.PrivnetTest` to simplify integration testing against a private network (like Yaci DevKit).

## Setup

Ensure you have a local Yaci DevKit instance running.

In your test file, `use Sutra.PrivnetTest`:

```elixir
defmodule MyProject.MyTest do
use Sutra.PrivnetTest

test "test something" do
# ...
end
end
```

This automatically checks if Yaci is running and sets up the provider configuration.

## Managing Wallets

### Default Wallets

Access pre-funded default wallets from the Yaci DevKit (indices 0-20):

```elixir
test "uses default wallet" do
with_default_wallet(0, fn %{address: address, signing_key: key} ->
# Test logic here
IO.puts("Wallet Address: #{address}")
end)
end
```

The wallet is automatically topped up if its balance is zero.

### New Ephemeral Wallets

Create a fresh wallet for isolation:

```elixir
test "uses fresh wallet" do
with_new_wallet(fn %{address: address, signing_key: key} ->
# This wallet is funded with 100 ADA by default
end)
end
```

## Loading Funds

You can explicitly load funds into an address:

```elixir
# Load 1000 ADA
load_ada(address, 1000)

# Load multiple UTxOs
load_ada(address, [1000, 500])
```

## Waiting for Transactions

Since blockchain operations are asynchronous, use `await_tx` to wait for a transaction to be confirmed:

```elixir
tx_id = submit_some_transaction()

# detailed info
tx_info = await_tx(tx_id)

# or assert success
assert Sutra.PrivnetTest.await_tx(tx_id)
```
59 changes: 59 additions & 0 deletions e2e-tests/sutra-elixir/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Sutra Elixir Scripts

This README explains how to install Elixir and run the example Elixir scripts in this folder.

## 1. Install Elixir

On Debian/Ubuntu you can install Elixir with:

```bash
sudo apt-get update
sudo apt-get install -y elixir
```

For other platforms or the latest release, see the official instructions: https://elixir-lang.org/install.html

## 2. Run Tests

- Native Script Minting:

```bash
elixir nativescript_mint.exs
```

- Plutus Script Minting:

```bash
elixir plutus_mint.exs
```

- Register stake credential:

```bash
elixir register_stake.exs
```

- Delegate vote to DRep:

```bash
elixir delegate_vote.exs
```

- Withdraw stake:

```bash
elixir withdraw_stake.exs
```

> **Warning:**
>
> You must run `register_stake.exs` at least once before delegating to a DRep or withdrawing stake.

> **Note:**
>
> If your Yaci Devkit is running on a custom endpoint, export these environment variables before running the scripts:
>
> ```bash
> export YACI_GENERAL_API_URL="http://your-host:8080"
> export YACI_ADMIN_API_URL="http://your-host:10000"
> ```
53 changes: 53 additions & 0 deletions e2e-tests/sutra-elixir/delegate_vote.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#
# Note: This test assumes Native script and Plutus script staking credentials are already registered on chain.
# If not, please run elixir register_stake.exs before running this test.

alias Sutra.Cardano.Common.Drep
alias Sutra.Cardano.Script.NativeScript
alias Sutra.Data
alias Sutra.Cardano.Script
alias Sutra.Crypto.Key

Code.eval_file("setup.exs")

mnemonic =
"test test test test test test test test test test test test test test test test test test test test test test test sauce"

{:ok, root_key} = Key.root_key_from_mnemonic(mnemonic)
{:ok, extended_key} = Key.derive_child(root_key, 0, 0)

{:ok, wallet_address} = Key.address(extended_key, :preprod)

{:ok, extended_key_2} = Key.derive_child(root_key, 0, 0)

{:ok, wallet_address_2} = Key.address(extended_key_2, :preprod)


always_true_script = "58da010100229800aba2aba1aab9eaab9dab9a9bae002488888966002646465300130063754003300800398040012444b30013370e9000001c4c9289bae300b300a375400915980099b874800800e2646644944c030004c030c034004c028dd5002456600266e1d2004003899251300b300a375400915980099b874801800e2646644944dd698060009806180680098051baa0048acc004cdc3a40100071324a2601660146ea80122646644944dd698060009806180680098051baa00440208041008201040203007300800130070013004375400f149a26cac80101"

script =
always_true_script
|> Script.apply_params([Base.encode16("sutra-yaci-devkit-register-stake", case: :lower)])
|> Script.new(:plutus_v3)

script_json = %{
"type" => "all",
"scripts" => [
%{
"type" => "sig",
"keyHash" => wallet_address.payment_credential.hash
}
]
}

tx_id =
Sutra.new_tx()
|> Sutra.delegate_vote(script, Drep.abstain(), Data.void())
|> Sutra.delegate_vote(NativeScript.from_json(script_json), Drep.no_confidence())
|> Sutra.delegate_vote(wallet_address, Drep.abstain())
|> Sutra.build_tx!(wallet_address: [wallet_address, wallet_address_2])
|> Sutra.sign_tx([extended_key, extended_key_2])
|> Sutra.sign_tx_with_raw_extended_key(extended_key.stake_key)
|> Sutra.submit_tx()

IO.inspect(tx_id)
52 changes: 52 additions & 0 deletions e2e-tests/sutra-elixir/nativescript_mint.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
alias Sutra.Cardano.Address
alias Sutra.Cardano.Asset
alias Sutra.Crypto.Key
alias Sutra.Cardano.Script
alias Sutra.Cardano.Script.NativeScript


# Setup sutra package & init yaci provider
Code.eval_file("setup.exs")

mnemonic =
"test test test test test test test test test test test test test test test test test test test test test test test sauce"

{:ok, root_key} = Key.root_key_from_mnemonic(mnemonic)
{:ok, extended_key} = Key.derive_child(root_key, 0, 0)

{:ok, wallet_address} = Key.address(extended_key, :preprod)

script_json = %{
"type" => "all",
"scripts" => [
%{
"type" => "sig",
"keyHash" => wallet_address.payment_credential.hash
}
]
}

script = NativeScript.from_json(script_json)

policy_id = NativeScript.to_script(script) |> Script.hash_script()

assets = %{
Base.encode16("SUTRA-NATIVE-TKN") => 1
}

tx_id =
Sutra.new_tx()
|> Sutra.mint_asset(policy_id, assets, script)
|> Sutra.add_output(wallet_address, %{
policy_id => assets
})
|> Sutra.add_output(
Address.from_script(policy_id, :testnet),
Asset.from_lovelace(1000),
{:datum_hash, "check As Hash"}
)
|> Sutra.build_tx!(wallet_address: [wallet_address])
|> Sutra.sign_tx([extended_key])
|> Sutra.submit_tx()

IO.puts("Tx Submitted for Mint with Native Script TxId: #{tx_id}")
56 changes: 56 additions & 0 deletions e2e-tests/sutra-elixir/plutus_mint.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
defmodule AlwaysSucceedMint do
@moduledoc false

alias Sutra.Cardano.Address
alias Sutra.Cardano.Asset
alias Sutra.Cardano.Script
alias Sutra.Crypto.Key
alias Sutra.Data

@compiled_code "589a010100323232323223225333004323232323232533300a3370e9000000899251375c601a60186ea800854ccc028cdc3a4004002264664464a66601c66e1d2000300f37540042a66601c66e1cdd6980898081baa00200114a22c2c6eb4018c038004c038c03c004c030dd50010b18051baa001300b300c003300a002300900230090013006375400229309b2b1bae0015734aae7555cf2ba15745"

def run() do

mnemonic =
"test test test test test test test test test test test test test test test test test test test test test test test sauce"

{:ok, root_key} = Key.root_key_from_mnemonic(mnemonic)

# deriving two account to have utxo to cover collateral
{:ok, extended_key_acct1} = Key.derive_child(root_key, 0, 0)
{:ok, wallet_address_1} = Key.address(extended_key_acct1, :preprod)


{:ok, extended_key_acct2} = Key.derive_child(root_key, 1, 0)
{:ok, wallet_address_2} = Key.address(extended_key_acct2, :preprod)

simple_mint_script =
@compiled_code
|> Script.apply_params([Base.encode16("some-params", case: :lower)])
|> Script.new(:plutus_v3)

mint_script_address = Address.from_script(simple_mint_script, :preprod)

out_token_name = Base.encode16("YACI-DEVKIT-TEST", case: :lower)
policy_id = Script.hash_script(simple_mint_script)

out_value =
Asset.zero()
|> Asset.add(policy_id, out_token_name, 100)

tx_id =
Sutra.new_tx()
|> Sutra.mint_asset(policy_id, %{out_token_name => 100}, simple_mint_script, Data.void())
|> Sutra.add_output(mint_script_address, out_value, {:inline_datum, 58})
|> Sutra.build_tx!(wallet_address: [wallet_address_1, wallet_address_2])
|> Sutra.sign_tx([extended_key_acct1, extended_key_acct2])
|> Sutra.submit_tx()

IO.puts(" Tx submitted with : #{tx_id}")
end
end

# Setup sutra package & init yaci provider
Code.eval_file("setup.exs")

AlwaysSucceedMint.run()
49 changes: 49 additions & 0 deletions e2e-tests/sutra-elixir/register_stake.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
alias Sutra.Cardano.Script.NativeScript
alias Sutra.Data
alias Sutra.Cardano.Script
alias Sutra.Crypto.Key

Code.eval_file("setup.exs")

mnemonic =
"test test test test test test test test test test test test test test test test test test test test test test test sauce"

{:ok, root_key} = Key.root_key_from_mnemonic(mnemonic)
{:ok, extended_key} = Key.derive_child(root_key, 0, 0)

{:ok, wallet_address} = Key.address(extended_key, :preprod)


{:ok, extended_key_2} = Key.derive_child(root_key, 0, 0)

{:ok, wallet_address_2} = Key.address(extended_key, :preprod)

always_true_script = "58da010100229800aba2aba1aab9eaab9dab9a9bae002488888966002646465300130063754003300800398040012444b30013370e9000001c4c9289bae300b300a375400915980099b874800800e2646644944c030004c030c034004c028dd5002456600266e1d2004003899251300b300a375400915980099b874801800e2646644944dd698060009806180680098051baa0048acc004cdc3a40100071324a2601660146ea80122646644944dd698060009806180680098051baa00440208041008201040203007300800130070013004375400f149a26cac80101"


script =
always_true_script
|> Script.apply_params([Base.encode16("sutra-yaci-devkit-register-stake", case: :lower)])
|> Script.new(:plutus_v3)

script_json = %{
"type" => "all",
"scripts" => [
%{
"type" => "sig",
"keyHash" => wallet_address.payment_credential.hash
}
]
}

tx_id =
Sutra.new_tx()
|> Sutra.register_stake_credential(script, Data.void())
|> Sutra.register_stake_credential(NativeScript.from_json(script_json))
|> Sutra.register_stake_credential(wallet_address)
|> Sutra.build_tx!(wallet_address: [wallet_address, wallet_address_2])
|> Sutra.sign_tx([extended_key, extended_key_2])
|> Sutra.sign_tx_with_raw_extended_key(extended_key.stake_key)
|> Sutra.submit_tx()

IO.inspect(tx_id)
18 changes: 18 additions & 0 deletions e2e-tests/sutra-elixir/setup.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Mix.install([:sutra_cardano])

alias Sutra.Provider.Yaci

# Setup Yaci Devkit provider
Application.put_env(:sutra, :provider, Yaci)

Application.put_env(
:sutra,
:yaci_general_api_url,
System.get_env("YACI_GENERAL_API_URL", "http://localhost:8080")
)

Application.put_env(
:sutra,
:yaci_admin_api_url,
System.get_env("YACI_ADMIN_API_URL", "http://localhost:10000")
)
Loading
Loading