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
9 changes: 2 additions & 7 deletions contracts/test/CW20toERC20PointerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,10 @@ describe("CW20 to ERC20 Pointer", function () {
const balanceBefore = respBefore.data.balance;

const res = await executeWasm(pointer, { transfer: { recipient: accounts[1].seiAddress, amount: "100" } });
const txHash = res["txhash"];
const receipt = await ethers.provider.send('sei_getTransactionReceipt', [`0x${txHash}`]);
expect(receipt).not.to.be.null;
console.log("receipt[\"blockNumber\"]", receipt["blockNumber"]);
const bn = receipt["blockNumber"];
const bn = `0x${Number(res.height).toString(16)}`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] res.height is only set to the real inclusion height when waitForAdminTxCommit's block-walk finds the tx (contracts/test/lib.js:1007); on the logged anomalous path it stays at the -b sync CheckTx default of "0". bn then becomes 0x0, and since this test's only remaining purpose is the negative assertion expect(ethlogs.length).to.equal(0) over exactly that one block, it would pass vacuously against block 0 instead of failing. A guard such as expect(Number(res.height)).to.be.greaterThan(0) before building the filter keeps the assertion meaningful.

const filter = {
fromBlock: bn,
toBlock: 'latest',
address: receipt["to"],
toBlock: bn,
topics: [ethers.id("Transfer(address,address,uint256)")]
};
// send via eth_ endpoint - synthetic event doesn't show up
Expand Down
2 changes: 1 addition & 1 deletion contracts/test/ERC20toCW20PointerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ describe("ERC20 to CW20 Pointer", function () {
const ethReceipts = await ethers.provider.send('eth_getBlockReceipts', ['0x' + blockNumber.toString(16)]);
expect(ethReceipts.length).to.equal(1);

const ethTx = await ethers.provider.send('sei_getTransactionReceipt', [receipt.hash]);
const ethTx = await ethers.provider.send('eth_getTransactionReceipt', [receipt.hash]);
expect(ethTx.logs.length).to.equal(1); // check for transfer event
const ethTxByHash = await ethers.provider.send('eth_getTransactionByHash', [tx.hash]);
expect(ethTxByHash).to.not.be.null;
Expand Down
6 changes: 3 additions & 3 deletions contracts/test/ERC721toCW721PointerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ describe("ERC721 to CW721 Pointer", function () {
const ethBlockReceipts = await ethers.provider.send('eth_getBlockReceipts', ['0x' + receipt.blockNumber.toString(16)]);
expect(ethBlockReceipts.length).to.equal(1);

const ethTx = await ethers.provider.send('sei_getTransactionReceipt', [receipt.hash]);
const ethTx = await ethers.provider.send('eth_getTransactionReceipt', [receipt.hash]);
expect(ethTx.logs.length).to.equal(1);
const ethTxByHash = await ethers.provider.send('eth_getTransactionByHash', [receipt.hash]);
expect(ethTxByHash).to.not.be.null;
Expand All @@ -162,8 +162,8 @@ describe("ERC721 to CW721 Pointer", function () {
it("set approval for all", async function () {
const setApprovalForAllTxResp = await pointerAcc0.setApprovalForAll(accounts[1].evmAddress, true, { gasPrice: ethers.parseUnits('100', 'gwei') });
const receipt = await setApprovalForAllTxResp.wait();
const seiReceipt = await ethers.provider.send('sei_getTransactionReceipt', [receipt.hash]);
expect(seiReceipt.logs.length).to.equal(1);
const ethReceipt = await ethers.provider.send('eth_getTransactionReceipt', [receipt.hash]);
expect(ethReceipt.logs.length).to.equal(1);
const approved = await pointerAcc0.isApprovedForAll(accounts[0].evmAddress, accounts[1].evmAddress);
expect(approved).to.equal(true);

Expand Down
5 changes: 2 additions & 3 deletions docker/localnode/config/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,12 @@ trace_allow_js_tracers = false
# Legacy sei_* JSON-RPC (EVM HTTP only).
# DEPRECATION: All sei_* methods are deprecated and scheduled for removal - no new integrations.
# HTTP 200; gate errors: JSON-RPC error (data legacy_sei_deprecated). Success: unchanged body; optional header Sei-Legacy-RPC-Deprecation.
# Docker localnet enables every remaining gated method. Production defaults from seid init
# remain the three-method address/Cosmos allowlist - see evmrpc/config.
# Docker localnet enables every remaining gated method, matching the production defaults
# from seid init - see evmrpc/config.
enabled_legacy_sei_apis = [
"sei_getCosmosTx",
"sei_getEVMAddress",
"sei_getSeiAddress",
"sei_getTransactionReceipt",
]

###############################################################################
Expand Down
1 change: 0 additions & 1 deletion docker/rpcnode/config/app.toml
Original file line number Diff line number Diff line change
Expand Up @@ -278,5 +278,4 @@ enabled_legacy_sei_apis = [
"sei_getCosmosTx",
"sei_getEVMAddress",
"sei_getSeiAddress",
"sei_getTransactionReceipt",
]
4 changes: 2 additions & 2 deletions evmrpc/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ EVM RPCs prefixed by `eth_` and `debug_` on Sei generally follows [Ethereum's sp
- `eth_getProof` — deprecated rather than permanently incompatible; the message directs callers who need proofs to the Sei team.

## `sei_` prefixed endpoints
The legacy `sei_` namespace contains address/Cosmos transaction helpers and a synthetic-aware transaction receipt method.
The legacy `sei_` namespace contains address and Cosmos transaction helpers.

Legacy **`sei_*`** JSON-RPC (EVM HTTP only) are **gated** by the `[evm].enabled_legacy_sei_apis` list in `app.toml` (after `deny_list`). Enforcement is **centralized** in `wrapSeiLegacyHTTP` (see `sei_legacy_http.go`): it inspects the JSON-RPC `method` field only. Wired from `HTTPServer.EnableRPC` via `HTTPConfig.SeiLegacyAllowlist` — handlers do not duplicate gate logic. The surface is **deprecated** and scheduled for removal; **only methods named in that array** are allowed. `seid init` / `DefaultConfig` pre-fill the three `sei_*` address/Cosmos helpers; the receipt method appears **commented** in the generated template. **Docker localnet** (`docker/localnode/config/app.toml`) enables **all remaining** gated methods. **HTTP 200** for all responses. **Disabled** methods return JSON-RPC `error` code `-32601`, `message` explains not enabled + deprecated, `data` `"legacy_sei_deprecated"`. **Allowed** single-object bodies pass through **unchanged**; JSON **batches** may be subset-forwarded with responses merged by `id` (for requests that include `id`). Per JSON-RPC 2.0, **notifications** (no `id` in the request) do not produce entries in the batch response array, so the merged array is **not** 1:1 with the request batch when notifications are present; if nothing would be returned, the gateway sends an **empty HTTP body** (not `[]`). Optional deprecation signal: HTTP header `Sei-Legacy-RPC-Deprecation` (`SeiLegacyDeprecationHTTPHeader` in `sei_legacy.go`). Coverage: `evmrpc/sei_legacy_test.go` and `integration_test/evm_module/rpc_io_test/testdata/sei_legacy_deprecation/*.iox`.
Legacy **`sei_*`** JSON-RPC (EVM HTTP only) are **gated** by the `[evm].enabled_legacy_sei_apis` list in `app.toml` (after `deny_list`). Enforcement is **centralized** in `wrapSeiLegacyHTTP` (see `sei_legacy_http.go`): it inspects the JSON-RPC `method` field only. Wired from `HTTPServer.EnableRPC` via `HTTPConfig.SeiLegacyAllowlist` — handlers do not duplicate gate logic. The surface is **deprecated** and scheduled for removal; **only methods named in that array** are allowed. `seid init` / `DefaultConfig` and **Docker localnet** (`docker/localnode/config/app.toml`) enable all three remaining address/Cosmos helpers. **HTTP 200** for all responses. **Disabled** methods return JSON-RPC `error` code `-32601`, `message` explains not enabled + deprecated, `data` `"legacy_sei_deprecated"`. **Allowed** single-object bodies pass through **unchanged**; JSON **batches** may be subset-forwarded with responses merged by `id` (for requests that include `id`). Per JSON-RPC 2.0, **notifications** (no `id` in the request) do not produce entries in the batch response array, so the merged array is **not** 1:1 with the request batch when notifications are present; if nothing would be returned, the gateway sends an **empty HTTP body** (not `[]`). Optional deprecation signal: HTTP header `Sei-Legacy-RPC-Deprecation` (`SeiLegacyDeprecationHTTPHeader` in `sei_legacy.go`). Coverage: `evmrpc/sei_legacy_test.go` and `integration_test/evm_module/rpc_io_test/testdata/sei_legacy_deprecation/*.iox`.

## `debug_` prefixed endpoints
`debug_trace*` endpoints should faithfully replay historical execution. If a transaction encountered an error during its actual execution, a `debug_trace*` call for it should reflect so. If a transction consumed X amount of gas during its actual execution, a `debug_trace*` call should show that exact amount as well.
Expand Down
10 changes: 3 additions & 7 deletions evmrpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,10 @@ The remaining `sei_*` methods are:
- `sei_getSeiAddress`
- `sei_getEVMAddress`
- `sei_getCosmosTx`
- `sei_getTransactionReceipt`

These methods are available only on EVM HTTP and are gated by
`[evm].enabled_legacy_sei_apis`. The address and Cosmos transaction helpers are
enabled by default. The receipt method includes synthetic receipts and must be
enabled explicitly.
enabled by default.

There is no remaining block- or filter-level JSON-RPC method for discovering
synthetic logs from Cosmos-originated transactions. Clients that already know a
synthetic transaction hash can enable and call `sei_getTransactionReceipt` to
retrieve its logs.
There is no remaining JSON-RPC method for discovering synthetic logs from
Cosmos-originated transactions.
7 changes: 2 additions & 5 deletions evmrpc/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -859,15 +859,12 @@ deny_list = {{ .EVM.DenyList }}
# gate errors use standard JSON-RPC error encoding (see evmrpc/AGENTS.md). Successful allowlisted
# responses are unchanged; nodes may set HTTP header Sei-Legacy-RPC-Deprecation (see AGENTS.md).
#
# Only methods listed in enabled_legacy_sei_apis are allowed. Init defaults enable the three
# address/Cosmos helpers; uncomment the receipt method below to enable it.
# Only methods listed in enabled_legacy_sei_apis are allowed. Init defaults enable all three
# remaining address/Cosmos helpers.
enabled_legacy_sei_apis = [
{{- range .EVM.EnabledLegacySeiApis }}
"{{ . }}",
{{- end }}

# Optional legacy method - uncomment to enable (same deprecation applies):
# "sei_getTransactionReceipt",
]

# max number of logs a single eth_getLogs query may match before it errors,
Expand Down
2 changes: 1 addition & 1 deletion evmrpc/rate_limit_middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func TestComposedStack_RateLimitDistinctFromSizeBudget(t *testing.T) {
const maxBody = 4096
reg := mustRateLimitRegistry(t, 0.001, 1)
gate := NewRateLimitGate(reg, 0, true, "evm")
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getTransactionReceipt"})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getCosmosTx"})

body := `{"jsonrpc":"2.0","id":1,"method":"eth_call","params":[]}`
base := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down
23 changes: 3 additions & 20 deletions evmrpc/sei_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,26 +40,9 @@ var (
// seiLegacyGatedMethods is the full set of JSON-RPC methods on the sei namespace that
// are subject to [evm] enabled_legacy_sei_apis in app.toml.
var seiLegacyGatedMethods = map[string]struct{}{
"sei_getCosmosTx": {},
"sei_getEVMAddress": {},
"sei_getSeiAddress": {},
"sei_getTransactionReceipt": {},
}

// SeiLegacyAllExtraMethodNames returns gated sei_* methods other than the usual default trio
// (sei_getSeiAddress, sei_getEVMAddress, sei_getCosmosTx). Used to compose full test configs.
func SeiLegacyAllExtraMethodNames() []string {
out := make([]string, 0, len(seiLegacyGatedMethods))
for m := range seiLegacyGatedMethods {
switch strings.ToLower(m) {
case "sei_getseiaddress", "sei_getevmaddress", "sei_getcosmostx":
continue
default:
out = append(out, m)
}
}
sort.Strings(out)
return out
"sei_getCosmosTx": {},
"sei_getEVMAddress": {},
"sei_getSeiAddress": {},
}

// SeiLegacyAllGatedMethodNames returns every gated sei_* method (sorted). Use when tests need full parity.
Expand Down
10 changes: 5 additions & 5 deletions evmrpc/sei_legacy_http_gzip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ func makeEchoResp(raw json.RawMessage) json.RawMessage {

// allowAll is a permissive allowlist that lets every sei_* method through.
var allowAll = map[string]struct{}{
"sei_getTransactionReceipt": {},
"sei_someMethod": {},
"sei_getCosmosTx": {},
"sei_someMethod": {},
}

// postJSON builds an *http.Request with the given JSON body and Accept-Encoding: gzip.
Expand Down Expand Up @@ -165,7 +165,7 @@ func TestHandleSingle_NonGatedMethod_GzipIntact(t *testing.T) {
func TestHandleSingle_GatedMethod_DeprecationHeader(t *testing.T) {
gate := wrapSeiLegacyHTTP(gzipHandler(echoHandler), allowAll, 0)

body := `{"jsonrpc":"2.0","id":1,"method":"sei_getTransactionReceipt"}`
body := `{"jsonrpc":"2.0","id":1,"method":"sei_getCosmosTx"}`
rec := httptest.NewRecorder()
gate.ServeHTTP(rec, postJSON(t, body))

Expand All @@ -185,7 +185,7 @@ func TestHandleBatch_GatedMethod_DeprecationHeader(t *testing.T) {
gate := wrapSeiLegacyHTTP(gzipHandler(echoHandler), allowAll, 0)

body := `[
{"jsonrpc":"2.0","id":1,"method":"sei_getTransactionReceipt"},
{"jsonrpc":"2.0","id":1,"method":"sei_getCosmosTx"},
{"jsonrpc":"2.0","id":2,"method":"eth_blockNumber"}
]`
rec := httptest.NewRecorder()
Expand All @@ -207,7 +207,7 @@ func TestHandleSingle_BlockedMethod_ReturnsError(t *testing.T) {
// Empty allowlist → all sei_* methods are blocked.
gate := wrapSeiLegacyHTTP(gzipHandler(echoHandler), map[string]struct{}{}, 0)

body := `{"jsonrpc":"2.0","id":1,"method":"sei_getTransactionReceipt"}`
body := `{"jsonrpc":"2.0","id":1,"method":"sei_getCosmosTx"}`
rec := httptest.NewRecorder()
gate.ServeHTTP(rec, postJSON(t, body))

Expand Down
34 changes: 17 additions & 17 deletions evmrpc/sei_legacy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ func TestBuildSeiLegacyEnabledSet_InitDefaults(t *testing.T) {
}
}

func TestBuildSeiLegacyEnabledSet_Extra(t *testing.T) {
func TestBuildSeiLegacyEnabledSet_RemovedReceiptIgnored(t *testing.T) {
s := BuildSeiLegacyEnabledSet([]string{"SEI_GETTRANSACTIONRECEIPT"})
if _, ok := s["sei_getTransactionReceipt"]; !ok {
t.Fatal("expected case-insensitive match")
if len(s) != 0 {
t.Fatalf("removed method should not be accepted, got %v", s)
}
}

Expand All @@ -67,15 +67,15 @@ func TestSeiLegacyGateError_DisabledWhenEmptyAllowlist(t *testing.T) {
}

func TestSeiLegacyGateError_AllowedWhenListed(t *testing.T) {
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getTransactionReceipt"})
err := seiLegacyGateError("Sei_GetTransactionReceipt", enabled)
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getCosmosTx"})
err := seiLegacyGateError("Sei_GetCosmosTx", enabled)
if err != nil {
t.Fatalf("unexpected: %v", err)
}
}

func TestSeiLegacyGateError_UnknownSeiNamespaceFailsClosed(t *testing.T) {
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getTransactionReceipt"})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getCosmosTx"})
err := seiLegacyGateError("sei_notARealRegisteredMethod", enabled)
if err == nil {
t.Fatal("expected error for unknown sei_* method when allowlist is active")
Expand All @@ -100,7 +100,7 @@ func TestWrapSeiLegacyHTTP_UnknownSeiMethodBlocked(t *testing.T) {
inner := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
t.Fatal("inner should not run for unknown sei_* method")
})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getTransactionReceipt"})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getCosmosTx"})
h := wrapSeiLegacyHTTP(inner, enabled, 0)
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(
`{"jsonrpc":"2.0","id":1,"method":"sei_futureHypotheticalMethod","params":[]}`))
Expand Down Expand Up @@ -161,12 +161,12 @@ func TestWrapSeiLegacyHTTP_RaisedBodyLimitNotTruncated(t *testing.T) {
gotLen = len(b)
_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":"0x1"}`))
})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getTransactionReceipt"})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getCosmosTx"})
h := wrapSeiLegacyHTTP(inner, enabled, maxBody)

// Allowed gated method with a padded param pushing the body well past 5MiB.
pad := strings.Repeat("a", 6*1024*1024)
body := `{"jsonrpc":"2.0","id":1,"method":"sei_getTransactionReceipt","params":["` + pad + `"]}`
body := `{"jsonrpc":"2.0","id":1,"method":"sei_getCosmosTx","params":["` + pad + `"]}`
if len(body) <= seiLegacyHTTPDefault5MiB {
t.Fatalf("test body %d must exceed 5MiB to exercise truncation", len(body))
}
Expand All @@ -186,13 +186,13 @@ func TestWrapSeiLegacyHTTP_OverLimitBodyRejectedNotTruncated(t *testing.T) {
inner := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
innerCalled = true
})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getTransactionReceipt"})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getCosmosTx"})
h := wrapSeiLegacyHTTP(inner, enabled, maxBody)

// Body exceeds maxBody. The gate must reject with 413 rather than silently
// truncating to maxBody and forwarding to the inner handler.
pad := strings.Repeat("a", maxBody)
body := `{"jsonrpc":"2.0","id":1,"method":"sei_getTransactionReceipt","params":["` + pad + `"]}`
body := `{"jsonrpc":"2.0","id":1,"method":"sei_getCosmosTx","params":["` + pad + `"]}`
if int64(len(body)) <= maxBody {
t.Fatalf("test body %d must exceed maxBody %d", len(body), maxBody)
}
Expand Down Expand Up @@ -220,10 +220,10 @@ func TestWrapSeiLegacyHTTP_BodyExactlyAtLimitForwarded(t *testing.T) {
gotLen = len(b)
_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":"0x1"}`))
})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getTransactionReceipt"})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getCosmosTx"})
h := wrapSeiLegacyHTTP(inner, enabled, maxBody)

prefix := `{"jsonrpc":"2.0","id":1,"method":"sei_getTransactionReceipt","params":["`
prefix := `{"jsonrpc":"2.0","id":1,"method":"sei_getCosmosTx","params":["`
suffix := `"]}`
pad := strings.Repeat("a", maxBody-len(prefix)-len(suffix))
body := prefix + pad + suffix
Expand All @@ -250,9 +250,9 @@ func TestWrapSeiLegacyHTTP_BodyExactlyAtLimitForwarded(t *testing.T) {
// be rejected with 413 without reaching the inner handler, and an at-limit body must pass.
func TestComposedStack_OverLimitRejectedConsistently(t *testing.T) {
const maxBody = 1024
prefix := `{"jsonrpc":"2.0","id":1,"method":"sei_getTransactionReceipt","params":["`
prefix := `{"jsonrpc":"2.0","id":1,"method":"sei_getCosmosTx","params":["`
suffix := `"]}`
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getTransactionReceipt"})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getCosmosTx"})

mkBody := func(total int) string {
return prefix + strings.Repeat("a", total-len(prefix)-len(suffix)) + suffix
Expand Down Expand Up @@ -305,10 +305,10 @@ func TestWrapSeiLegacyHTTP_AllowedMethodPassthroughAndDeprecationHeader(t *testi
called = true
_, _ = w.Write([]byte(`{"jsonrpc":"2.0","id":1,"result":{"number":"0x1"}}`))
})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getTransactionReceipt"})
enabled := BuildSeiLegacyEnabledSet([]string{"sei_getCosmosTx"})
h := wrapSeiLegacyHTTP(inner, enabled, 0)
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(
`{"jsonrpc":"2.0","id":1,"method":"sei_getTransactionReceipt","params":[]}`))
`{"jsonrpc":"2.0","id":1,"method":"sei_getCosmosTx","params":[]}`))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
h.ServeHTTP(rec, req)
Expand Down
6 changes: 0 additions & 6 deletions evmrpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ func NewEVMHTTPServer(
}
seiLegacyAllowlist := BuildSeiLegacyEnabledSet(config.EnabledLegacySeiApis)

seiTxAPI := NewSeiTransactionAPI(tmClient, k, ctxProvider, txConfigProvider, homeDir, ConnectionTypeHTTP, methodTimeout, watermarks, globalBlockCache, cacheCreationMutex)

// DB semaphore aligned with worker count
dbReadSemaphore := make(chan struct{}, workerCount)
globalLogSlicePool := NewLogSlicePool()
Expand All @@ -129,10 +127,6 @@ func NewEVMHTTPServer(
Namespace: "eth",
Service: txAPI,
},
{
Namespace: "sei",
Service: seiTxAPI,
},
{
Namespace: "eth",
Service: NewStateAPI(tmClient, k, ctxProvider, ConnectionTypeHTTP, watermarks),
Expand Down
Loading
Loading