Skip to content

Commit 0694b52

Browse files
authored
[api] eth_getTransactionRecipt return TransactionLogs in events way (#4687)
1 parent 398177c commit 0694b52

13 files changed

+335
-29
lines changed

action/Makefile

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,52 @@
22

33
# Variables
44
SOLC := solc
5-
SOL_FILE := native_staking_contract_interface.sol
6-
ABI_FILE := native_staking_contract_abi.json
5+
NATIVE_SOL_FILE := native_staking_contract_interface.sol
6+
NATIVE_ABI_FILE := native_staking_contract_abi.json
7+
ACCOUNT_SOL_FILE := account_contract_interface.sol
8+
ACCOUNT_ABI_FILE := account_contract_abi.json
79
TEMP_DIR := .temp
810

911
# Default target
1012
.PHONY: all
1113
all: abi
1214

13-
# Generate ABI from Solidity interface
15+
# Generate all ABI files
1416
.PHONY: abi
15-
abi: $(ABI_FILE)
17+
abi: native-abi account-abi
1618

17-
$(ABI_FILE): $(SOL_FILE)
18-
@echo "Generating ABI from $(SOL_FILE)..."
19+
# Generate ABI from native staking contract interface
20+
.PHONY: native-abi
21+
native-abi: $(NATIVE_ABI_FILE)
22+
23+
$(NATIVE_ABI_FILE): $(NATIVE_SOL_FILE)
24+
@echo "Generating ABI from $(NATIVE_SOL_FILE)..."
1925
@mkdir -p $(TEMP_DIR)
20-
@$(SOLC) --abi $(SOL_FILE) -o $(TEMP_DIR) --overwrite
26+
@$(SOLC) --abi $(NATIVE_SOL_FILE) -o $(TEMP_DIR) --overwrite
2127
@# Extract the ABI from the generated file and format it
2228
@if [ -f "$(TEMP_DIR)/INativeStakingContract.abi" ]; then \
23-
cat "$(TEMP_DIR)/INativeStakingContract.abi" | jq '.' > $(ABI_FILE); \
24-
echo "ABI generated successfully: $(ABI_FILE)"; \
29+
cat "$(TEMP_DIR)/INativeStakingContract.abi" | jq '.' > $(NATIVE_ABI_FILE); \
30+
echo "ABI generated successfully: $(NATIVE_ABI_FILE)"; \
31+
else \
32+
echo "Error: Native staking ABI file not generated"; \
33+
exit 1; \
34+
fi
35+
@rm -rf $(TEMP_DIR)
36+
37+
# Generate ABI from account contract interface
38+
.PHONY: account-abi
39+
account-abi: $(ACCOUNT_ABI_FILE)
40+
41+
$(ACCOUNT_ABI_FILE): $(ACCOUNT_SOL_FILE)
42+
@echo "Generating ABI from $(ACCOUNT_SOL_FILE)..."
43+
@mkdir -p $(TEMP_DIR)
44+
@$(SOLC) --abi $(ACCOUNT_SOL_FILE) -o $(TEMP_DIR) --overwrite
45+
@# Extract the ABI from the generated file and format it
46+
@if [ -f "$(TEMP_DIR)/IAccountProtocolContract.abi" ]; then \
47+
cat "$(TEMP_DIR)/IAccountProtocolContract.abi" | jq '.' > $(ACCOUNT_ABI_FILE); \
48+
echo "ABI generated successfully: $(ACCOUNT_ABI_FILE)"; \
2549
else \
26-
echo "Error: ABI file not generated"; \
50+
echo "Error: Account contract ABI file not generated"; \
2751
exit 1; \
2852
fi
2953
@rm -rf $(TEMP_DIR)
@@ -32,7 +56,7 @@ $(ABI_FILE): $(SOL_FILE)
3256
.PHONY: clean
3357
clean:
3458
@echo "Cleaning generated files..."
35-
@rm -f $(ABI_FILE)
59+
@rm -f $(NATIVE_ABI_FILE) $(ACCOUNT_ABI_FILE)
3660
@rm -rf $(TEMP_DIR)
3761

3862
# Install dependencies (solc and jq)
@@ -82,39 +106,51 @@ check-deps:
82106
# Watch for changes and regenerate ABI
83107
.PHONY: watch
84108
watch: check-deps
85-
@echo "Watching $(SOL_FILE) for changes..."
109+
@echo "Watching $(NATIVE_SOL_FILE) and $(ACCOUNT_SOL_FILE) for changes..."
86110
@while true; do \
87-
inotifywait -e modify $(SOL_FILE) 2>/dev/null || \
111+
inotifywait -e modify $(NATIVE_SOL_FILE) $(ACCOUNT_SOL_FILE) 2>/dev/null || \
88112
(echo "inotifywait not available, using sleep instead"; sleep 5); \
89113
make abi; \
90114
echo "Waiting for changes..."; \
91115
done
92116

93-
# Validate generated ABI
117+
# Validate generated ABI files
94118
.PHONY: validate
95-
validate: $(ABI_FILE)
96-
@echo "Validating ABI file..."
97-
@if jq empty $(ABI_FILE) 2>/dev/null; then \
98-
echo "✅ ABI file is valid JSON"; \
99-
echo "Functions found: $$(jq '[.[] | select(.type=="function")] | length' $(ABI_FILE))"; \
100-
echo "Events found: $$(jq '[.[] | select(.type=="event")] | length' $(ABI_FILE))"; \
119+
validate: $(NATIVE_ABI_FILE) $(ACCOUNT_ABI_FILE)
120+
@echo "Validating ABI files..."
121+
@echo "Validating $(NATIVE_ABI_FILE)..."
122+
@if jq empty $(NATIVE_ABI_FILE) 2>/dev/null; then \
123+
echo "✅ Native staking ABI file is valid JSON"; \
124+
echo "Functions found: $$(jq '[.[] | select(.type=="function")] | length' $(NATIVE_ABI_FILE))"; \
125+
echo "Events found: $$(jq '[.[] | select(.type=="event")] | length' $(NATIVE_ABI_FILE))"; \
126+
else \
127+
echo "❌ Native staking ABI file is not valid JSON"; \
128+
exit 1; \
129+
fi
130+
@echo "Validating $(ACCOUNT_ABI_FILE)..."
131+
@if jq empty $(ACCOUNT_ABI_FILE) 2>/dev/null; then \
132+
echo "✅ Account contract ABI file is valid JSON"; \
133+
echo "Functions found: $$(jq '[.[] | select(.type=="function")] | length' $(ACCOUNT_ABI_FILE))"; \
134+
echo "Events found: $$(jq '[.[] | select(.type=="event")] | length' $(ACCOUNT_ABI_FILE))"; \
101135
else \
102-
echo "❌ ABI file is not valid JSON"; \
136+
echo "Account contract ABI file is not valid JSON"; \
103137
exit 1; \
104138
fi
105139

106140
# Show help
107141
.PHONY: help
108142
help:
109143
@echo "Available targets:"
110-
@echo " abi - Generate ABI from Solidity interface (default)"
144+
@echo " abi - Generate all ABI files from Solidity interfaces (default)"
145+
@echo " native-abi - Generate ABI from native staking contract interface"
146+
@echo " account-abi - Generate ABI from account contract interface"
111147
@echo " clean - Remove generated files"
112148
@echo " install-deps - Install solc and jq dependencies"
113149
@echo " check-deps - Check if dependencies are installed"
114150
@echo " watch - Watch for changes and regenerate ABI"
115-
@echo " validate - Validate generated ABI file"
151+
@echo " validate - Validate generated ABI files"
116152
@echo " help - Show this help message"
117153
@echo ""
118154
@echo "Files:"
119-
@echo " Input: $(SOL_FILE)"
120-
@echo " Output: $(ABI_FILE)"
155+
@echo " Input: $(NATIVE_SOL_FILE) -> Output: $(NATIVE_ABI_FILE)"
156+
@echo " Input: $(ACCOUNT_SOL_FILE) -> Output: $(ACCOUNT_ABI_FILE)"

action/account_contract_abi.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package action
2+
3+
import (
4+
_ "embed"
5+
"math/big"
6+
"strings"
7+
"sync"
8+
9+
"github.com/ethereum/go-ethereum/accounts/abi"
10+
"github.com/ethereum/go-ethereum/common"
11+
"github.com/iotexproject/go-pkgs/hash"
12+
"github.com/iotexproject/iotex-address/address"
13+
)
14+
15+
var (
16+
//go:embed account_contract_abi.json
17+
AccountContractABIJSON string
18+
19+
accountContractABI abi.ABI
20+
accountContractTransferEvent abi.Event
21+
22+
onceAcc sync.Once
23+
)
24+
25+
func initAccountContractABI() {
26+
onceAcc.Do(func() {
27+
var err error
28+
accountContractABI, err = abi.JSON(strings.NewReader(AccountContractABIJSON))
29+
if err != nil {
30+
panic("failed to load account contract ABI: " + err.Error())
31+
}
32+
var ok bool
33+
accountContractTransferEvent, ok = accountContractABI.Events["Transfer"]
34+
if !ok {
35+
panic("failed to load Transfer event from account contract ABI")
36+
}
37+
})
38+
}
39+
40+
// AccountContractABI returns the ABI of the account contract
41+
func AccountContractABI() *abi.ABI {
42+
initAccountContractABI()
43+
return &accountContractABI
44+
}
45+
46+
// PackAccountTransferEvent packs the parameters for the account transfer event
47+
func PackAccountTransferEvent(
48+
from, to address.Address, amount *big.Int, typ uint8,
49+
) (Topics, []byte, error) {
50+
initAccountContractABI()
51+
data, err := accountContractTransferEvent.Inputs.NonIndexed().Pack(amount)
52+
if err != nil {
53+
return nil, nil, err
54+
}
55+
topics := make(Topics, 4)
56+
topics[0] = hash.Hash256(accountContractTransferEvent.ID)
57+
topics[1] = hash.Hash256(common.BytesToHash(from.Bytes()))
58+
topics[2] = hash.Hash256(common.BytesToHash(to.Bytes()))
59+
topics[3] = hash.Hash256(common.BytesToHash([]byte{typ}))
60+
return topics, data, nil
61+
}

action/account_contract_abi.json

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
[
2+
{
3+
"anonymous": false,
4+
"inputs": [
5+
{
6+
"indexed": true,
7+
"internalType": "address",
8+
"name": "from",
9+
"type": "address"
10+
},
11+
{
12+
"indexed": true,
13+
"internalType": "address",
14+
"name": "to",
15+
"type": "address"
16+
},
17+
{
18+
"indexed": true,
19+
"internalType": "enum IAccountProtocolContract.TransactionLogType",
20+
"name": "logType",
21+
"type": "uint8"
22+
},
23+
{
24+
"indexed": false,
25+
"internalType": "uint256",
26+
"name": "amount",
27+
"type": "uint256"
28+
}
29+
],
30+
"name": "Transfer",
31+
"type": "event"
32+
}
33+
]
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: UNLICENSED
2+
pragma solidity ^0.8.24;
3+
4+
// Interface for the Account Protocol contract
5+
interface IAccountProtocolContract {
6+
// enum representing different types of transfer events
7+
enum TransactionLogType {
8+
IN_CONTRACT_TRANSFER,
9+
WITHDRAW_BUCKET,
10+
CREATE_BUCKET,
11+
DEPOSIT_TO_BUCKET,
12+
CANDIDATE_SELF_STAKE,
13+
CANDIDATE_REGISTRATION_FEE,
14+
GAS_FEE,
15+
NATIVE_TRANSFER,
16+
DEPOSIT_TO_REWARDING_FUND,
17+
CLAIM_FROM_REWARDING_FUND,
18+
BLOB_FEE,
19+
PRIORITY_FEE
20+
}
21+
22+
// Event emitted when a transfer occurs
23+
event Transfer(
24+
address indexed from,
25+
address indexed to,
26+
TransactionLogType indexed logType,
27+
uint256 amount
28+
);
29+
}

action/receipt.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212
"google.golang.org/protobuf/proto"
1313

1414
"github.com/iotexproject/go-pkgs/hash"
15+
"github.com/iotexproject/iotex-address/address"
1516
"github.com/iotexproject/iotex-proto/golang/iotextypes"
17+
"github.com/pkg/errors"
1618

1719
"github.com/iotexproject/iotex-core/v2/pkg/log"
1820
)
@@ -195,6 +197,25 @@ func (receipt *Receipt) UpdateIndex(txIndex, logIndex uint32) uint32 {
195197
return logIndex
196198
}
197199

