From 2affc39907675ccd4d1372196087a3c4d987a5b9 Mon Sep 17 00:00:00 2001 From: bdchatham Date: Wed, 22 Jul 2026 16:24:43 -0700 Subject: [PATCH 1/4] feat(genesis): add Vesting field to SeiNetwork's GenesisAccount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an optional Vesting field (amount, end-time, delayed) to spec.genesis.accounts[] so a genesis-time account can be seeded under a vesting schedule instead of as a plain account; nil (the default) is unchanged. Threads through the SDK's plain GenesisAccount mirror, the k8s-provider render step, and the planner's mapping into the sidecar's assemble-and-upload-genesis task params. Restores a coverage path lost when sei-chain#3714 deprecated live MsgCreateVestingAccount: constructing the vesting account directly at genesis-assembly time sidesteps that deprecation entirely, since it never goes through the msg-server tx path. Depends on sei-protocol/seictl#, which adds the matching GenesisAccountVesting wire type and the actual account-construction logic. This PR is code-complete and fully tested locally against that branch (via a temporary go.mod replace, not committed), but go.mod still pins seictl v0.0.64 — bumping it to a version containing the new field is a required follow-up once that PR merges and tags, not before. CI on this PR is expected red until then. --- api/v1alpha1/seinetwork_types.go | 29 +++++++++++++++++ api/v1alpha1/zz_generated.deepcopy.go | 24 ++++++++++++++- config/crd/sei.io_seinetworks.yaml | 32 +++++++++++++++++++ internal/planner/group.go | 10 +++++- internal/planner/group_accounts_test.go | 41 +++++++++++++++++++++++++ manifests/sei.io_seinetworks.yaml | 32 +++++++++++++++++++ sdk/sei/provider/k8s/render.go | 11 +++++-- sdk/sei/provider/k8s/render_test.go | 29 +++++++++++++++++ sdk/sei/spec.go | 12 ++++++++ 9 files changed, 216 insertions(+), 4 deletions(-) diff --git a/api/v1alpha1/seinetwork_types.go b/api/v1alpha1/seinetwork_types.go index c09b40b1..8e48c97a 100644 --- a/api/v1alpha1/seinetwork_types.go +++ b/api/v1alpha1/seinetwork_types.go @@ -148,6 +148,35 @@ type GenesisAccount struct { // Balance is the initial balance in coin notation (e.g. "1000000usei"). // +kubebuilder:validation:MinLength=1 Balance string `json:"balance"` + + // Vesting, when set, locks part of Balance under a vesting schedule + // instead of funding a standard account; nil produces today's plain + // account. The locked portion mirrors the (deprecated-for-live-tx) + // x/auth/vesting account types — ContinuousVestingAccount by default, + // or DelayedVestingAccount when Vesting.Delayed is set — constructed + // directly at genesis-assembly time rather than via a live + // MsgCreateVestingAccount tx, so it is unaffected by that deprecation. + // +optional + Vesting *GenesisAccountVesting `json:"vesting,omitempty"` +} + +// GenesisAccountVesting is the vesting schedule locking part of a +// GenesisAccount's Balance. +type GenesisAccountVesting struct { + // Amount is the vesting-locked portion of the account's Balance, in coin + // notation (e.g. "1000000usei"). Must not exceed Balance. + // +kubebuilder:validation:MinLength=1 + Amount string `json:"amount"` + + // EndTime is the unix timestamp at which the vesting schedule completes. + // +kubebuilder:validation:Minimum=1 + EndTime int64 `json:"endTime"` + + // Delayed selects a DelayedVestingAccount (Amount unlocks all at once at + // EndTime) instead of the default ContinuousVestingAccount (Amount + // unlocks linearly from the network's genesis time to EndTime). + // +optional + Delayed bool `json:"delayed,omitempty"` } // --------------------------------------------------------------------------- diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a8cbb646..1a926cd9 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -267,6 +267,11 @@ func (in *FullNodeSpec) DeepCopy() *FullNodeSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GenesisAccount) DeepCopyInto(out *GenesisAccount) { *out = *in + if in.Vesting != nil { + in, out := &in.Vesting, &out.Vesting + *out = new(GenesisAccountVesting) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenesisAccount. @@ -279,13 +284,30 @@ func (in *GenesisAccount) DeepCopy() *GenesisAccount { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GenesisAccountVesting) DeepCopyInto(out *GenesisAccountVesting) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GenesisAccountVesting. +func (in *GenesisAccountVesting) DeepCopy() *GenesisAccountVesting { + if in == nil { + return nil + } + out := new(GenesisAccountVesting) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GenesisCeremonyConfig) DeepCopyInto(out *GenesisCeremonyConfig) { *out = *in if in.Accounts != nil { in, out := &in.Accounts, &out.Accounts *out = make([]GenesisAccount, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } if in.Overrides != nil { in, out := &in.Overrides, &out.Overrides diff --git a/config/crd/sei.io_seinetworks.yaml b/config/crd/sei.io_seinetworks.yaml index cecb12b7..47ab3f0e 100644 --- a/config/crd/sei.io_seinetworks.yaml +++ b/config/crd/sei.io_seinetworks.yaml @@ -167,6 +167,38 @@ spec: (e.g. "1000000usei"). minLength: 1 type: string + vesting: + description: |- + Vesting, when set, locks part of Balance under a vesting schedule + instead of funding a standard account; nil produces today's plain + account. The locked portion mirrors the (deprecated-for-live-tx) + x/auth/vesting account types — ContinuousVestingAccount by default, + or DelayedVestingAccount when Vesting.Delayed is set — constructed + directly at genesis-assembly time rather than via a live + MsgCreateVestingAccount tx, so it is unaffected by that deprecation. + properties: + amount: + description: |- + Amount is the vesting-locked portion of the account's Balance, in coin + notation (e.g. "1000000usei"). Must not exceed Balance. + minLength: 1 + type: string + delayed: + description: |- + Delayed selects a DelayedVestingAccount (Amount unlocks all at once at + EndTime) instead of the default ContinuousVestingAccount (Amount + unlocks linearly from the network's genesis time to EndTime). + type: boolean + endTime: + description: EndTime is the unix timestamp at which + the vesting schedule completes. + format: int64 + minimum: 1 + type: integer + required: + - amount + - endTime + type: object required: - address - balance diff --git a/internal/planner/group.go b/internal/planner/group.go index 2ae8d69e..81498bd5 100644 --- a/internal/planner/group.go +++ b/internal/planner/group.go @@ -34,7 +34,15 @@ func (p *genesisGroupPlanner) BuildPlan( accounts := make([]sidecar.GenesisAccountEntry, len(network.Spec.Genesis.Accounts)) for i, a := range network.Spec.Genesis.Accounts { - accounts[i] = sidecar.GenesisAccountEntry{Address: a.Address, Balance: a.Balance} + entry := sidecar.GenesisAccountEntry{Address: a.Address, Balance: a.Balance} + if a.Vesting != nil { + entry.Vesting = &sidecar.GenesisAccountVesting{ + Amount: a.Vesting.Amount, + EndTime: a.Vesting.EndTime, + Delayed: a.Vesting.Delayed, + } + } + accounts[i] = entry } // Validate at planner-time so bech32 / shape errors hit diff --git a/internal/planner/group_accounts_test.go b/internal/planner/group_accounts_test.go index 6f70c166..e878e03e 100644 --- a/internal/planner/group_accounts_test.go +++ b/internal/planner/group_accounts_test.go @@ -76,6 +76,47 @@ func TestBuildPlan_NilAccountsOmitsField(t *testing.T) { } } +func TestBuildPlan_PropagatesVesting(t *testing.T) { + group := groupWithAccounts([]seiv1alpha1.GenesisAccount{ + { + Address: validSeiAddr, + Balance: "2000000usei", + Vesting: &seiv1alpha1.GenesisAccountVesting{ + Amount: "1000000usei", + EndTime: 1893456000, + Delayed: true, + }, + }, + {Address: validSeiAddr2, Balance: testBalance1000usei}, // no Vesting: must stay nil, not zero-valued + }) + p, err := ForGroup(group) + if err != nil { + t.Fatalf("ForGroup: %v", err) + } + plan, err := p.BuildPlan(group) + if err != nil { + t.Fatalf("BuildPlan: %v", err) + } + + var params sidecar.AssembleAndUploadGenesisTask + if err := json.Unmarshal(plan.Tasks[0].Params.Raw, ¶ms); err != nil { + t.Fatalf("unmarshal: %v", err) + } + if len(params.Accounts) != 2 { + t.Fatalf("Accounts: got %d, want 2", len(params.Accounts)) + } + v := params.Accounts[0].Vesting + if v == nil { + t.Fatalf("Accounts[0].Vesting: got nil, want set") + } + if v.Amount != "1000000usei" || v.EndTime != 1893456000 || !v.Delayed { + t.Errorf("Accounts[0].Vesting = %+v", v) + } + if params.Accounts[1].Vesting != nil { + t.Errorf("Accounts[1].Vesting: got %+v, want nil", params.Accounts[1].Vesting) + } +} + func TestBuildPlan_RejectsBadBech32_PlannerTime(t *testing.T) { // Validate() runs at planner time so a bad address surfaces in // kubectl describe rather than burning a sidecar Job pod. diff --git a/manifests/sei.io_seinetworks.yaml b/manifests/sei.io_seinetworks.yaml index cecb12b7..47ab3f0e 100644 --- a/manifests/sei.io_seinetworks.yaml +++ b/manifests/sei.io_seinetworks.yaml @@ -167,6 +167,38 @@ spec: (e.g. "1000000usei"). minLength: 1 type: string + vesting: + description: |- + Vesting, when set, locks part of Balance under a vesting schedule + instead of funding a standard account; nil produces today's plain + account. The locked portion mirrors the (deprecated-for-live-tx) + x/auth/vesting account types — ContinuousVestingAccount by default, + or DelayedVestingAccount when Vesting.Delayed is set — constructed + directly at genesis-assembly time rather than via a live + MsgCreateVestingAccount tx, so it is unaffected by that deprecation. + properties: + amount: + description: |- + Amount is the vesting-locked portion of the account's Balance, in coin + notation (e.g. "1000000usei"). Must not exceed Balance. + minLength: 1 + type: string + delayed: + description: |- + Delayed selects a DelayedVestingAccount (Amount unlocks all at once at + EndTime) instead of the default ContinuousVestingAccount (Amount + unlocks linearly from the network's genesis time to EndTime). + type: boolean + endTime: + description: EndTime is the unix timestamp at which + the vesting schedule completes. + format: int64 + minimum: 1 + type: integer + required: + - amount + - endTime + type: object required: - address - balance diff --git a/sdk/sei/provider/k8s/render.go b/sdk/sei/provider/k8s/render.go index ac55f17d..f7011bd6 100644 --- a/sdk/sei/provider/k8s/render.go +++ b/sdk/sei/provider/k8s/render.go @@ -58,8 +58,15 @@ func renderNetwork(spec sei.NetworkSpec, namespace string) *seiv1alpha1.SeiNetwo net.Spec.ConfigOverrides = maps.Clone(spec.Config) } for _, a := range spec.Accounts { - net.Spec.Genesis.Accounts = append(net.Spec.Genesis.Accounts, - seiv1alpha1.GenesisAccount{Address: a.Address, Balance: a.Balance}) + acc := seiv1alpha1.GenesisAccount{Address: a.Address, Balance: a.Balance} + if a.Vesting != nil { + acc.Vesting = &seiv1alpha1.GenesisAccountVesting{ + Amount: a.Vesting.Amount, + EndTime: a.Vesting.EndTime, + Delayed: a.Vesting.Delayed, + } + } + net.Spec.Genesis.Accounts = append(net.Spec.Genesis.Accounts, acc) } if spec.SidecarImage != "" { // Pin the seictl sidecar image on this network; the controller propagates diff --git a/sdk/sei/provider/k8s/render_test.go b/sdk/sei/provider/k8s/render_test.go index 4e5780d2..7f3ec382 100644 --- a/sdk/sei/provider/k8s/render_test.go +++ b/sdk/sei/provider/k8s/render_test.go @@ -7,6 +7,35 @@ import ( "github.com/sei-protocol/sei-k8s-controller/sdk/sei" ) +func TestRenderNetwork_PropagatesVesting(t *testing.T) { + spec := sei.NetworkSpec{ + Name: testNet, Image: testImage, Validators: 1, + Accounts: []sei.GenesisAccount{ + { + Address: "sei1abc", + Balance: "2000000usei", + Vesting: &sei.GenesisAccountVesting{Amount: "1000000usei", EndTime: 1893456000, Delayed: true}, + }, + {Address: "sei1def", Balance: "100usei"}, // no Vesting: must stay nil, not zero-valued + }, + } + net := renderNetwork(spec, testNS) + + if len(net.Spec.Genesis.Accounts) != 2 { + t.Fatalf("genesis accounts = %+v", net.Spec.Genesis.Accounts) + } + v := net.Spec.Genesis.Accounts[0].Vesting + if v == nil { + t.Fatalf("Accounts[0].Vesting: got nil, want set") + } + if v.Amount != "1000000usei" || v.EndTime != 1893456000 || !v.Delayed { + t.Errorf("Accounts[0].Vesting = %+v", v) + } + if net.Spec.Genesis.Accounts[1].Vesting != nil { + t.Errorf("Accounts[1].Vesting: got %+v, want nil", net.Spec.Genesis.Accounts[1].Vesting) + } +} + func TestRenderNetwork_ChainIDDefaultsToName(t *testing.T) { spec := sei.NetworkSpec{ Name: testNet, Image: testImage, Validators: 4, diff --git a/sdk/sei/spec.go b/sdk/sei/spec.go index 22fcb095..7118f3cf 100644 --- a/sdk/sei/spec.go +++ b/sdk/sei/spec.go @@ -38,6 +38,18 @@ type NetworkSpec struct { type GenesisAccount struct { Address string Balance string + + // Vesting, when set, locks part of Balance under a vesting schedule + // instead of funding a standard account; nil produces a plain account. + Vesting *GenesisAccountVesting +} + +// GenesisAccountVesting is the vesting schedule locking part of a +// GenesisAccount's Balance. Mirrors seiv1alpha1.GenesisAccountVesting. +type GenesisAccountVesting struct { + Amount string // vesting-locked portion of Balance, coin notation; must not exceed Balance + EndTime int64 // unix timestamp the vesting schedule completes + Delayed bool // DelayedVestingAccount (all-at-once at EndTime) instead of the default ContinuousVestingAccount (linear from genesis time) } // NodeSpec is the typed input to CreateNode — one RPC node peered to a network. From 42bf5d24abf31801ceb484ff2f563dfc8ee68cfc Mon Sep 17 00:00:00 2001 From: bdchatham Date: Wed, 22 Jul 2026 16:40:47 -0700 Subject: [PATCH 2/4] docs(crd): describe vesting field behaviorally, not by internal type names Addresses xreview idiom finding (S1): the GenesisAccount.Vesting field doc becomes operator-facing via kubectl explain and the rendered CRD description, but named internal sei-chain implementation symbols (ContinuousVestingAccount, DelayedVestingAccount, MsgCreateVestingAccount) that no other field doc in GenesisCeremonyConfig references. Rewritten to describe what the operator gets (locked coins that count toward balance, can be staked, unlock linearly or all-at-once at EndTime); the type-mapping provenance stays at the seictl construction site where it belongs. Regenerated CRD manifests. No schema/deepcopy change. --- api/v1alpha1/seinetwork_types.go | 23 ++++++++++------------- config/crd/sei.io_seinetworks.yaml | 21 +++++++++------------ manifests/sei.io_seinetworks.yaml | 21 +++++++++------------ 3 files changed, 28 insertions(+), 37 deletions(-) diff --git a/api/v1alpha1/seinetwork_types.go b/api/v1alpha1/seinetwork_types.go index 8e48c97a..4099af8d 100644 --- a/api/v1alpha1/seinetwork_types.go +++ b/api/v1alpha1/seinetwork_types.go @@ -149,32 +149,29 @@ type GenesisAccount struct { // +kubebuilder:validation:MinLength=1 Balance string `json:"balance"` - // Vesting, when set, locks part of Balance under a vesting schedule - // instead of funding a standard account; nil produces today's plain - // account. The locked portion mirrors the (deprecated-for-live-tx) - // x/auth/vesting account types — ContinuousVestingAccount by default, - // or DelayedVestingAccount when Vesting.Delayed is set — constructed - // directly at genesis-assembly time rather than via a live - // MsgCreateVestingAccount tx, so it is unaffected by that deprecation. + // Vesting, when set, locks part of Balance on an unlock schedule instead + // of funding a fully-spendable account; nil funds a standard account. + // The locked coins still count toward the balance and can be staked, but + // cannot be transferred until they unlock. // +optional Vesting *GenesisAccountVesting `json:"vesting,omitempty"` } -// GenesisAccountVesting is the vesting schedule locking part of a -// GenesisAccount's Balance. +// GenesisAccountVesting locks part of a GenesisAccount's Balance on an unlock +// schedule that completes at EndTime. type GenesisAccountVesting struct { // Amount is the vesting-locked portion of the account's Balance, in coin // notation (e.g. "1000000usei"). Must not exceed Balance. // +kubebuilder:validation:MinLength=1 Amount string `json:"amount"` - // EndTime is the unix timestamp at which the vesting schedule completes. + // EndTime is the unix timestamp at which the locked Amount fully unlocks. + // Must be after the network's genesis time. // +kubebuilder:validation:Minimum=1 EndTime int64 `json:"endTime"` - // Delayed selects a DelayedVestingAccount (Amount unlocks all at once at - // EndTime) instead of the default ContinuousVestingAccount (Amount - // unlocks linearly from the network's genesis time to EndTime). + // Delayed unlocks the full Amount all at once at EndTime. The default + // (false) unlocks it linearly from the network's genesis time to EndTime. // +optional Delayed bool `json:"delayed,omitempty"` } diff --git a/config/crd/sei.io_seinetworks.yaml b/config/crd/sei.io_seinetworks.yaml index 47ab3f0e..6bb439dd 100644 --- a/config/crd/sei.io_seinetworks.yaml +++ b/config/crd/sei.io_seinetworks.yaml @@ -169,13 +169,10 @@ spec: type: string vesting: description: |- - Vesting, when set, locks part of Balance under a vesting schedule - instead of funding a standard account; nil produces today's plain - account. The locked portion mirrors the (deprecated-for-live-tx) - x/auth/vesting account types — ContinuousVestingAccount by default, - or DelayedVestingAccount when Vesting.Delayed is set — constructed - directly at genesis-assembly time rather than via a live - MsgCreateVestingAccount tx, so it is unaffected by that deprecation. + Vesting, when set, locks part of Balance on an unlock schedule instead + of funding a fully-spendable account; nil funds a standard account. + The locked coins still count toward the balance and can be staked, but + cannot be transferred until they unlock. properties: amount: description: |- @@ -185,13 +182,13 @@ spec: type: string delayed: description: |- - Delayed selects a DelayedVestingAccount (Amount unlocks all at once at - EndTime) instead of the default ContinuousVestingAccount (Amount - unlocks linearly from the network's genesis time to EndTime). + Delayed unlocks the full Amount all at once at EndTime. The default + (false) unlocks it linearly from the network's genesis time to EndTime. type: boolean endTime: - description: EndTime is the unix timestamp at which - the vesting schedule completes. + description: |- + EndTime is the unix timestamp at which the locked Amount fully unlocks. + Must be after the network's genesis time. format: int64 minimum: 1 type: integer diff --git a/manifests/sei.io_seinetworks.yaml b/manifests/sei.io_seinetworks.yaml index 47ab3f0e..6bb439dd 100644 --- a/manifests/sei.io_seinetworks.yaml +++ b/manifests/sei.io_seinetworks.yaml @@ -169,13 +169,10 @@ spec: type: string vesting: description: |- - Vesting, when set, locks part of Balance under a vesting schedule - instead of funding a standard account; nil produces today's plain - account. The locked portion mirrors the (deprecated-for-live-tx) - x/auth/vesting account types — ContinuousVestingAccount by default, - or DelayedVestingAccount when Vesting.Delayed is set — constructed - directly at genesis-assembly time rather than via a live - MsgCreateVestingAccount tx, so it is unaffected by that deprecation. + Vesting, when set, locks part of Balance on an unlock schedule instead + of funding a fully-spendable account; nil funds a standard account. + The locked coins still count toward the balance and can be staked, but + cannot be transferred until they unlock. properties: amount: description: |- @@ -185,13 +182,13 @@ spec: type: string delayed: description: |- - Delayed selects a DelayedVestingAccount (Amount unlocks all at once at - EndTime) instead of the default ContinuousVestingAccount (Amount - unlocks linearly from the network's genesis time to EndTime). + Delayed unlocks the full Amount all at once at EndTime. The default + (false) unlocks it linearly from the network's genesis time to EndTime. type: boolean endTime: - description: EndTime is the unix timestamp at which - the vesting schedule completes. + description: |- + EndTime is the unix timestamp at which the locked Amount fully unlocks. + Must be after the network's genesis time. format: int64 minimum: 1 type: integer From 0a655b47e71b4654502ddff5cfac4ce8eb547574 Mon Sep 17 00:00:00 2001 From: bdchatham Date: Thu, 23 Jul 2026 07:50:06 -0700 Subject: [PATCH 3/4] chore: bump seictl v0.0.64 -> v0.0.68 for GenesisAccountVesting wire type v0.0.68 ships seictl#238's GenesisAccountVesting field that this branch's planner mapping and SDK depend on. Unblocks the previously-red CI (the draft was pinned to v0.0.64, which lacked the field). Verified locally: the seictl/sidecar/client-consuming packages build and the vesting propagation tests (TestBuildPlan_PropagatesVesting, TestRenderNetwork_PropagatesVesting) pass against the real v0.0.68. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c6eaf132..09dde97b 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/google/uuid v1.6.0 github.com/onsi/gomega v1.39.1 github.com/sei-protocol/sei-config v0.0.23 - github.com/sei-protocol/seictl v0.0.64 + github.com/sei-protocol/seictl v0.0.68 go.opentelemetry.io/otel v1.43.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 go.opentelemetry.io/otel/exporters/prometheus v0.65.0 diff --git a/go.sum b/go.sum index 72a29253..6e7af962 100644 --- a/go.sum +++ b/go.sum @@ -172,8 +172,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sei-protocol/sei-config v0.0.23 h1:pGFxRnKoXZLK3Ew/Vd5lgC5RvH9Wo58NnL19CuMgFIk= github.com/sei-protocol/sei-config v0.0.23/go.mod h1:zcEdLzyIH2AyP0/QRBE3s4Y9eGn0C/qAUx1c4o4EROU= -github.com/sei-protocol/seictl v0.0.64 h1:GfAA/2br7nDTlpiav1BC7Z2fUS9iv7gXYhECXKClPiY= -github.com/sei-protocol/seictl v0.0.64/go.mod h1:YJYgm3yl1fTpLWdPrpwgAqyONK5IwSZ3Dlw4dzH6U6U= +github.com/sei-protocol/seictl v0.0.68 h1:dCT94Ys4OjiPt5Y6TWqtgJ8GKTiWv7tN87D8HqIQxbk= +github.com/sei-protocol/seictl v0.0.68/go.mod h1:kI3HIAIWzuJSme8LqJH1WZiITrSIx5yDtJJea07Xt8Y= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= From e2c3239d87e8428811ea16578c2482112e4e0ba0 Mon Sep 17 00:00:00 2001 From: bdchatham Date: Thu, 23 Jul 2026 07:58:20 -0700 Subject: [PATCH 4/4] test: dedup string literals in new vesting tests (goconst) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI's golangci-lint (--new-from-patch) flagged two goconst issues my new tests introduced: reuse the existing testAccountBalance const for the vesting amount in group_accounts_test.go, and add a shared testGenesisAddr const for the repeated "sei1abc" fixture in render_test.go. Verified with golangci-lint locally (the tool I'd skipped — ran go vet/gofmt only). --- internal/planner/group_accounts_test.go | 4 ++-- sdk/sei/provider/k8s/k8s_test.go | 2 ++ sdk/sei/provider/k8s/render_test.go | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/planner/group_accounts_test.go b/internal/planner/group_accounts_test.go index e878e03e..daa6c758 100644 --- a/internal/planner/group_accounts_test.go +++ b/internal/planner/group_accounts_test.go @@ -82,7 +82,7 @@ func TestBuildPlan_PropagatesVesting(t *testing.T) { Address: validSeiAddr, Balance: "2000000usei", Vesting: &seiv1alpha1.GenesisAccountVesting{ - Amount: "1000000usei", + Amount: testAccountBalance, EndTime: 1893456000, Delayed: true, }, @@ -109,7 +109,7 @@ func TestBuildPlan_PropagatesVesting(t *testing.T) { if v == nil { t.Fatalf("Accounts[0].Vesting: got nil, want set") } - if v.Amount != "1000000usei" || v.EndTime != 1893456000 || !v.Delayed { + if v.Amount != testAccountBalance || v.EndTime != 1893456000 || !v.Delayed { t.Errorf("Accounts[0].Vesting = %+v", v) } if params.Accounts[1].Vesting != nil { diff --git a/sdk/sei/provider/k8s/k8s_test.go b/sdk/sei/provider/k8s/k8s_test.go index 598e15d4..8e5a0ca5 100644 --- a/sdk/sei/provider/k8s/k8s_test.go +++ b/sdk/sei/provider/k8s/k8s_test.go @@ -26,6 +26,8 @@ const ( testNet = "chaos-net" testImage = "img:1" + testGenesisAddr = "sei1abc" // non-validator genesis account fixture + rpc0Name = "rpc-0" rpc1Name = "rpc-1" resultKey = "result" diff --git a/sdk/sei/provider/k8s/render_test.go b/sdk/sei/provider/k8s/render_test.go index 7f3ec382..7099b811 100644 --- a/sdk/sei/provider/k8s/render_test.go +++ b/sdk/sei/provider/k8s/render_test.go @@ -12,7 +12,7 @@ func TestRenderNetwork_PropagatesVesting(t *testing.T) { Name: testNet, Image: testImage, Validators: 1, Accounts: []sei.GenesisAccount{ { - Address: "sei1abc", + Address: testGenesisAddr, Balance: "2000000usei", Vesting: &sei.GenesisAccountVesting{Amount: "1000000usei", EndTime: 1893456000, Delayed: true}, }, @@ -41,7 +41,7 @@ func TestRenderNetwork_ChainIDDefaultsToName(t *testing.T) { Name: testNet, Image: testImage, Validators: 4, Genesis: map[string]string{"staking.params.unbonding_time": "60s"}, Config: map[string]string{"app.pruning": "nothing"}, - Accounts: []sei.GenesisAccount{{Address: "sei1abc", Balance: "100usei"}}, + Accounts: []sei.GenesisAccount{{Address: testGenesisAddr, Balance: "100usei"}}, Labels: map[string]string{testRunLabel: testRunID}, DeletionPolicy: sei.DeletionDelete, } @@ -63,7 +63,7 @@ func TestRenderNetwork_ChainIDDefaultsToName(t *testing.T) { if got := net.Spec.ConfigOverrides["app.pruning"]; got != "nothing" { t.Errorf("configOverrides = %v, want app.pruning=nothing", net.Spec.ConfigOverrides) } - if len(net.Spec.Genesis.Accounts) != 1 || net.Spec.Genesis.Accounts[0].Address != "sei1abc" { + if len(net.Spec.Genesis.Accounts) != 1 || net.Spec.Genesis.Accounts[0].Address != testGenesisAddr { t.Errorf("genesis accounts = %+v", net.Spec.Genesis.Accounts) } // DeletionPolicy threads through so an ephemeral chain cascades to its