Add full Chain 138 integration: 8 steps, chain spec, app-ethereum config, docs
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
19
step-03-coin-module/README.md
Normal file
19
step-03-coin-module/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Step 3 — Create coin module
|
||||
|
||||
For Chain 138 we **extend the Ethereum family** in ledger-live (no new coin-module). Use:
|
||||
|
||||
**Note:** `network-explorer.ts` uses Blockscout REST v2 API (`GET /api/v2/addresses/{address}/transactions`) with `next_page_params` pagination. Do not use Etherscan-style `module=account&action=txlist` — Blockscout uses a different API.
|
||||
|
||||
- **config.chain138.ts** — Chain 138 RPC and explorer config; plug into Ethereum family config or currency config.
|
||||
- **network-explorer.ts** — Example network layer (getLastBlock, getBalance, getTransactionCount, getAddressTransactions). Adapt to the Ethereum coin-module’s network/bridge API (e.g. wrap in the same interface as other EVM chains).
|
||||
|
||||
If Ledger requests a **dedicated** coin-module, create `libs/coin-modules/coin-defi_oracle_meta/` with:
|
||||
|
||||
- `bridge/` — sync, buildTransaction, signOperation, broadcast, getFeesForTransaction, getTransactionStatus
|
||||
- `logic/` — core logic (no bridge imports)
|
||||
- `network/` — this explorer + RPC wrapper
|
||||
- `signer/` — Step 4 getAddress + sign
|
||||
- `types/` — bridge, signer, errors
|
||||
- `config.ts`, `index.ts`
|
||||
|
||||
Dependency rule: `logic` → `network` only; `bridge` → `logic`, `network`, `signer`.
|
||||
28
step-03-coin-module/config.chain138.ts
Normal file
28
step-03-coin-module/config.chain138.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Step 3 — Create coin module (config for Ethereum family + Chain 138)
|
||||
* Target: ledger-live libs/coin-modules (or extend libs/ledger-live-common/src/families/ethereum)
|
||||
*
|
||||
* For Chain 138 we extend the Ethereum family. This config is used by the bridge/network.
|
||||
*/
|
||||
|
||||
export const CHAIN_ID = 138 as const;
|
||||
|
||||
export const RPC_URLS = [
|
||||
"https://rpc-http-pub.d-bis.org",
|
||||
"https://rpc.d-bis.org",
|
||||
"https://rpc2.d-bis.org",
|
||||
"https://rpc.public-0138.defi-oracle.io",
|
||||
"https://rpc.defi-oracle.io",
|
||||
] as const;
|
||||
|
||||
export const EXPLORER_BASE = "https://explorer.d-bis.org" as const;
|
||||
|
||||
export const chain138Config = {
|
||||
chainId: CHAIN_ID,
|
||||
rpcUrls: RPC_URLS,
|
||||
explorer: {
|
||||
address: `${EXPLORER_BASE}/address/$address`,
|
||||
tx: `${EXPLORER_BASE}/tx/$hash`,
|
||||
token: `${EXPLORER_BASE}/token/$contractAddress?a=$address`,
|
||||
},
|
||||
} as const;
|
||||
75
step-03-coin-module/network-explorer.ts
Normal file
75
step-03-coin-module/network-explorer.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Step 3 — Coin module: network layer (explorer/RPC wrapper)
|
||||
* Target: ledger-live libs/coin-modules/coin-ethereum (or coin-defi_oracle_meta) src/network/
|
||||
*
|
||||
* Wraps Chain 138 RPC and Blockscout for sync, history, fees. Use in bridge.
|
||||
*/
|
||||
|
||||
const RPC_URL = "https://rpc-http-pub.d-bis.org";
|
||||
const EXPLORER_API = "https://explorer.d-bis.org/api";
|
||||
|
||||
export async function getLastBlock(): Promise<number> {
|
||||
const res = await fetch(RPC_URL, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_blockNumber", params: [] }),
|
||||
});
|
||||
const data = await res.json();
|
||||
return parseInt(data.result, 16);
|
||||
}
|
||||
|
||||
export async function getBalance(address: string): Promise<string> {
|
||||
const res = await fetch(RPC_URL, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
method: "eth_getBalance",
|
||||
params: [address, "latest"],
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
return data.result ?? "0x0";
|
||||
}
|
||||
|
||||
export async function getTransactionCount(address: string): Promise<number> {
|
||||
const res = await fetch(RPC_URL, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
method: "eth_getTransactionCount",
|
||||
params: [address, "latest"],
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
return parseInt(data.result ?? "0x0", 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Explorer: get transactions for address.
|
||||
* Blockscout REST v2: GET /api/v2/addresses/{address_hash}/transactions
|
||||
* Response: { items: Tx[], next_page_params?: { block_number, index, items_count } }
|
||||
*/
|
||||
const EXPLORER_API_V2 = "https://explorer.d-bis.org/api/v2";
|
||||
|
||||
export async function getAddressTransactions(
|
||||
address: string,
|
||||
params?: { block_number?: number; index?: number; items_count?: number }
|
||||
): Promise<{ items: unknown[]; next_page_params?: { block_number: number; index: number; items_count: number } }> {
|
||||
let url = `${EXPLORER_API_V2}/addresses/${encodeURIComponent(address)}/transactions`;
|
||||
if (params?.block_number != null && params?.index != null && params?.items_count != null) {
|
||||
url += `?block_number=${params.block_number}&index=${params.index}&items_count=${params.items_count}`;
|
||||
}
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) return { items: [] };
|
||||
const data = await res.json();
|
||||
return {
|
||||
items: data.items ?? [],
|
||||
next_page_params: data.next_page_params,
|
||||
};
|
||||
}
|
||||
|
||||
export { RPC_URL, EXPLORER_API, EXPLORER_API_V2 };
|
||||
Reference in New Issue
Block a user