diff --git a/backend/pkg/api/data_access/general.go b/backend/pkg/api/data_access/general.go new file mode 100644 index 000000000..7debc5dfc --- /dev/null +++ b/backend/pkg/api/data_access/general.go @@ -0,0 +1,48 @@ +package dataaccess + +import ( + "context" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/gobitfly/beaconchain/pkg/api/types" + "github.com/gobitfly/beaconchain/pkg/commons/db" +) + +// retrieve (primary) ens name and optional name (=label) maintained by beaconcha.in, if present +func (d *DataAccessService) GetNamesAndEnsForAddresses(ctx context.Context, addressMap map[string]*types.Address) error { + addresses := make([][]byte, 0, len(addressMap)) + ensMapping := make(map[string]string, len(addressMap)) + for address, data := range addressMap { + ensMapping[address] = "" + add, err := hexutil.Decode(address) + if err != nil { + return err + } + addresses = append(addresses, add) + if data == nil { + addressMap[address] = &types.Address{Hash: types.Hash(address)} + } + } + // determine ENS names + if err := db.GetEnsNamesForAddresses(ensMapping); err != nil { + return err + } + for address, ens := range ensMapping { + addressMap[address].Ens = ens + } + + // determine names + names := []struct { + Address []byte `db:"address"` + Name string `db:"name"` + }{} + err := d.alloyReader.SelectContext(ctx, &names, `SELECT address, name FROM address_names WHERE address = ANY($1)`, addresses) + if err != nil { + return err + } + + for _, name := range names { + addressMap[hexutil.Encode(name.Address)].Label = name.Name + } + return nil +} diff --git a/backend/pkg/api/data_access/vdb_blocks.go b/backend/pkg/api/data_access/vdb_blocks.go index 3780599b7..2a2a0953a 100644 --- a/backend/pkg/api/data_access/vdb_blocks.go +++ b/backend/pkg/api/data_access/vdb_blocks.go @@ -15,6 +15,7 @@ import ( "github.com/gobitfly/beaconchain/pkg/commons/cache" "github.com/gobitfly/beaconchain/pkg/commons/db" "github.com/gobitfly/beaconchain/pkg/commons/log" + "github.com/gobitfly/beaconchain/pkg/commons/types" "github.com/gobitfly/beaconchain/pkg/commons/utils" "github.com/shopspring/decimal" ) @@ -345,7 +346,8 @@ func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, das } data := make([]t.VDBBlocksTableRow, len(proposals)) - ensMapping := make(map[string]string) + addressMapping := make(map[string]*t.Address) + contractStatusRequests := make([]db.ContractInteractionAtRequest, 0, len(proposals)) for i, proposal := range proposals { data[i].GroupId = proposal.Group if dashboardId.AggregateGroups { @@ -382,7 +384,13 @@ func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, das Hash: t.Hash(hexutil.Encode(proposal.FeeRecipient)), } data[i].RewardRecipient = &rewardRecp - ensMapping[hexutil.Encode(proposal.FeeRecipient)] = "" + addressMapping[hexutil.Encode(proposal.FeeRecipient)] = nil + contractStatusRequests = append(contractStatusRequests, db.ContractInteractionAtRequest{ + Address: fmt.Sprintf("%x", proposal.FeeRecipient), + Block: proposal.Block.Int64, + TxIdx: -1, + TraceIdx: -1, + }) reward.El = proposal.ElReward.Decimal.Mul(decimal.NewFromInt(1e18)) } if proposal.ClReward.Valid { @@ -393,13 +401,22 @@ func (d *DataAccessService) GetValidatorDashboardBlocks(ctx context.Context, das } // determine reward recipient ENS names startTime = time.Now() - if err := db.GetEnsNamesForAddresses(ensMapping); err != nil { + // determine ens/names + if err := d.GetNamesAndEnsForAddresses(ctx, addressMapping); err != nil { return nil, nil, err } - log.Debugf("=== getting ens names took %s", time.Since(startTime)) + log.Debugf("=== getting ens + labels names took %s", time.Since(startTime)) + // determine contract statuses + contractStatuses, err := d.bigtable.GetAddressContractInteractionsAt(contractStatusRequests) + if err != nil { + return nil, nil, err + } + var contractIdx int for i := range data { if data[i].RewardRecipient != nil { - data[i].RewardRecipient.Ens = ensMapping[string(data[i].RewardRecipient.Hash)] + data[i].RewardRecipient = addressMapping[string(data[i].RewardRecipient.Hash)] + data[i].RewardRecipient.IsContract = contractStatuses[contractIdx] == types.CONTRACT_CREATION || contractStatuses[contractIdx] == types.CONTRACT_PRESENT + contractIdx += 1 } } if !moreDataFlag && !currentCursor.IsValid() { diff --git a/backend/pkg/api/data_access/vdb_deposits.go b/backend/pkg/api/data_access/vdb_deposits.go index 7d32cc002..8c1d49003 100644 --- a/backend/pkg/api/data_access/vdb_deposits.go +++ b/backend/pkg/api/data_access/vdb_deposits.go @@ -14,6 +14,7 @@ import ( "github.com/gobitfly/beaconchain/pkg/api/enums" t "github.com/gobitfly/beaconchain/pkg/api/types" "github.com/gobitfly/beaconchain/pkg/commons/db" + "github.com/gobitfly/beaconchain/pkg/commons/types" "github.com/gobitfly/beaconchain/pkg/commons/utils" "github.com/lib/pq" "github.com/shopspring/decimal" @@ -121,17 +122,27 @@ func (d *DataAccessService) GetValidatorDashboardElDeposits(ctx context.Context, } responseData := make([]t.VDBExecutionDepositsTableRow, len(data)) + addressMapping := make(map[string]*t.Address) + fromContractStatusRequests := make([]db.ContractInteractionAtRequest, len(data)) + depositorContractStatusRequests := make([]db.ContractInteractionAtRequest, 0, len(data)) for i, row := range data { responseData[i] = t.VDBExecutionDepositsTableRow{ PublicKey: t.PubKey(pubkeys[i]), Block: uint64(row.BlockNumber), Timestamp: row.Timestamp.Unix(), - From: t.Address{Hash: t.Hash(hexutil.Encode(row.From))}, TxHash: t.Hash(hexutil.Encode(row.TxHash)), WithdrawalCredential: t.Hash(hexutil.Encode(row.WithdrawalCredentials)), Amount: utils.GWeiToWei(big.NewInt(row.Amount)), Valid: row.Valid, } + addressMapping[hexutil.Encode(row.From)] = nil + fromContractStatusRequests[i] = db.ContractInteractionAtRequest{ + Address: fmt.Sprintf("%x", row.From), + Block: row.BlockNumber, + // TODO not entirely correct, would need to determine tx index and itx index of tx. But good enough for now + TxIdx: -1, + TraceIdx: -1, + } if row.GroupId.Valid { if dashboardId.AggregateGroups { responseData[i].GroupId = t.DefaultGroupId @@ -143,6 +154,10 @@ func (d *DataAccessService) GetValidatorDashboardElDeposits(ctx context.Context, } if len(row.Depositor) > 0 { responseData[i].Depositor = t.Address{Hash: t.Hash(hexutil.Encode(row.Depositor))} + addressMapping[hexutil.Encode(row.Depositor)] = nil + depositorReq := fromContractStatusRequests[i] + depositorReq.Address = fmt.Sprintf("%x", row.Depositor) + depositorContractStatusRequests = append(depositorContractStatusRequests, depositorReq) } else { responseData[i].Depositor = responseData[i].From } @@ -150,6 +165,30 @@ func (d *DataAccessService) GetValidatorDashboardElDeposits(ctx context.Context, responseData[i].Index = &v } } + + // populate address data + if err := d.GetNamesAndEnsForAddresses(ctx, addressMapping); err != nil { + return nil, nil, err + } + fromContractStatuses, err := d.bigtable.GetAddressContractInteractionsAt(fromContractStatusRequests) + if err != nil { + return nil, nil, err + } + depositorContractStatuses, err := d.bigtable.GetAddressContractInteractionsAt(depositorContractStatusRequests) + if err != nil { + return nil, nil, err + } + var depositorIdx int + for i := range data { + responseData[i].From = *addressMapping[string(responseData[i].From.Hash)] + responseData[i].From.IsContract = fromContractStatuses[i] == types.CONTRACT_CREATION || fromContractStatuses[i] == types.CONTRACT_PRESENT + responseData[i].Depositor.IsContract = responseData[i].From.IsContract + if responseData[i].Depositor.Hash != responseData[i].From.Hash { + responseData[i].Depositor.IsContract = depositorContractStatuses[depositorIdx] == types.CONTRACT_CREATION || depositorContractStatuses[depositorIdx] == types.CONTRACT_PRESENT + depositorIdx += 1 + } + } + var paging t.Paging moreDataFlag := len(responseData) > int(limit) diff --git a/backend/pkg/api/data_access/vdb_withdrawals.go b/backend/pkg/api/data_access/vdb_withdrawals.go index c3837f6f0..21a190f44 100644 --- a/backend/pkg/api/data_access/vdb_withdrawals.go +++ b/backend/pkg/api/data_access/vdb_withdrawals.go @@ -17,6 +17,7 @@ import ( t "github.com/gobitfly/beaconchain/pkg/api/types" "github.com/gobitfly/beaconchain/pkg/commons/cache" "github.com/gobitfly/beaconchain/pkg/commons/db" + "github.com/gobitfly/beaconchain/pkg/commons/types" "github.com/gobitfly/beaconchain/pkg/commons/utils" "github.com/lib/pq" "github.com/pkg/errors" @@ -111,6 +112,7 @@ func (d *DataAccessService) GetValidatorDashboardWithdrawals(ctx context.Context // Get the withdrawals for the validators queryResult := []struct { BlockSlot uint64 `db:"block_slot"` + BlockNumber uint64 `db:"exec_block_number"` WithdrawalIndex uint64 `db:"withdrawalindex"` ValidatorIndex uint64 `db:"validatorindex"` Address []byte `db:"address"` @@ -121,6 +123,7 @@ func (d *DataAccessService) GetValidatorDashboardWithdrawals(ctx context.Context withdrawalsQuery := ` SELECT w.block_slot, + b.exec_block_number, w.withdrawalindex, w.validatorindex, w.address, @@ -196,32 +199,43 @@ func (d *DataAccessService) GetValidatorDashboardWithdrawals(ctx context.Context } // Prepare the ENS map - addressEns := make(map[string]string) - for _, withdrawal := range queryResult { + addressMapping := make(map[string]*t.Address) + contractStatusRequests := make([]db.ContractInteractionAtRequest, len(queryResult)) + for i, withdrawal := range queryResult { address := hexutil.Encode(withdrawal.Address) - addressEns[address] = "" + addressMapping[address] = nil + contractStatusRequests[i] = db.ContractInteractionAtRequest{ + Address: fmt.Sprintf("%x", withdrawal.Address), + Block: int64(withdrawal.BlockNumber), + TxIdx: -1, + TraceIdx: -1, + } + } + + // Get the ENS names and (label) names for the addresses + if err := d.GetNamesAndEnsForAddresses(ctx, addressMapping); err != nil { + return nil, nil, err } - // Get the ENS names for the addresses - if err := db.GetEnsNamesForAddresses(addressEns); err != nil { + // Get the contract status for the addresses + contractStatuses, err := d.bigtable.GetAddressContractInteractionsAt(contractStatusRequests) + if err != nil { return nil, nil, err } // Create the result cursorData := make([]t.WithdrawalsCursor, 0) - for _, withdrawal := range queryResult { + for i, withdrawal := range queryResult { address := hexutil.Encode(withdrawal.Address) result = append(result, t.VDBWithdrawalsTableRow{ - Epoch: withdrawal.BlockSlot / utils.Config.Chain.ClConfig.SlotsPerEpoch, - Slot: withdrawal.BlockSlot, - Index: withdrawal.ValidatorIndex, - GroupId: validatorGroupMap[withdrawal.ValidatorIndex], - Recipient: t.Address{ - Hash: t.Hash(address), - Ens: addressEns[address], - }, - Amount: utils.GWeiToWei(big.NewInt(int64(withdrawal.Amount))), + Epoch: withdrawal.BlockSlot / utils.Config.Chain.ClConfig.SlotsPerEpoch, + Slot: withdrawal.BlockSlot, + Index: withdrawal.ValidatorIndex, + Recipient: *addressMapping[address], + GroupId: validatorGroupMap[withdrawal.ValidatorIndex], + Amount: utils.GWeiToWei(big.NewInt(int64(withdrawal.Amount))), }) + result[i].Recipient.IsContract = contractStatuses[i] == types.CONTRACT_CREATION || contractStatuses[i] == types.CONTRACT_PRESENT cursorData = append(cursorData, t.WithdrawalsCursor{ Slot: withdrawal.BlockSlot, WithdrawalIndex: withdrawal.WithdrawalIndex, @@ -256,7 +270,8 @@ func (d *DataAccessService) GetValidatorDashboardWithdrawals(ctx context.Context if nextData != nil { // Complete the next data nextData.GroupId = validatorGroupMap[nextData.Index] - nextData.Recipient.Ens = addressEns[string(nextData.Recipient.Hash)] + // TODO integrate label/ens data for "next" row + // nextData.Recipient.Ens = addressEns[string(nextData.Recipient.Hash)] } else { // If there is no next data, add a missing estimate row nextData = &t.VDBWithdrawalsTableRow{ @@ -393,12 +408,28 @@ func (d *DataAccessService) getNextWithdrawalRow(queryValidators []t.VDBValidato withdrawalAmount = 0 } + ens_name, err := db.GetEnsNameForAddress(*address, utils.SlotToTime(nextWithdrawalSlot)) + if err != sql.ErrNoRows { + return nil, err + } + + contractStatusReq := []db.ContractInteractionAtRequest{{ + Address: fmt.Sprintf("%x", address), + Block: -1, + }} + contractStatus, err := d.bigtable.GetAddressContractInteractionsAt(contractStatusReq) + if err != nil { + return nil, err + } + nextData := &t.VDBWithdrawalsTableRow{ Epoch: nextWithdrawalSlot / utils.Config.Chain.ClConfig.SlotsPerEpoch, Slot: nextWithdrawalSlot, Index: *nextValidator, Recipient: t.Address{ - Hash: t.Hash(address.String()), + Hash: t.Hash(address.String()), + Ens: ens_name, + IsContract: contractStatus[0] == types.CONTRACT_CREATION || contractStatus[0] == types.CONTRACT_PRESENT, }, Amount: utils.GWeiToWei(big.NewInt(int64(withdrawalAmount))), } diff --git a/backend/pkg/api/types/common.go b/backend/pkg/api/types/common.go index e658afa80..1734205c8 100644 --- a/backend/pkg/api/types/common.go +++ b/backend/pkg/api/types/common.go @@ -34,8 +34,10 @@ type PubKey string type Hash string // blocks, txs etc. type Address struct { - Hash Hash `json:"hash"` - Ens string `json:"ens,omitempty"` + Hash Hash `json:"hash"` + IsContract bool `json:"is_contract"` + Ens string `json:"ens,omitempty"` + Label string `json:"label,omitempty"` } type LuckItem struct { diff --git a/backend/pkg/commons/db/bigtable_eth1.go b/backend/pkg/commons/db/bigtable_eth1.go index e2fca3052..3861374f2 100644 --- a/backend/pkg/commons/db/bigtable_eth1.go +++ b/backend/pkg/commons/db/bigtable_eth1.go @@ -3138,7 +3138,7 @@ func (bigtable *Bigtable) GetAddressName(address []byte) (string, error) { add := common.Address{} add.SetBytes(address) - name, err := GetEnsNameForAddress(add) + name, err := GetEnsNameForAddress(add, time.Time{}) if err == nil && len(name) > 0 { return name, nil } @@ -3215,11 +3215,11 @@ type isContractInfo struct { ts gcp_bigtable.Timestamp } -type contractInteractionAtRequest struct { - address string - block int64 - txIdx int64 - traceIdx int64 +type ContractInteractionAtRequest struct { + Address string // expected all lowercase without 0x prefix + Block int64 + TxIdx int64 + TraceIdx int64 } func (bigtable *Bigtable) getAddressIsContractHistories(histories map[string][]isContractInfo) error { @@ -3270,7 +3270,7 @@ func (bigtable *Bigtable) getAddressIsContractHistories(histories map[string][]i // returns account state after the given execution state // -1 is latest (e.g. "txIdx" = -1 returns the contract state after execution of "block", "block" = -1 returns the state at chain head) -func (bigtable *Bigtable) GetAddressContractInteractionsAt(requests []contractInteractionAtRequest) ([]types.ContractInteractionType, error) { +func (bigtable *Bigtable) GetAddressContractInteractionsAt(requests []ContractInteractionAtRequest) ([]types.ContractInteractionType, error) { results := make([]types.ContractInteractionType, len(requests)) if len(requests) == 0 { return results, nil @@ -3279,7 +3279,7 @@ func (bigtable *Bigtable) GetAddressContractInteractionsAt(requests []contractIn // get histories histories := make(map[string][]isContractInfo, len(requests)) for _, request := range requests { - histories[request.address] = nil + histories[request.Address] = nil } err := bigtable.getAddressIsContractHistories(histories) if err != nil { @@ -3288,22 +3288,22 @@ func (bigtable *Bigtable) GetAddressContractInteractionsAt(requests []contractIn // evaluate requests; CONTRACT_NONE is default for i, request := range requests { - history, ok := histories[request.address] + history, ok := histories[request.Address] if !ok || history == nil || len(history) == 0 { continue } latestUpdateIdxBeforeReq := 0 - if request.block != -1 { + if request.Block != -1 { var block, tx, itx uint64 - if request.txIdx == -1 { - block = uint64(request.block + 1) - } else if request.traceIdx == -1 { - block = uint64(request.block) - tx = uint64(request.txIdx + 1) + if request.TxIdx == -1 { + block = uint64(request.Block + 1) + } else if request.TraceIdx == -1 { + block = uint64(request.Block) + tx = uint64(request.TxIdx + 1) } else { - block = uint64(request.block) - tx = uint64(request.txIdx) - itx = uint64(request.traceIdx + 1) + block = uint64(request.Block) + tx = uint64(request.TxIdx) + itx = uint64(request.TraceIdx + 1) } req_ts, err := encodeIsContractUpdateTs(block, tx, itx) if err != nil { @@ -3319,7 +3319,7 @@ func (bigtable *Bigtable) GetAddressContractInteractionsAt(requests []contractIn } b, tx, trace := decodeIsContractUpdateTs(history[latestUpdateIdxBeforeReq].ts) - exact_match := request.block == -1 || request.block == int64(b) && (request.txIdx == -1 || request.txIdx == int64(tx) && (request.traceIdx == -1 || request.traceIdx == int64(trace))) + exact_match := request.Block == -1 || request.Block == int64(b) && (request.TxIdx == -1 || request.TxIdx == int64(tx) && (request.TraceIdx == -1 || request.TraceIdx == int64(trace))) if exact_match { results[i] = types.CONTRACT_DESTRUCTION @@ -3343,17 +3343,17 @@ func (bigtable *Bigtable) GetAddressContractInteractionsAt(requests []contractIn // convenience function to get contract interaction status per transaction of a block func (bigtable *Bigtable) GetAddressContractInteractionsAtBlock(block *types.Eth1Block) ([]types.ContractInteractionType, error) { - requests := make([]contractInteractionAtRequest, len(block.GetTransactions())) + requests := make([]ContractInteractionAtRequest, len(block.GetTransactions())) for i, tx := range block.GetTransactions() { address := tx.GetTo() if len(address) == 0 { address = tx.GetContractAddress() } - requests[i] = contractInteractionAtRequest{ - address: fmt.Sprintf("%x", address), - block: int64(block.GetNumber()), - txIdx: int64(i), - traceIdx: -1, + requests[i] = ContractInteractionAtRequest{ + Address: fmt.Sprintf("%x", address), + Block: int64(block.GetNumber()), + TxIdx: int64(i), + TraceIdx: -1, } } @@ -3363,19 +3363,19 @@ func (bigtable *Bigtable) GetAddressContractInteractionsAtBlock(block *types.Eth // convenience function to get contract interaction status per subtransaction of a transaction // 2nd parameter specifies [tx_idx, trace_idx] for each internal tx func (bigtable *Bigtable) GetAddressContractInteractionsAtITransactions(itransactions []*types.Eth1InternalTransactionIndexed, idxs [][2]int64) ([][2]types.ContractInteractionType, error) { - requests := make([]contractInteractionAtRequest, 0, len(itransactions)*2) + requests := make([]ContractInteractionAtRequest, 0, len(itransactions)*2) for i, tx := range itransactions { - requests = append(requests, contractInteractionAtRequest{ - address: fmt.Sprintf("%x", tx.GetFrom()), - block: int64(tx.GetBlockNumber()), - txIdx: idxs[i][0], - traceIdx: idxs[i][1], + requests = append(requests, ContractInteractionAtRequest{ + Address: fmt.Sprintf("%x", tx.GetFrom()), + Block: int64(tx.GetBlockNumber()), + TxIdx: idxs[i][0], + TraceIdx: idxs[i][1], }) - requests = append(requests, contractInteractionAtRequest{ - address: fmt.Sprintf("%x", tx.GetTo()), - block: int64(tx.GetBlockNumber()), - txIdx: idxs[i][0], - traceIdx: idxs[i][1], + requests = append(requests, ContractInteractionAtRequest{ + Address: fmt.Sprintf("%x", tx.GetTo()), + Block: int64(tx.GetBlockNumber()), + TxIdx: idxs[i][0], + TraceIdx: idxs[i][1], }) } results, err := bigtable.GetAddressContractInteractionsAt(requests) @@ -3392,20 +3392,20 @@ func (bigtable *Bigtable) GetAddressContractInteractionsAtITransactions(itransac // convenience function to get contract interaction status per parity trace func (bigtable *Bigtable) GetAddressContractInteractionsAtParityTraces(traces []*rpc.ParityTraceResult) ([][2]types.ContractInteractionType, error) { - requests := make([]contractInteractionAtRequest, 0, len(traces)*2) + requests := make([]ContractInteractionAtRequest, 0, len(traces)*2) for i, itx := range traces { from, to, _, _ := itx.ConvertFields() - requests = append(requests, contractInteractionAtRequest{ - address: fmt.Sprintf("%x", from), - block: int64(itx.BlockNumber), - txIdx: int64(itx.TransactionPosition), - traceIdx: int64(i), + requests = append(requests, ContractInteractionAtRequest{ + Address: fmt.Sprintf("%x", from), + Block: int64(itx.BlockNumber), + TxIdx: int64(itx.TransactionPosition), + TraceIdx: int64(i), }) - requests = append(requests, contractInteractionAtRequest{ - address: fmt.Sprintf("%x", to), - block: int64(itx.BlockNumber), - txIdx: int64(itx.TransactionPosition), - traceIdx: int64(i), + requests = append(requests, ContractInteractionAtRequest{ + Address: fmt.Sprintf("%x", to), + Block: int64(itx.BlockNumber), + TxIdx: int64(itx.TransactionPosition), + TraceIdx: int64(i), }) } results, err := bigtable.GetAddressContractInteractionsAt(requests) @@ -3422,13 +3422,13 @@ func (bigtable *Bigtable) GetAddressContractInteractionsAtParityTraces(traces [] // convenience function to get contract interaction status per transaction func (bigtable *Bigtable) GetAddressContractInteractionsAtTransactions(transactions []*types.Eth1TransactionIndexed, idxs []int64) ([]types.ContractInteractionType, error) { - requests := make([]contractInteractionAtRequest, len(transactions)) + requests := make([]ContractInteractionAtRequest, len(transactions)) for i, tx := range transactions { - requests[i] = contractInteractionAtRequest{ - address: fmt.Sprintf("%x", tx.GetTo()), - block: int64(tx.GetBlockNumber()), - txIdx: idxs[i], - traceIdx: -1, + requests[i] = ContractInteractionAtRequest{ + Address: fmt.Sprintf("%x", tx.GetTo()), + Block: int64(tx.GetBlockNumber()), + TxIdx: idxs[i], + TraceIdx: -1, } } return bigtable.GetAddressContractInteractionsAt(requests) diff --git a/backend/pkg/commons/db/ens.go b/backend/pkg/commons/db/ens.go index 46001df56..5020f31e6 100644 --- a/backend/pkg/commons/db/ens.go +++ b/backend/pkg/commons/db/ens.go @@ -609,15 +609,19 @@ func GetAddressForEnsName(name string) (address *common.Address, err error) { return address, err } -func GetEnsNameForAddress(address common.Address) (name string, err error) { +// pass invalid time to get latest data +func GetEnsNameForAddress(address common.Address, validUntil time.Time) (name string, err error) { + if validUntil.IsZero() { + validUntil = time.Now() + } err = ReaderDb.Get(&name, ` SELECT ens_name FROM ens WHERE address = $1 AND is_primary_name AND - valid_to >= now() - ;`, address.Bytes()) + valid_to >= $2 + ;`, address.Bytes(), validUntil) return name, err } diff --git a/backend/pkg/commons/db/migrations/postgres/20240822134034_add_address_tags.sql b/backend/pkg/commons/db/migrations/postgres/20240822134034_add_address_tags.sql new file mode 100644 index 000000000..3579f7ef6 --- /dev/null +++ b/backend/pkg/commons/db/migrations/postgres/20240822134034_add_address_tags.sql @@ -0,0 +1,15 @@ +-- +goose Up +-- +goose StatementBegin +SELECT('up SQL query - create address_names table'); +CREATE TABLE IF NOT EXISTS address_names ( + address bytea NOT NULL UNIQUE, + name TEXT NOT NULL, + PRIMARY KEY (address, name) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +SELECT('down SQL query - drop address_names table'); +DROP TABLE IF EXISTS address_names; +-- +goose StatementEnd diff --git a/frontend/types/api/common.ts b/frontend/types/api/common.ts index ecce7231e..3c8c9506a 100644 --- a/frontend/types/api/common.ts +++ b/frontend/types/api/common.ts @@ -27,7 +27,9 @@ export type PubKey = string; export type Hash = string; // blocks, txs etc. export interface Address { hash: Hash; + is_contract: boolean; ens?: string; + label?: string; } export interface LuckItem { percent: number /* float64 */;