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
4 changes: 3 additions & 1 deletion chain/core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod h256;
mod heap_address;
mod heap_h256;
mod shard_config;
mod shard_id;
mod time;

pub use address::Address;
Expand All @@ -17,5 +18,6 @@ pub use flags::*;
pub use h256::H256;
pub use heap_address::HeapAddress;
pub use heap_h256::HeapH256;
pub use shard_config::{ShardConfig, ShardId};
pub use shard_config::ShardConfig;
pub use shard_id::ShardId;
pub use time::*;
45 changes: 11 additions & 34 deletions chain/core/src/types/shard_config.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,4 @@
use super::Address;

/// Identifies a shard by its numeric index.
///
/// Regular shards are numbered from `0` to `number_of_shards - 1`.
/// The special value [`ShardId::METACHAIN_ID`] (`u32::MAX`) identifies the metachain.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(transparent)
)]
pub struct ShardId(u32);

impl ShardId {
/// Shard ID reserved for the metachain.
pub const METACHAIN_ID: ShardId = ShardId(u32::MAX);

pub fn as_u32(&self) -> u32 {
self.0
}
}

impl From<u32> for ShardId {
fn from(value: u32) -> Self {
ShardId(value)
}
}
use super::{Address, shard_id::ShardId};

/// Precomputed configuration for shard assignment.
/// Mirrors the Go `multiShardCoordinator` struct.
Expand Down Expand Up @@ -95,7 +68,7 @@ impl ShardConfig {
shard = val & self.mask_low;
}

ShardId(shard)
ShardId::from(shard)
}

/// Returns true if both addresses belong to the same shard.
Expand Down Expand Up @@ -176,9 +149,9 @@ mod tests {
let addr = address_from_u32(i);
let shard_id = sr.compute_id(&addr);
assert!(
shard_id.0 < sr.number_of_shards,
shard_id.as_u32() < sr.number_of_shards,
"i={i}: shard {} >= {num_of_shards}",
shard_id.0
shard_id.as_u32()
);
}
}
Expand Down Expand Up @@ -208,7 +181,7 @@ mod tests {
let addr = address_from_u32(address);
assert_eq!(
sr.compute_id(&addr),
ShardId(expected_shard),
ShardId::from(expected_shard),
"address={address}"
);
}
Expand Down Expand Up @@ -285,7 +258,11 @@ mod tests {
];
for &(hex, expected_shard) in dataset {
let addr = address_from_hex(hex);
assert_eq!(sr.compute_id(&addr), ShardId(expected_shard), "hex={hex}");
assert_eq!(
sr.compute_id(&addr),
ShardId::from(expected_shard),
"hex={hex}"
);
}
}

Expand All @@ -308,7 +285,7 @@ mod tests {
let addr = address_from_u32(address);
assert_eq!(
sr.compute_id(&addr),
ShardId(expected_shard),
ShardId::from(expected_shard),
"address={address}"
);
}
Expand Down
36 changes: 36 additions & 0 deletions chain/core/src/types/shard_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/// Identifies a shard by its numeric index.
///
/// Regular shards are numbered from `0` to `number_of_shards - 1`.
/// The special value [`ShardId::METACHAIN_ID`] (`u32::MAX`) identifies the metachain.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(transparent)
)]
pub struct ShardId(u32);

impl ShardId {
/// Shard ID reserved for the metachain.
pub const METACHAIN_ID: ShardId = ShardId(u32::MAX);

pub fn as_u32(&self) -> u32 {
self.0
}
}

impl From<u32> for ShardId {
fn from(value: u32) -> Self {
ShardId(value)
}
}