200+
// TransferLogs converts transaction logs to logs with given account contract address and starting log index
201+
func (receipt *Receipt) TransferLogs(accountContractAddr string, logIndex uint32) ([]*Log, error) {
202+
logs := make([]*Log, 0, len(receipt.transactionLogs))
203+
for _, tlog := range receipt.transactionLogs {
204+
log, err := tlog.convertToLog()
205+
if err != nil {
206+
return nil, errors.Wrapf(err, "failed to convert transaction log to log: %s", tlog.Type)
207+
}
208+
log.Address = accountContractAddr
209+
log.BlockHeight = receipt.BlockHeight
210+
log.ActionHash = receipt.ActionHash
211+
log.Index = logIndex
212+
log.TxIndex = receipt.TxIndex
213+
logs = append(logs, log)
214+
logIndex++
215+
}
216+
return logs, nil
217+
}
218+
198219
// ConvertToLogPb converts a Log to protobuf's Log
199220
func (log *Log) ConvertToLogPb() *iotextypes.Log {
200221
l := &iotextypes.Log{}
@@ -246,3 +267,36 @@ func (log *Log) Deserialize(buf []byte) error {
246267
log.ConvertFromLogPb(pbLog)
247268
return nil
248269
}
270+
271+
func convertSpecialAddress(addr string) string {
272+
switch addr {
273+
case address.StakingBucketPoolAddr:
274+
return address.StakingProtocolAddr
275+
case address.RewardingPoolAddr:
276+
return address.RewardingProtocol
277+
case "":
278+
return address.ZeroAddress
279+
}
280+
return addr
281+
}
282+
283+
func (tlog *TransactionLog) convertToLog() (*Log, error) {
284+
sender := convertSpecialAddress(tlog.Sender)
285+
recipient := convertSpecialAddress(tlog.Recipient)
286+
from, err := address.FromString(sender)
287+
if err != nil {
288+
return nil, errors.Wrapf(err, "failed to convert sender address from string: %s", sender)
289+
}
290+
to, err := address.FromString(recipient)
291+
if err != nil {
292+
return nil, errors.Wrapf(err, "failed to convert recipient address from string: %s", recipient)
293+
}
294+
topics, data, err := PackAccountTransferEvent(from, to, tlog.Amount, uint8(tlog.Type))
295+
if err != nil {
296+
return nil, errors.Wrapf(err, "failed to pack account transfer event: %s", tlog.Type)
297+
}
298+
return &Log{
299+
Topics: topics,
300+
Data: data,
301+
}, nil
302+
}

api/coreservice.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,8 @@ func (core *coreService) BalanceAt(ctx context.Context, addr address.Address, he
359359
if err != nil {
360360
return "", status.Error(codes.Internal, err.Error())
361361
}
362-
if addrStr == address.RewardingPoolAddr || addrStr == address.StakingBucketPoolAddr {
362+
if addrStr == address.RewardingPoolAddr || addrStr == address.StakingBucketPoolAddr ||
363+
addrStr == address.RewardingProtocol || addrStr == address.StakingProtocolAddr {
363364
acc, _, err := core.getProtocolAccount(ctx, addrStr)
364365
if err != nil {
365366
return "", err
@@ -1883,7 +1884,7 @@ func (core *coreService) getProtocolAccount(ctx context.Context, addr string) (*
18831884
err error
18841885
)
18851886
switch addr {
1886-
case address.RewardingPoolAddr:
1887+
case address.RewardingPoolAddr, address.RewardingProtocol:
18871888
if out, err = core.ReadState("rewarding", "", []byte("TotalBalance"), nil); err != nil {
18881889
return nil, nil, err
18891890
}
@@ -1892,7 +1893,7 @@ func (core *coreService) getProtocolAccount(ctx context.Context, addr string) (*
18921893
return nil, nil, errors.New("balance convert error")
18931894
}
18941895
balance = val.String()
1895-
case address.StakingBucketPoolAddr:
1896+
case address.StakingBucketPoolAddr, address.StakingProtocolAddr:
18961897
methodName, err := proto.Marshal(&iotexapi.ReadStakingDataMethod{
18971898
Method: iotexapi.ReadStakingDataMethod_TOTAL_STAKING_AMOUNT,
18981899
})

0 commit comments

Comments
 (0)