From f820427f72184e6af1e51e309d3755c793b1e39b Mon Sep 17 00:00:00 2001 From: Denis Subbotin Date: Tue, 27 Jun 2023 12:23:10 +0300 Subject: [PATCH] few minors --- api/example.http | 3 + api/http-client.env.json | 2 +- api/openapi.json | 66 +++++++- api/openapi.yml | 44 +++++- go.mod | 2 +- go.sum | 4 + pkg/api/account_handlers.go | 3 + pkg/api/staking_converters.go | 19 +-- pkg/api/staking_handlers.go | 5 + pkg/core/staking.go | 1 + pkg/litestorage/stacking.go | 9 +- pkg/oas/oas_client_gen.go | 77 ++++++++++ pkg/oas/oas_handlers_gen.go | 99 ++++++++++++ pkg/oas/oas_interfaces_gen.go | 4 + pkg/oas/oas_json_gen.go | 222 +++++++++++++++++++++++++++ pkg/oas/oas_parameters_gen.go | 54 ++++++- pkg/oas/oas_response_decoders_gen.go | 156 +++++++++++++++++++ pkg/oas/oas_response_encoders_gen.go | 67 ++++++++ pkg/oas/oas_router_gen.go | 66 +++++++- pkg/oas/oas_schemas_gen.go | 46 ++++++ pkg/oas/oas_server_gen.go | 6 + pkg/oas/oas_unimplemented_gen.go | 9 ++ pkg/oas/oas_validators_gen.go | 53 +++++++ 23 files changed, 993 insertions(+), 24 deletions(-) diff --git a/api/example.http b/api/example.http index 3990f5d7..56cdee37 100644 --- a/api/example.http +++ b/api/example.http @@ -60,6 +60,9 @@ GET {{host}}/v2/nfts/collections/EQDaaxtmY6Dk0YzIV0zNnbUpbjZ92TJHBvO72esc0srwv8K ### GET {{host}}/v2/dns/auctions +### +GET {{host}}/v2/dns/abdul.ton/bids + ### GET {{host}}/v2/accounts/EQDD3n4osteDDhhSerivXKBzi-IJbIhCTkxRYSlhHEh_hyAW/publickey diff --git a/api/http-client.env.json b/api/http-client.env.json index b9a5e5e1..13d0a129 100644 --- a/api/http-client.env.json +++ b/api/http-client.env.json @@ -11,4 +11,4 @@ "host": "https://tonapi.io", "metrics": "http://localhost:9010" } -} \ No newline at end of file +} diff --git a/api/openapi.json b/api/openapi.json index 867ebf68..f29dcf4a 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -131,7 +131,7 @@ "required": false, "schema": { "default": 30, - "maximum": 360, + "maximum": 366, "minimum": 1, "type": "integer" } @@ -832,6 +832,21 @@ ], "type": "object" }, + "ApyHistory": { + "properties": { + "apy": { + "type": "number" + }, + "time": { + "type": "integer" + } + }, + "required": [ + "apy", + "time" + ], + "type": "object" + }, "Auction": { "properties": { "bids": { @@ -4687,6 +4702,55 @@ ] } }, + "/v2/staking/pool/{account_id}/history": { + "get": { + "description": "Pool info", + "operationId": "stakingPoolHistory", + "parameters": [ + { + "$ref": "#/components/parameters/accountIdParameter" + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "apy": { + "items": { + "$ref": "#/components/schemas/ApyHistory" + }, + "type": "array" + } + }, + "required": [ + "apy" + ], + "type": "object" + } + } + }, + "description": "history" + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/UnauthorizedError" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "500": { + "$ref": "#/components/responses/InternalError" + } + }, + "tags": [ + "Staking" + ] + } + }, "/v2/staking/pools": { "get": { "description": "All pools available in network", diff --git a/api/openapi.yml b/api/openapi.yml index 1101aa04..75e96728 100644 --- a/api/openapi.yml +++ b/api/openapi.yml @@ -1149,6 +1149,37 @@ paths: $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalError' + /v2/staking/pool/{account_id}/history: + get: + description: Pool info + operationId: stakingPoolHistory + tags: + - Staking + parameters: + - $ref: '#/components/parameters/accountIdParameter' + responses: + '200': + description: "history" + content: + application/json: + schema: + type: object + required: + - apy + properties: + apy: + type: array + items: + $ref: '#/components/schemas/ApyHistory' + + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/UnauthorizedError' + '404': + $ref: '#/components/responses/NotFound' + '500': + $ref: '#/components/responses/InternalError' /v2/staking/pools: get: description: All pools available in network @@ -1510,7 +1541,7 @@ components: type: integer default: 30 minimum: 1 - maximum: 360 + maximum: 366 collectionQuery: in: query name: collection @@ -2979,6 +3010,17 @@ components: items: $ref: '#/components/schemas/TraceId' + ApyHistory: + type: object + required: + - apy + - time + properties: + apy: + type: number + time: + type: integer + Subscription: type: object required: diff --git a/go.mod b/go.mod index ba711539..0f3b6996 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/sourcegraph/conc v0.3.0 github.com/stretchr/testify v1.8.1 github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230413094040-a66ab71fb269 - github.com/tonkeeper/tongo v1.1.2-0.20230616094523-df5e7618cf8d + github.com/tonkeeper/tongo v1.1.2-0.20230626203548-fcb095498b53 go.opentelemetry.io/otel v1.11.1 go.opentelemetry.io/otel/metric v0.33.0 go.opentelemetry.io/otel/trace v1.11.1 diff --git a/go.sum b/go.sum index 8320e20e..c460ce35 100644 --- a/go.sum +++ b/go.sum @@ -270,6 +270,10 @@ github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230413094040-a66ab71fb269 h1 github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230413094040-a66ab71fb269/go.mod h1:VGp8QednbWkKHcpQVlWyO0XSqAA0cR6d9wEdrDmHbbA= github.com/tonkeeper/tongo v1.1.2-0.20230616094523-df5e7618cf8d h1:I8TNw0yH3Pn1bC1hNb37ZfHvQi47I/jHHLUyYax0qxg= github.com/tonkeeper/tongo v1.1.2-0.20230616094523-df5e7618cf8d/go.mod h1:LdOBjpUz6vLp1EdX3E0XLNks9YI5XMSqaQahfOMrBEY= +github.com/tonkeeper/tongo v1.1.2-0.20230621154152-2691afbb960d h1:ipxDEMcJ4Nn4RkyZlFXg9BtqRanZbeJuNjxhqoRtvUo= +github.com/tonkeeper/tongo v1.1.2-0.20230621154152-2691afbb960d/go.mod h1:LdOBjpUz6vLp1EdX3E0XLNks9YI5XMSqaQahfOMrBEY= +github.com/tonkeeper/tongo v1.1.2-0.20230626203548-fcb095498b53 h1:TzXHChKTlAsqfXr8Vh3lTFYHLfIPBCXbUBbIkGGeUIs= +github.com/tonkeeper/tongo v1.1.2-0.20230626203548-fcb095498b53/go.mod h1:LdOBjpUz6vLp1EdX3E0XLNks9YI5XMSqaQahfOMrBEY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= diff --git a/pkg/api/account_handlers.go b/pkg/api/account_handlers.go index 0f70c6bc..0dcb918d 100644 --- a/pkg/api/account_handlers.go +++ b/pkg/api/account_handlers.go @@ -200,6 +200,9 @@ func (h Handler) GetDnsExpiring(ctx context.Context, params oas.GetDnsExpiringPa } accounts := make([]tongo.AccountID, 0, len(dnsExpiring)) var response oas.DnsExpiring + if len(dnsExpiring) == 0 { + return &response, nil + } for _, dns := range dnsExpiring { if dns.DnsItem != nil { accounts = append(accounts, dns.DnsItem.Address) diff --git a/pkg/api/staking_converters.go b/pkg/api/staking_converters.go index db8b047f..3dd53b9f 100644 --- a/pkg/api/staking_converters.go +++ b/pkg/api/staking_converters.go @@ -48,14 +48,15 @@ func convertStakingTFPool(p core.TFPool, info addressbook.TFPoolInfo, apy float6 func convertLiquidStaking(p core.LiquidPool, apy float64) oas.PoolInfo { name := p.Address.ToHuman(true, false) return oas.PoolInfo{ - Address: p.Address.ToRaw(), - Name: name, - TotalAmount: p.TotalAmount, - Implementation: oas.PoolInfoImplementationLiquidTF, - Apy: 330, - MinStake: 10, - Verified: p.VerifiedSources, - CurrentNominators: 1, - MaxNominators: 0, + Address: p.Address.ToRaw(), + Name: name, + TotalAmount: p.TotalAmount, + Implementation: oas.PoolInfoImplementationLiquidTF, + Apy: 330, + MinStake: 10, + Verified: p.VerifiedSources, + CurrentNominators: 1, + MaxNominators: 0, + LiquidJettonMaster: oas.NewOptString(p.JettonMaster.ToRaw()), } } diff --git a/pkg/api/staking_handlers.go b/pkg/api/staking_handlers.go index 0ed3e5ac..ef4f2f89 100644 --- a/pkg/api/staking_handlers.go +++ b/pkg/api/staking_handlers.go @@ -3,6 +3,7 @@ package api import ( "context" "errors" + "fmt" "github.com/tonkeeper/tongo" "golang.org/x/exp/slices" @@ -194,3 +195,7 @@ func (h Handler) PoolsByNominators(ctx context.Context, params oas.PoolsByNomina } return &result, nil } + +func (h Handler) StakingPoolHistory(ctx context.Context, params oas.StakingPoolHistoryParams) (oas.StakingPoolHistoryRes, error) { + return nil, fmt.Errorf("not implemented") +} diff --git a/pkg/core/staking.go b/pkg/core/staking.go index dfaa0c57..04010837 100644 --- a/pkg/core/staking.go +++ b/pkg/core/staking.go @@ -28,4 +28,5 @@ type LiquidPool struct { Address tongo.AccountID TotalAmount int64 VerifiedSources bool + JettonMaster tongo.AccountID } diff --git a/pkg/litestorage/stacking.go b/pkg/litestorage/stacking.go index b016250d..830100dc 100644 --- a/pkg/litestorage/stacking.go +++ b/pkg/litestorage/stacking.go @@ -165,11 +165,11 @@ func (s *LiteStorage) GetTFPools(ctx context.Context, onlyVerified bool) ([]core return result, nil } func (s *LiteStorage) GetLiquidPool(ctx context.Context, pool tongo.AccountID) (core.LiquidPool, error) { - _, v, err := abi.GetFinanceData(ctx, s.client, pool) + _, v, err := abi.GetPoolFullData(ctx, s.client, pool) if err != nil { return core.LiquidPool{}, err } - p, ok := v.(abi.GetFinanceData_PoolResult) + p, ok := v.(abi.GetPoolFullDataResult) if !ok { return core.LiquidPool{}, fmt.Errorf("invalid type") } @@ -179,10 +179,15 @@ func (s *LiteStorage) GetLiquidPool(ctx context.Context, pool tongo.AccountID) ( } code := state.Account.Account.Storage.State.AccountActive.StateInit.Code.Value.Value hash, err := code.Hash() + jettonMaster, err := tongo.AccountIDFromTlb(p.JettonMinter) + if err != nil || jettonMaster == nil { + return core.LiquidPool{}, fmt.Errorf("invalid pool jetton %v", jettonMaster) + } return core.LiquidPool{ Address: pool, TotalAmount: p.TotalBalance, VerifiedSources: bytes.Equal(hash, references.TFLiquidPoolCodeHash[:]), + JettonMaster: *jettonMaster, }, err } diff --git a/pkg/oas/oas_client_gen.go b/pkg/oas/oas_client_gen.go index 064e3bcf..f670379b 100644 --- a/pkg/oas/oas_client_gen.go +++ b/pkg/oas/oas_client_gen.go @@ -4192,6 +4192,83 @@ func (c *Client) SetWalletBackup(ctx context.Context, request SetWalletBackupReq return result, nil } +// StakingPoolHistory invokes stakingPoolHistory operation. +// +// Pool info. +// +// GET /v2/staking/pool/{account_id}/history +func (c *Client) StakingPoolHistory(ctx context.Context, params StakingPoolHistoryParams) (res StakingPoolHistoryRes, err error) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("stakingPoolHistory"), + } + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + c.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...) + }() + + // Increment request counter. + c.requests.Add(ctx, 1, otelAttrs...) + + // Start a span for this request. + ctx, span := c.cfg.Tracer.Start(ctx, "StakingPoolHistory", + trace.WithAttributes(otelAttrs...), + clientSpanKind, + ) + // Track stage for error reporting. + var stage string + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + c.errors.Add(ctx, 1, otelAttrs...) + } + span.End() + }() + + stage = "BuildURL" + u := uri.Clone(c.requestURL(ctx)) + u.Path += "/v2/staking/pool/" + { + // Encode "account_id" parameter. + e := uri.NewPathEncoder(uri.PathEncoderConfig{ + Param: "account_id", + Style: uri.PathStyleSimple, + Explode: false, + }) + if err := func() error { + return e.EncodeValue(conv.StringToString(params.AccountID)) + }(); err != nil { + return res, errors.Wrap(err, "encode path") + } + u.Path += e.Result() + } + u.Path += "/history" + + stage = "EncodeRequest" + r, err := ht.NewRequest(ctx, "GET", u, nil) + if err != nil { + return res, errors.Wrap(err, "create request") + } + + stage = "SendRequest" + resp, err := c.cfg.Client.Do(r) + if err != nil { + return res, errors.Wrap(err, "do request") + } + defer resp.Body.Close() + + stage = "DecodeResponse" + result, err := decodeStakingPoolHistoryResponse(resp) + if err != nil { + return res, errors.Wrap(err, "decode response") + } + + return result, nil +} + // StakingPoolInfo invokes stakingPoolInfo operation. // // Pool info. diff --git a/pkg/oas/oas_handlers_gen.go b/pkg/oas/oas_handlers_gen.go index f7433e97..63d2da05 100644 --- a/pkg/oas/oas_handlers_gen.go +++ b/pkg/oas/oas_handlers_gen.go @@ -4775,6 +4775,105 @@ func (s *Server) handleSetWalletBackupRequest(args [0]string, w http.ResponseWri } } +// handleStakingPoolHistoryRequest handles stakingPoolHistory operation. +// +// Pool info. +// +// GET /v2/staking/pool/{account_id}/history +func (s *Server) handleStakingPoolHistoryRequest(args [1]string, w http.ResponseWriter, r *http.Request) { + otelAttrs := []attribute.KeyValue{ + otelogen.OperationID("stakingPoolHistory"), + } + + // Start a span for this request. + ctx, span := s.cfg.Tracer.Start(r.Context(), "StakingPoolHistory", + trace.WithAttributes(otelAttrs...), + serverSpanKind, + ) + defer span.End() + + // Run stopwatch. + startTime := time.Now() + defer func() { + elapsedDuration := time.Since(startTime) + s.duration.Record(ctx, elapsedDuration.Microseconds(), otelAttrs...) + }() + + // Increment request counter. + s.requests.Add(ctx, 1, otelAttrs...) + + var ( + recordError = func(stage string, err error) { + span.RecordError(err) + span.SetStatus(codes.Error, stage) + s.errors.Add(ctx, 1, otelAttrs...) + } + err error + opErrContext = ogenerrors.OperationContext{ + Name: "StakingPoolHistory", + ID: "stakingPoolHistory", + } + ) + params, err := decodeStakingPoolHistoryParams(args, r) + if err != nil { + err = &ogenerrors.DecodeParamsError{ + OperationContext: opErrContext, + Err: err, + } + recordError("DecodeParams", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + var response StakingPoolHistoryRes + if m := s.cfg.Middleware; m != nil { + mreq := middleware.Request{ + Context: ctx, + OperationName: "StakingPoolHistory", + OperationID: "stakingPoolHistory", + Body: nil, + Params: middleware.Parameters{ + { + Name: "account_id", + In: "path", + }: params.AccountID, + }, + Raw: r, + } + + type ( + Request = struct{} + Params = StakingPoolHistoryParams + Response = StakingPoolHistoryRes + ) + response, err = middleware.HookMiddleware[ + Request, + Params, + Response, + ]( + m, + mreq, + unpackStakingPoolHistoryParams, + func(ctx context.Context, request Request, params Params) (Response, error) { + return s.h.StakingPoolHistory(ctx, params) + }, + ) + } else { + response, err = s.h.StakingPoolHistory(ctx, params) + } + if err != nil { + recordError("Internal", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } + + if err := encodeStakingPoolHistoryResponse(response, w, span); err != nil { + recordError("EncodeResponse", err) + s.cfg.ErrorHandler(ctx, w, r, err) + return + } +} + // handleStakingPoolInfoRequest handles stakingPoolInfo operation. // // Pool info. diff --git a/pkg/oas/oas_interfaces_gen.go b/pkg/oas/oas_interfaces_gen.go index 1f188d3b..e7b7e256 100644 --- a/pkg/oas/oas_interfaces_gen.go +++ b/pkg/oas/oas_interfaces_gen.go @@ -189,6 +189,10 @@ type SetWalletBackupRes interface { setWalletBackupRes() } +type StakingPoolHistoryRes interface { + stakingPoolHistoryRes() +} + type StakingPoolInfoRes interface { stakingPoolInfoRes() } diff --git a/pkg/oas/oas_json_gen.go b/pkg/oas/oas_json_gen.go index 3ecf8e74..4d1b78e5 100644 --- a/pkg/oas/oas_json_gen.go +++ b/pkg/oas/oas_json_gen.go @@ -2347,6 +2347,121 @@ func (s *ActionType) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode implements json.Marshaler. +func (s ApyHistory) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s ApyHistory) encodeFields(e *jx.Encoder) { + { + + e.FieldStart("apy") + e.Float64(s.Apy) + } + { + + e.FieldStart("time") + e.Int(s.Time) + } +} + +var jsonFieldsNameOfApyHistory = [2]string{ + 0: "apy", + 1: "time", +} + +// Decode decodes ApyHistory from json. +func (s *ApyHistory) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode ApyHistory to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "apy": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + v, err := d.Float64() + s.Apy = float64(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"apy\"") + } + case "time": + requiredBitSet[0] |= 1 << 1 + if err := func() error { + v, err := d.Int() + s.Time = int(v) + if err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"time\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode ApyHistory") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000011, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfApyHistory) { + name = jsonFieldsNameOfApyHistory[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s ApyHistory) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *ApyHistory) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode implements json.Marshaler. func (s Auction) Encode(e *jx.Encoder) { e.ObjStart() @@ -13438,6 +13553,113 @@ func (s *SmartContractAction) UnmarshalJSON(data []byte) error { return s.Decode(d) } +// Encode implements json.Marshaler. +func (s StakingPoolHistoryOK) Encode(e *jx.Encoder) { + e.ObjStart() + s.encodeFields(e) + e.ObjEnd() +} + +// encodeFields encodes fields. +func (s StakingPoolHistoryOK) encodeFields(e *jx.Encoder) { + { + + e.FieldStart("apy") + e.ArrStart() + for _, elem := range s.Apy { + elem.Encode(e) + } + e.ArrEnd() + } +} + +var jsonFieldsNameOfStakingPoolHistoryOK = [1]string{ + 0: "apy", +} + +// Decode decodes StakingPoolHistoryOK from json. +func (s *StakingPoolHistoryOK) Decode(d *jx.Decoder) error { + if s == nil { + return errors.New("invalid: unable to decode StakingPoolHistoryOK to nil") + } + var requiredBitSet [1]uint8 + + if err := d.ObjBytes(func(d *jx.Decoder, k []byte) error { + switch string(k) { + case "apy": + requiredBitSet[0] |= 1 << 0 + if err := func() error { + s.Apy = make([]ApyHistory, 0) + if err := d.Arr(func(d *jx.Decoder) error { + var elem ApyHistory + if err := elem.Decode(d); err != nil { + return err + } + s.Apy = append(s.Apy, elem) + return nil + }); err != nil { + return err + } + return nil + }(); err != nil { + return errors.Wrap(err, "decode field \"apy\"") + } + default: + return d.Skip() + } + return nil + }); err != nil { + return errors.Wrap(err, "decode StakingPoolHistoryOK") + } + // Validate required fields. + var failures []validate.FieldError + for i, mask := range [1]uint8{ + 0b00000001, + } { + if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { + // Mask only required fields and check equality to mask using XOR. + // + // If XOR result is not zero, result is not equal to expected, so some fields are missed. + // Bits of fields which would be set are actually bits of missed fields. + missed := bits.OnesCount8(result) + for bitN := 0; bitN < missed; bitN++ { + bitIdx := bits.TrailingZeros8(result) + fieldIdx := i*8 + bitIdx + var name string + if fieldIdx < len(jsonFieldsNameOfStakingPoolHistoryOK) { + name = jsonFieldsNameOfStakingPoolHistoryOK[fieldIdx] + } else { + name = strconv.Itoa(fieldIdx) + } + failures = append(failures, validate.FieldError{ + Name: name, + Error: validate.ErrFieldRequired, + }) + // Reset bit. + result &^= 1 << bitIdx + } + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + + return nil +} + +// MarshalJSON implements stdjson.Marshaler. +func (s StakingPoolHistoryOK) MarshalJSON() ([]byte, error) { + e := jx.Encoder{} + s.Encode(&e) + return e.Bytes(), nil +} + +// UnmarshalJSON implements stdjson.Unmarshaler. +func (s *StakingPoolHistoryOK) UnmarshalJSON(data []byte) error { + d := jx.DecodeBytes(data) + return s.Decode(d) +} + // Encode implements json.Marshaler. func (s StakingPoolInfoOK) Encode(e *jx.Encoder) { e.ObjStart() diff --git a/pkg/oas/oas_parameters_gen.go b/pkg/oas/oas_parameters_gen.go index 155bb02f..5ada47db 100644 --- a/pkg/oas/oas_parameters_gen.go +++ b/pkg/oas/oas_parameters_gen.go @@ -1063,7 +1063,7 @@ func decodeGetDnsExpiringParams(args [1]string, r *http.Request) (params GetDnsE MinSet: true, Min: 1, MaxSet: true, - Max: 360, + Max: 366, MinExclusive: false, MaxExclusive: false, MultipleOfSet: false, @@ -3671,6 +3671,58 @@ func decodeSetWalletBackupParams(args [0]string, r *http.Request) (params SetWal return params, nil } +// StakingPoolHistoryParams is parameters of stakingPoolHistory operation. +type StakingPoolHistoryParams struct { + // Account ID. + AccountID string +} + +func unpackStakingPoolHistoryParams(packed middleware.Parameters) (params StakingPoolHistoryParams) { + { + key := middleware.ParameterKey{ + Name: "account_id", + In: "path", + } + params.AccountID = packed[key].(string) + } + return params +} + +func decodeStakingPoolHistoryParams(args [1]string, r *http.Request) (params StakingPoolHistoryParams, _ error) { + // Decode path: account_id. + { + param := args[0] + if len(param) > 0 { + d := uri.NewPathDecoder(uri.PathDecoderConfig{ + Param: "account_id", + Value: param, + Style: uri.PathStyleSimple, + Explode: false, + }) + + if err := func() error { + val, err := d.DecodeValue() + if err != nil { + return err + } + + c, err := conv.ToString(val) + if err != nil { + return err + } + + params.AccountID = c + return nil + }(); err != nil { + return params, errors.Wrap(err, "path: account_id: parse") + } + } else { + return params, errors.New("path: account_id: not specified") + } + } + return params, nil +} + // StakingPoolInfoParams is parameters of stakingPoolInfo operation. type StakingPoolInfoParams struct { // Account ID. diff --git a/pkg/oas/oas_response_decoders_gen.go b/pkg/oas/oas_response_decoders_gen.go index efa00526..68879a61 100644 --- a/pkg/oas/oas_response_decoders_gen.go +++ b/pkg/oas/oas_response_decoders_gen.go @@ -6514,6 +6514,162 @@ func decodeSetWalletBackupResponse(resp *http.Response) (res SetWalletBackupRes, return res, validate.UnexpectedStatusCode(resp.StatusCode) } +func decodeStakingPoolHistoryResponse(resp *http.Response) (res StakingPoolHistoryRes, err error) { + switch resp.StatusCode { + case 200: + // Code 200. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + b, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + + d := jx.DecodeBytes(b) + var response StakingPoolHistoryOK + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "decode \"application/json\"") + } + if err := d.Skip(); err != io.EOF { + return res, errors.New("unexpected trailing data") + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 400: + // Code 400. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + b, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + + d := jx.DecodeBytes(b) + var response BadRequest + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "decode \"application/json\"") + } + if err := d.Skip(); err != io.EOF { + return res, errors.New("unexpected trailing data") + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 401: + // Code 401. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + b, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + + d := jx.DecodeBytes(b) + var response UnauthorizedError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "decode \"application/json\"") + } + if err := d.Skip(); err != io.EOF { + return res, errors.New("unexpected trailing data") + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 404: + // Code 404. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + b, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + + d := jx.DecodeBytes(b) + var response NotFound + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "decode \"application/json\"") + } + if err := d.Skip(); err != io.EOF { + return res, errors.New("unexpected trailing data") + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + case 500: + // Code 500. + ct, _, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return res, errors.Wrap(err, "parse media type") + } + switch { + case ct == "application/json": + b, err := io.ReadAll(resp.Body) + if err != nil { + return res, err + } + + d := jx.DecodeBytes(b) + var response InternalError + if err := func() error { + if err := response.Decode(d); err != nil { + return err + } + return nil + }(); err != nil { + return res, errors.Wrap(err, "decode \"application/json\"") + } + if err := d.Skip(); err != io.EOF { + return res, errors.New("unexpected trailing data") + } + return &response, nil + default: + return res, validate.InvalidContentType(ct) + } + } + return res, validate.UnexpectedStatusCode(resp.StatusCode) +} + func decodeStakingPoolInfoResponse(resp *http.Response) (res StakingPoolInfoRes, err error) { switch resp.StatusCode { case 200: diff --git a/pkg/oas/oas_response_encoders_gen.go b/pkg/oas/oas_response_encoders_gen.go index 53d204f4..2513535c 100644 --- a/pkg/oas/oas_response_encoders_gen.go +++ b/pkg/oas/oas_response_encoders_gen.go @@ -2842,6 +2842,73 @@ func encodeSetWalletBackupResponse(response SetWalletBackupRes, w http.ResponseW } } +func encodeStakingPoolHistoryResponse(response StakingPoolHistoryRes, w http.ResponseWriter, span trace.Span) error { + switch response := response.(type) { + case *StakingPoolHistoryOK: + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + span.SetStatus(codes.Ok, http.StatusText(200)) + + e := jx.GetEncoder() + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + return nil + + case *BadRequest: + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + span.SetStatus(codes.Error, http.StatusText(400)) + + e := jx.GetEncoder() + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + return nil + + case *UnauthorizedError: + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + span.SetStatus(codes.Error, http.StatusText(401)) + + e := jx.GetEncoder() + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + return nil + + case *NotFound: + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + span.SetStatus(codes.Error, http.StatusText(404)) + + e := jx.GetEncoder() + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + return nil + + case *InternalError: + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + span.SetStatus(codes.Error, http.StatusText(500)) + + e := jx.GetEncoder() + response.Encode(e) + if _, err := e.WriteTo(w); err != nil { + return errors.Wrap(err, "write") + } + return nil + + default: + return errors.Errorf("unexpected response type: %T", response) + } +} + func encodeStakingPoolInfoResponse(response StakingPoolInfoRes, w http.ResponseWriter, span trace.Span) error { switch response := response.(type) { case *StakingPoolInfoOK: diff --git a/pkg/oas/oas_router_gen.go b/pkg/oas/oas_router_gen.go index cc096243..df04db70 100644 --- a/pkg/oas/oas_router_gen.go +++ b/pkg/oas/oas_router_gen.go @@ -1063,12 +1063,15 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { } // Param: "account_id" - // Leaf parameter - args[0] = elem - elem = "" + // Match until "/" + idx := strings.IndexByte(elem, '/') + if idx < 0 { + idx = len(elem) + } + args[0] = elem[:idx] + elem = elem[idx:] if len(elem) == 0 { - // Leaf node. switch r.Method { case "GET": s.handleStakingPoolInfoRequest([1]string{ @@ -1080,6 +1083,28 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } + switch elem[0] { + case '/': // Prefix: "/history" + if l := len("/history"); len(elem) >= l && elem[0:l] == "/history" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + // Leaf node. + switch r.Method { + case "GET": + s.handleStakingPoolHistoryRequest([1]string{ + args[0], + }, w, r) + default: + s.notAllowed(w, r, "GET") + } + + return + } + } case 's': // Prefix: "s" if l := len("s"); len(elem) >= l && elem[0:l] == "s" { elem = elem[l:] @@ -2393,14 +2418,17 @@ func (s *Server) FindRoute(method, path string) (r Route, _ bool) { } // Param: "account_id" - // Leaf parameter - args[0] = elem - elem = "" + // Match until "/" + idx := strings.IndexByte(elem, '/') + if idx < 0 { + idx = len(elem) + } + args[0] = elem[:idx] + elem = elem[idx:] if len(elem) == 0 { switch method { case "GET": - // Leaf: StakingPoolInfo r.name = "StakingPoolInfo" r.operationID = "stakingPoolInfo" r.args = args @@ -2410,6 +2438,28 @@ func (s *Server) FindRoute(method, path string) (r Route, _ bool) { return } } + switch elem[0] { + case '/': // Prefix: "/history" + if l := len("/history"); len(elem) >= l && elem[0:l] == "/history" { + elem = elem[l:] + } else { + break + } + + if len(elem) == 0 { + switch method { + case "GET": + // Leaf: StakingPoolHistory + r.name = "StakingPoolHistory" + r.operationID = "stakingPoolHistory" + r.args = args + r.count = 1 + return r, true + default: + return + } + } + } case 's': // Prefix: "s" if l := len("s"); len(elem) >= l && elem[0:l] == "s" { elem = elem[l:] diff --git a/pkg/oas/oas_schemas_gen.go b/pkg/oas/oas_schemas_gen.go index 55a8b78e..943c4151 100644 --- a/pkg/oas/oas_schemas_gen.go +++ b/pkg/oas/oas_schemas_gen.go @@ -833,6 +833,32 @@ const ( ActionTypeUnknown ActionType = "Unknown" ) +// Ref: #/components/schemas/ApyHistory +type ApyHistory struct { + Apy float64 `json:"apy"` + Time int `json:"time"` +} + +// GetApy returns the value of Apy. +func (s ApyHistory) GetApy() float64 { + return s.Apy +} + +// GetTime returns the value of Time. +func (s ApyHistory) GetTime() int { + return s.Time +} + +// SetApy sets the value of Apy. +func (s *ApyHistory) SetApy(val float64) { + s.Apy = val +} + +// SetTime sets the value of Time. +func (s *ApyHistory) SetTime(val int) { + s.Time = val +} + // Ref: #/components/schemas/Auction type Auction struct { Domain string `json:"domain"` @@ -1044,6 +1070,7 @@ func (*BadRequest) poolsByNominatorsRes() {} func (*BadRequest) reindexAccountRes() {} func (*BadRequest) sendMessageRes() {} func (*BadRequest) setWalletBackupRes() {} +func (*BadRequest) stakingPoolHistoryRes() {} func (*BadRequest) stakingPoolInfoRes() {} func (*BadRequest) stakingPoolsRes() {} func (*BadRequest) tonConnectProofRes() {} @@ -2270,6 +2297,7 @@ func (*InternalError) poolsByNominatorsRes() {} func (*InternalError) reindexAccountRes() {} func (*InternalError) sendMessageRes() {} func (*InternalError) setWalletBackupRes() {} +func (*InternalError) stakingPoolHistoryRes() {} func (*InternalError) stakingPoolInfoRes() {} func (*InternalError) stakingPoolsRes() {} func (*InternalError) tonConnectProofRes() {} @@ -3407,6 +3435,7 @@ func (*NotFound) getTraceRes() {} func (*NotFound) getTracesByAccountRes() {} func (*NotFound) getTransactionRes() {} func (*NotFound) poolsByNominatorsRes() {} +func (*NotFound) stakingPoolHistoryRes() {} func (*NotFound) stakingPoolInfoRes() {} func (*NotFound) stakingPoolsRes() {} @@ -5609,6 +5638,22 @@ func (s *SmartContractAction) SetRefund(val OptRefund) { s.Refund = val } +type StakingPoolHistoryOK struct { + Apy []ApyHistory `json:"apy"` +} + +// GetApy returns the value of Apy. +func (s StakingPoolHistoryOK) GetApy() []ApyHistory { + return s.Apy +} + +// SetApy sets the value of Apy. +func (s *StakingPoolHistoryOK) SetApy(val []ApyHistory) { + s.Apy = val +} + +func (*StakingPoolHistoryOK) stakingPoolHistoryRes() {} + type StakingPoolInfoOK struct { Implementation PoolImplementation `json:"implementation"` Pool PoolInfo `json:"pool"` @@ -6721,6 +6766,7 @@ func (*UnauthorizedError) poolsByNominatorsRes() {} func (*UnauthorizedError) reindexAccountRes() {} func (*UnauthorizedError) sendMessageRes() {} func (*UnauthorizedError) setWalletBackupRes() {} +func (*UnauthorizedError) stakingPoolHistoryRes() {} func (*UnauthorizedError) stakingPoolInfoRes() {} func (*UnauthorizedError) stakingPoolsRes() {} func (*UnauthorizedError) tonConnectProofRes() {} diff --git a/pkg/oas/oas_server_gen.go b/pkg/oas/oas_server_gen.go index fb3d0822..64459dea 100644 --- a/pkg/oas/oas_server_gen.go +++ b/pkg/oas/oas_server_gen.go @@ -299,6 +299,12 @@ type Handler interface { // // PUT /v2/wallet/backup SetWalletBackup(ctx context.Context, req SetWalletBackupReq, params SetWalletBackupParams) (SetWalletBackupRes, error) + // StakingPoolHistory implements stakingPoolHistory operation. + // + // Pool info. + // + // GET /v2/staking/pool/{account_id}/history + StakingPoolHistory(ctx context.Context, params StakingPoolHistoryParams) (StakingPoolHistoryRes, error) // StakingPoolInfo implements stakingPoolInfo operation. // // Pool info. diff --git a/pkg/oas/oas_unimplemented_gen.go b/pkg/oas/oas_unimplemented_gen.go index 06e96957..1ed6f3ed 100644 --- a/pkg/oas/oas_unimplemented_gen.go +++ b/pkg/oas/oas_unimplemented_gen.go @@ -445,6 +445,15 @@ func (UnimplementedHandler) SetWalletBackup(ctx context.Context, req SetWalletBa return r, ht.ErrNotImplemented } +// StakingPoolHistory implements stakingPoolHistory operation. +// +// Pool info. +// +// GET /v2/staking/pool/{account_id}/history +func (UnimplementedHandler) StakingPoolHistory(ctx context.Context, params StakingPoolHistoryParams) (r StakingPoolHistoryRes, _ error) { + return r, ht.ErrNotImplemented +} + // StakingPoolInfo implements stakingPoolInfo operation. // // Pool info. diff --git a/pkg/oas/oas_validators_gen.go b/pkg/oas/oas_validators_gen.go index 9f1af670..062c0e2b 100644 --- a/pkg/oas/oas_validators_gen.go +++ b/pkg/oas/oas_validators_gen.go @@ -403,6 +403,24 @@ func (s ActionType) Validate() error { return errors.Errorf("invalid value: %v", s) } } +func (s ApyHistory) Validate() error { + var failures []validate.FieldError + if err := func() error { + if err := (validate.Float{}).Validate(float64(s.Apy)); err != nil { + return errors.Wrap(err, "float") + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "apy", + Error: err, + }) + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + return nil +} func (s AuctionBidAction) Validate() error { var failures []validate.FieldError if err := func() error { @@ -1396,6 +1414,41 @@ func (s SmartContractAction) Validate() error { } return nil } +func (s StakingPoolHistoryOK) Validate() error { + var failures []validate.FieldError + if err := func() error { + if s.Apy == nil { + return errors.New("nil is invalid value") + } + var failures []validate.FieldError + for i, elem := range s.Apy { + if err := func() error { + if err := elem.Validate(); err != nil { + return err + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: fmt.Sprintf("[%d]", i), + Error: err, + }) + } + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + return nil + }(); err != nil { + failures = append(failures, validate.FieldError{ + Name: "apy", + Error: err, + }) + } + if len(failures) > 0 { + return &validate.Error{Fields: failures} + } + return nil +} func (s StakingPoolInfoOK) Validate() error { var failures []validate.FieldError if err := func() error {