impl core::fmt::Display for ShardId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if *self == ShardId::METACHAIN_ID {
write!(f, "metachain")
} else {
write!(f, "{}", self.0)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# Pem files are used for interactions, but shouldn't be committed
*.pem

# Last deployed addresses
deploy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,47 @@ cargo run -- <COMMAND> [OPTIONS]

---

## Typical workflow

1. Fill in `config.toml` with the gateway, token IDs, contract addresses, and wallet PEM paths.
2. **Deploy** — creates one forwarder-blind contract per wallet and records the addresses in `deploy.toml`:
```bash
cargo run -- deploy
```
3. **Copy addresses** — open `deploy.toml` and paste the `contract_addresses` list into `config.toml`.
4. **Wrap** — give each wallet WEGLD to spend:
```bash
cargo run -- wrap -a <AMOUNT>
```
5. **Swap** — run any combination of swap commands to exercise the different call types.
6. **Drain** (optional) — recover tokens left in the contracts after `te` swaps:
```bash
cargo run -- drain
```

---

## Commands

### `deploy`

Deploy a new instance of the forwarder-blind contract.
Deploy one forwarder-blind contract instance per configured wallet.

```bash
cargo run -- deploy
```

Each wallet in `wallet_pem_paths` deploys its own contract instance. The resulting addresses
are written to `deploy.toml`.

> **After deploying**, copy the addresses from `deploy.toml` into the `contract_addresses`
> list in `config.toml` so that subsequent swap and drain commands can target them.

---

### `wrap`

Wrap EGLD into WEGLD via the WEGLD swap contract.
Wrap EGLD into WEGLD via the WEGLD swap contract. Runs once per wallet in parallel.

```bash
cargo run -- wrap -a <AMOUNT>
Expand All @@ -47,11 +73,11 @@ cargo run -- swap1 <METHOD> [OPTIONS]

| Method | Description |
|--------|-------------|
| `direct` | Swap directly on the DEX pair |
| `sync` | Swap via forwarder-blind using `blind_sync` |
| `async1` | Swap via forwarder-blind using `blind_async_v1` |
| `async2` | Swap via forwarder-blind using `blind_async_v2` |
| `te` | Swap via forwarder-blind using `blind_transf_exec` |
| `direct` | Each wallet swaps directly on the DEX pair |
| `sync` | Each wallet × each contract calls `blind_sync` (same-shard only) |
| `async1` | Each wallet × each contract calls `blind_async_v1` |
| `async2` | Each wallet × each contract calls `blind_async_v2` |
| `te` | Each wallet × each contract calls `blind_transf_exec` |

#### Options (all methods)

Expand Down Expand Up @@ -91,11 +117,11 @@ cargo run -- swap2 <METHOD> [OPTIONS]

| Method | Description |
|--------|-------------|
| `direct` | Swap directly on the DEX pair |
| `sync` | Swap via forwarder-blind using `blind_sync` |
| `async1` | Swap via forwarder-blind using `blind_async_v1` |
| `async2` | Swap via forwarder-blind using `blind_async_v2` |
| `te` | Swap via forwarder-blind using `blind_transf_exec` |
| `direct` | Each wallet swaps directly on the DEX pair |
| `sync` | Each wallet × each contract calls `blind_sync` (same-shard only) |
| `async1` | Each wallet × each contract calls `blind_async_v1` |
| `async2` | Each wallet × each contract calls `blind_async_v2` |
| `te` | Each wallet × each contract calls `blind_transf_exec` |

#### Options (all methods)

Expand Down Expand Up @@ -151,18 +177,22 @@ cargo run -- get-liquidity

### `drain`

Drain all WEGLD and USDC balances held by the deployed forwarder-blind contract back to the owner.
Useful to recover tokens left in the contract after transfer-execute swaps (which have no callback).
Drain all WEGLD and USDC balances held by the forwarder-blind contracts back to their owners.
Useful to recover tokens left in the contracts after transfer-execute swaps (which have no callback).

```bash
cargo run -- drain
```

For each contract address in `config.toml` (`contract_addresses`), the interactor looks up the
on-chain owner. If that owner is one of the registered wallets, it sends a drain transaction.
Contracts whose owner is not a registered wallet are skipped.

---

## Configuration

The interactor reads `config.toml` from the current directory. Example:
The interactor reads `config.toml` from the current directory:

```toml
chain_type = 'real'
Expand All @@ -171,15 +201,79 @@ wegld_address = 'erd1...' # WEGLD swap contract
pair_address = 'erd1...' # WEGLD/USDC DEX pair contract
wegld_token_id = 'WEGLD-bd4d79'
usdc_token_id = 'USDC-c76f1f'

wallet_pem_paths = [
'path/to/wallet1.pem',
'path/to/wallet2.pem',
]

contract_addresses = [
'erd1...',
'erd1...',
]
```

| Field | Description |
|-------|-------------|
| `chain_type` | `real` for mainnet/testnet or `simulator` for the chain simulator |
| `chain_type` | `real` for mainnet/testnet, or `simulator` for the chain simulator |
| `gateway_uri` | Gateway endpoint URL |
| `wegld_address` | Address of the WEGLD swap contract |
| `pair_address` | Address of the WEGLD/USDC DEX pair contract |
| `wegld_token_id` | ESDT identifier for WEGLD |
| `usdc_token_id` | ESDT identifier for USDC |
| `wallet_pem_paths` | List of PEM file paths, one per wallet. Paths are relative to the workspace root. If absent or empty, all operations are skipped with a warning. |
| `contract_addresses` | List of forwarder-blind contract addresses to target for swap-via-forwarder commands. |

---

## Deploy output

`deploy.toml` is written automatically after every `deploy` run. Example contents:

```toml
# These are the last deployed addresses. Copy them to config.toml contract_addresses to use them.
contract_addresses = [
"erd1...",
"erd1...",
]
```

The deployed contract address is persisted automatically in `state.toml` after a successful `deploy`.
This file is **output-only** — no command reads it back. All commands (swap, drain, etc.) read
contract addresses exclusively from `contract_addresses` in `config.toml`.

> **After deploying**, copy the addresses from `deploy.toml` into `config.toml`
> (`contract_addresses`) before running any other command.

---

## Multiple wallets and contracts

The interactor is designed to exercise the same call paths from many wallets simultaneously,
each going through its own contract instance. The relationship between wallets and contracts
is many-to-many.

### `deploy`
One deploy transaction is submitted per wallet. The N resulting contract addresses are stored in
`deploy.toml` in the same order as the wallets in `wallet_pem_paths`. Copy those addresses into
`config.toml` (`contract_addresses`) before running any other command.

### `wrap`
One wrap transaction is submitted per wallet. All transactions are batched and sent in parallel.

### `swap1` / `swap2` — `direct`
One swap transaction is submitted per wallet, sent directly to the DEX pair.

### `swap1` / `swap2` — `sync`, `async1`, `async2`, `te`
One transaction is submitted for every **(wallet, contract)** pair — the full Cartesian product.
For example, 3 wallets × 3 contracts = 9 transactions per command, all sent in a single batch.

**Shard constraint for `sync`:** `blind_sync` uses a synchronous call, which requires the
forwarder contract and the DEX pair to be on the same shard. Any (wallet, contract) pair where
the contract's shard differs from the DEX pair's shard is skipped with a warning.

### `drain`
Reads contract addresses from `config.toml` (`contract_addresses`), exactly like swap commands.
For each contract it fetches the on-chain owner and checks whether that address corresponds to
one of the registered wallets. Only contracts whose owner is a registered wallet receive a drain
transaction, so it is safe to run even when `config.toml` contains contracts deployed by other
parties.
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,18 @@ wegld_address = 'erd1qqqqqqqqqqqqqpgqmuk0q2saj0mgutxm4teywre6dl8wqf58xamqdrukln'
pair_address = 'erd1qqqqqqqqqqqqqpgqeel2kumf0r8ffyhth7pqdujjat9nx0862jpsg2pqaq'
wegld_token_id = 'WEGLD-bd4d79'
usdc_token_id = 'USDC-c76f1f'

# Optional: list of PEM file paths, one per wallet to use.
# Relative paths are resolved from the workspace root.
# If absent or empty, all operations are skipped with a warning.
wallet_pem_paths = [
'../../../../../sdk/core/src/test_wallets/s0phie.pem',
'../../../../../sdk/core/src/test_wallets/s1mon.pem',
'../../../../../sdk/core/src/test_wallets/s2onja.pem',
]

contract_addresses = [
"erd1qqqqqqqqqqqqqpgqwlqhw27xz6j2cqh8w8v53l5fdulfu4rqqqqqdt5qat",
"erd1qqqqqqqqqqqqqpgq62w4nut0gedtfysdgpajryeap0jptvthqqqs27tpsu",
"erd1qqqqqqqqqqqqqpgqmsxxxae3df5eylg23rapr4r4j8kevqjdqqpqq4uamr",
]
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ pub struct Config {
pub pair_address: Bech32Address,
pub wegld_token_id: String,
pub usdc_token_id: String,
/// Optional list of PEM file paths, one per wallet.
/// If absent or empty, all operations are skipped with a warning.
#[serde(default)]
pub wallet_pem_paths: Vec<String>,
/// Forwarder contract addresses to target for all swap transactions.
#[serde(default)]
pub contract_addresses: Vec<Bech32Address>,
}

impl Config {
Expand Down
Loading
Loading