Skip to content

Commit 1bd970e

Browse files
authored
Sutra SDK e2e Test (#165)
* feat: add Elixir scripts for minting, delegating, registering, and withdrawing stake * feat: add overview documentation for Sutra Elixir integration and wallet management * docs: update SDK integration details to include Sutra
1 parent 7ca8d2e commit 1bd970e

9 files changed

Lines changed: 418 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
- **🌐 Multi-node Support** - Enable multiple nodes specifically for rollback testing scenarios
2525
- **📊 Built-in Indexer** - Integrated Yaci Store with Blockfrost-compatible APIs
2626
- **🎯 Developer Tools** - Browser-based viewer, CLI management, and extensive APIs
27-
- **🔗 SDK Integration** - Seamless integration with popular Cardano SDKs (Mesh, CCL, Lucid Evolution)
27+
- **🔗 SDK Integration** - Seamless integration with popular Cardano SDKs (Mesh, CCL, Lucid Evolution, Sutra)
2828

2929
## 🏗️ Architecture
3030

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
## Configuration
2+
3+
```elixir
4+
config :sutra, :yaci,
5+
yaci_general_api_url: "http://localhost:8080", # Defaults to http://localhost:8080
6+
yaci_admin_api_url: "http://localhost:10000" # Defaults to http://localhost:10000
7+
```
8+
9+
## Privnet Testing
10+
11+
Sutra SDK includes a dedicated testing module `Sutra.PrivnetTest` to simplify integration testing against a private network (like Yaci DevKit).
12+
13+
## Setup
14+
15+
Ensure you have a local Yaci DevKit instance running.
16+
17+
In your test file, `use Sutra.PrivnetTest`:
18+
19+
```elixir
20+
defmodule MyProject.MyTest do
21+
use Sutra.PrivnetTest
22+
23+
test "test something" do
24+
# ...
25+
end
26+
end
27+
```
28+
29+
This automatically checks if Yaci is running and sets up the provider configuration.
30+
31+
## Managing Wallets
32+
33+
### Default Wallets
34+
35+
Access pre-funded default wallets from the Yaci DevKit (indices 0-20):
36+
37+
```elixir
38+
test "uses default wallet" do
39+
with_default_wallet(0, fn %{address: address, signing_key: key} ->
40+
# Test logic here
41+
IO.puts("Wallet Address: #{address}")
42+
end)
43+
end
44+
```
45+
46+
The wallet is automatically topped up if its balance is zero.
47+
48+
### New Ephemeral Wallets
49+
50+
Create a fresh wallet for isolation:
51+
52+
```elixir
53+
test "uses fresh wallet" do
54+
with_new_wallet(fn %{address: address, signing_key: key} ->
55+
# This wallet is funded with 100 ADA by default
56+
end)
57+
end
58+
```
59+
60+
## Loading Funds
61+
62+
You can explicitly load funds into an address:
63+
64+
```elixir
65+
# Load 1000 ADA
66+
load_ada(address, 1000)
67+
68+
# Load multiple UTxOs
69+
load_ada(address, [1000, 500])
70+
```
71+
72+
## Waiting for Transactions
73+
74+
Since blockchain operations are asynchronous, use `await_tx` to wait for a transaction to be confirmed:
75+
76+
```elixir
77+
tx_id = submit_some_transaction()
78+
79+
# detailed info
80+
tx_info = await_tx(tx_id)
81+
82+
# or assert success
83+
assert Sutra.PrivnetTest.await_tx(tx_id)
84+
```

e2e-tests/sutra-elixir/README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Sutra Elixir Scripts
2+
3+
This README explains how to install Elixir and run the example Elixir scripts in this folder.
4+
5+
## 1. Install Elixir
6+
7+
On Debian/Ubuntu you can install Elixir with:
8+
9+
```bash
10+
sudo apt-get update
11+
sudo apt-get install -y elixir
12+
```
13+
14+
For other platforms or the latest release, see the official instructions: https://elixir-lang.org/install.html
15+
16+
## 2. Run Tests
17+
18+
- Native Script Minting:
19+
20+
```bash
21+
elixir nativescript_mint.exs
22+
```
23+
24+
- Plutus Script Minting:
25+
26+
```bash
27+
elixir plutus_mint.exs
28+
```
29+
30+
- Register stake credential:
31+
32+
```bash
33+
elixir register_stake.exs
34+
```
35+
36+
- Delegate vote to DRep:
37+
38+
```bash
39+
elixir delegate_vote.exs
40+
```
41+
42+
- Withdraw stake:
43+
44+
```bash
45+
elixir withdraw_stake.exs
46+
```
47+
48+
> **Warning:**
49+
>
50+
> You must run `register_stake.exs` at least once before delegating to a DRep or withdrawing stake.
51+
52+
> **Note:**
53+
>
54+
> If your Yaci Devkit is running on a custom endpoint, export these environment variables before running the scripts:
55+
>
56+
> ```bash
57+
> export YACI_GENERAL_API_URL="http://your-host:8080"
58+
> export YACI_ADMIN_API_URL="http://your-host:10000"
59+
> ```
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#
2+
# Note: This test assumes Native script and Plutus script staking credentials are already registered on chain.
3+
# If not, please run elixir register_stake.exs before running this test.
4+
5+
alias Sutra.Cardano.Common.Drep
6+
alias Sutra.Cardano.Script.NativeScript
7+
alias Sutra.Data
8+
alias Sutra.Cardano.Script
9+
alias Sutra.Crypto.Key
10+
11+
Code.eval_file("setup.exs")
12+
13+
mnemonic =
14+
"test test test test test test test test test test test test test test test test test test test test test test test sauce"
15+
16+
{:ok, root_key} = Key.root_key_from_mnemonic(mnemonic)
17+
{:ok, extended_key} = Key.derive_child(root_key, 0, 0)
18+
19+
{:ok, wallet_address} = Key.address(extended_key, :preprod)
20+
21+
{:ok, extended_key_2} = Key.derive_child(root_key, 0, 0)
22+
23+
{:ok, wallet_address_2} = Key.address(extended_key_2, :preprod)
24+
25+
26+
always_true_script = "58da010100229800aba2aba1aab9eaab9dab9a9bae002488888966002646465300130063754003300800398040012444b30013370e9000001c4c9289bae300b300a375400915980099b874800800e2646644944c030004c030c034004c028dd5002456600266e1d2004003899251300b300a375400915980099b874801800e2646644944dd698060009806180680098051baa0048acc004cdc3a40100071324a2601660146ea80122646644944dd698060009806180680098051baa00440208041008201040203007300800130070013004375400f149a26cac80101"
27+
28+
script =
29+
always_true_script
30+
|> Script.apply_params([Base.encode16("sutra-yaci-devkit-register-stake", case: :lower)])
31+
|> Script.new(:plutus_v3)
32+
33+
script_json = %{
34+
"type" => "all",
35+
"scripts" => [
36+
%{
37+
"type" => "sig",
38+
"keyHash" => wallet_address.payment_credential.hash
39+
}
40+
]
41+
}
42+
43+
tx_id =
44+
Sutra.new_tx()
45+
|> Sutra.delegate_vote(script, Drep.abstain(), Data.void())
46+
|> Sutra.delegate_vote(NativeScript.from_json(script_json), Drep.no_confidence())
47+
|> Sutra.delegate_vote(wallet_address, Drep.abstain())
48+
|> Sutra.build_tx!(wallet_address: [wallet_address, wallet_address_2])
49+
|> Sutra.sign_tx([extended_key, extended_key_2])
50+
|> Sutra.sign_tx_with_raw_extended_key(extended_key.stake_key)
51+
|> Sutra.submit_tx()
52+
53+
IO.inspect(tx_id)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
alias Sutra.Cardano.Address
2+
alias Sutra.Cardano.Asset
3+
alias Sutra.Crypto.Key
4+
alias Sutra.Cardano.Script
5+
alias Sutra.Cardano.Script.NativeScript
6+
7+
8+
# Setup sutra package & init yaci provider
9+
Code.eval_file("setup.exs")
10+
11+
mnemonic =
12+
"test test test test test test test test test test test test test test test test test test test test test test test sauce"
13+
14+
{:ok, root_key} = Key.root_key_from_mnemonic(mnemonic)
15+
{:ok, extended_key} = Key.derive_child(root_key, 0, 0)
16+
17+
{:ok, wallet_address} = Key.address(extended_key, :preprod)
18+
19+
script_json = %{
20+
"type" => "all",
21+
"scripts" => [
22+
%{
23+
"type" => "sig",
24+
"keyHash" => wallet_address.payment_credential.hash
25+
}
26+
]
27+
}
28+
29+
script = NativeScript.from_json(script_json)
30+
31+
policy_id = NativeScript.to_script(script) |> Script.hash_script()
32+
33+
assets = %{
34+
Base.encode16("SUTRA-NATIVE-TKN") => 1
35+
}
36+
37+
tx_id =
38+
Sutra.new_tx()
39+
|> Sutra.mint_asset(policy_id, assets, script)
40+
|> Sutra.add_output(wallet_address, %{
41+
policy_id => assets
42+
})
43+
|> Sutra.add_output(
44+
Address.from_script(policy_id, :testnet),
45+
Asset.from_lovelace(1000),
46+
{:datum_hash, "check As Hash"}
47+
)
48+
|> Sutra.build_tx!(wallet_address: [wallet_address])
49+
|> Sutra.sign_tx([extended_key])
50+
|> Sutra.submit_tx()
51+
52+
IO.puts("Tx Submitted for Mint with Native Script TxId: #{tx_id}")
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
defmodule AlwaysSucceedMint do
2+
@moduledoc false
3+
4+
alias Sutra.Cardano.Address
5+
alias Sutra.Cardano.Asset
6+
alias Sutra.Cardano.Script
7+
alias Sutra.Crypto.Key
8+
alias Sutra.Data
9+
10+
@compiled_code "589a010100323232323223225333004323232323232533300a3370e9000000899251375c601a60186ea800854ccc028cdc3a4004002264664464a66601c66e1d2000300f37540042a66601c66e1cdd6980898081baa00200114a22c2c6eb4018c038004c038c03c004c030dd50010b18051baa001300b300c003300a002300900230090013006375400229309b2b1bae0015734aae7555cf2ba15745"
11+
12+
def run() do
13+
14+
mnemonic =
15+
"test test test test test test test test test test test test test test test test test test test test test test test sauce"
16+
17+
{:ok, root_key} = Key.root_key_from_mnemonic(mnemonic)
18+
19+
# deriving two account to have utxo to cover collateral
20+
{:ok, extended_key_acct1} = Key.derive_child(root_key, 0, 0)
21+
{:ok, wallet_address_1} = Key.address(extended_key_acct1, :preprod)
22+
23+
24+
{:ok, extended_key_acct2} = Key.derive_child(root_key, 1, 0)
25+
{:ok, wallet_address_2} = Key.address(extended_key_acct2, :preprod)
26+
27+
simple_mint_script =
28+
@compiled_code
29+
|> Script.apply_params([Base.encode16("some-params", case: :lower)])
30+
|> Script.new(:plutus_v3)
31+
32+
mint_script_address = Address.from_script(simple_mint_script, :preprod)
33+
34+
out_token_name = Base.encode16("YACI-DEVKIT-TEST", case: :lower)
35+
policy_id = Script.hash_script(simple_mint_script)
36+
37+
out_value =
38+
Asset.zero()
39+
|> Asset.add(policy_id, out_token_name, 100)
40+
41+
tx_id =
42+
Sutra.new_tx()
43+
|> Sutra.mint_asset(policy_id, %{out_token_name => 100}, simple_mint_script, Data.void())
44+
|> Sutra.add_output(mint_script_address, out_value, {:inline_datum, 58})
45+
|> Sutra.build_tx!(wallet_address: [wallet_address_1, wallet_address_2])
46+
|> Sutra.sign_tx([extended_key_acct1, extended_key_acct2])
47+
|> Sutra.submit_tx()
48+
49+
IO.puts(" Tx submitted with : #{tx_id}")
50+
end
51+
end
52+
53+
# Setup sutra package & init yaci provider
54+
Code.eval_file("setup.exs")
55+
56+
AlwaysSucceedMint.run()
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
alias Sutra.Cardano.Script.NativeScript
2+
alias Sutra.Data
3+
alias Sutra.Cardano.Script
4+
alias Sutra.Crypto.Key
5+
6+
Code.eval_file("setup.exs")
7+
8+
mnemonic =
9+
"test test test test test test test test test test test test test test test test test test test test test test test sauce"
10+
11+
{:ok, root_key} = Key.root_key_from_mnemonic(mnemonic)
12+
{:ok, extended_key} = Key.derive_child(root_key, 0, 0)
13+
14+
{:ok, wallet_address} = Key.address(extended_key, :preprod)
15+
16+
17+
{:ok, extended_key_2} = Key.derive_child(root_key, 0, 0)
18+
19+
{:ok, wallet_address_2} = Key.address(extended_key, :preprod)
20+
21+
always_true_script = "58da010100229800aba2aba1aab9eaab9dab9a9bae002488888966002646465300130063754003300800398040012444b30013370e9000001c4c9289bae300b300a375400915980099b874800800e2646644944c030004c030c034004c028dd5002456600266e1d2004003899251300b300a375400915980099b874801800e2646644944dd698060009806180680098051baa0048acc004cdc3a40100071324a2601660146ea80122646644944dd698060009806180680098051baa00440208041008201040203007300800130070013004375400f149a26cac80101"
22+
23+
24+
script =
25+
always_true_script
26+
|> Script.apply_params([Base.encode16("sutra-yaci-devkit-register-stake", case: :lower)])
27+
|> Script.new(:plutus_v3)
28+
29+
script_json = %{
30+
"type" => "all",
31+
"scripts" => [
32+
%{
33+
"type" => "sig",
34+
"keyHash" => wallet_address.payment_credential.hash
35+
}
36+
]
37+
}
38+
39+
tx_id =
40+
Sutra.new_tx()
41+
|> Sutra.register_stake_credential(script, Data.void())
42+
|> Sutra.register_stake_credential(NativeScript.from_json(script_json))
43+
|> Sutra.register_stake_credential(wallet_address)
44+
|> Sutra.build_tx!(wallet_address: [wallet_address, wallet_address_2])
45+
|> Sutra.sign_tx([extended_key, extended_key_2])
46+
|> Sutra.sign_tx_with_raw_extended_key(extended_key.stake_key)
47+
|> Sutra.submit_tx()
48+
49+
IO.inspect(tx_id)

e2e-tests/sutra-elixir/setup.exs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Mix.install([:sutra_cardano])
2+
3+
alias Sutra.Provider.Yaci
4+
5+
# Setup Yaci Devkit provider
6+
Application.put_env(:sutra, :provider, Yaci)
7+
8+
Application.put_env(
9+
:sutra,
10+
:yaci_general_api_url,
11+
System.get_env("YACI_GENERAL_API_URL", "http://localhost:8080")
12+
)
13+
14+
Application.put_env(
15+
:sutra,
16+
:yaci_admin_api_url,
17+
System.get_env("YACI_ADMIN_API_URL", "http://localhost:10000")
18+
)

0 commit comments

Comments
 (0)