@@ -4,13 +4,16 @@ import (
4
4
"context"
5
5
"errors"
6
6
"fmt"
7
-
8
7
"github.com/NilFoundation/nil/nil/common"
8
+ "github.com/NilFoundation/nil/nil/internal/config"
9
+ "github.com/NilFoundation/nil/nil/internal/contracts"
9
10
"github.com/NilFoundation/nil/nil/internal/db"
10
11
"github.com/NilFoundation/nil/nil/internal/execution"
11
12
"github.com/NilFoundation/nil/nil/internal/mpt"
12
13
"github.com/NilFoundation/nil/nil/internal/types"
13
14
rawapitypes "github.com/NilFoundation/nil/nil/services/rpc/rawapi/types"
15
+ "math/big"
16
+ "reflect"
14
17
)
15
18
16
19
var errBlockNotFound = errors .New ("block not found" )
@@ -79,6 +82,117 @@ func (api *LocalShardApi) GetTokens(
79
82
ctx context.Context ,
80
83
address types.Address ,
81
84
blockReference rawapitypes.BlockReference ,
85
+ ) (map [types.TokenId ]types.Value , error ) {
86
+ abi , err := contracts .GetAbi (contracts .NameTokenManager )
87
+ if err != nil {
88
+ return nil , fmt .Errorf ("cannot get ABI: %w" , err )
89
+ }
90
+
91
+ calldata , err := abi .Pack ("getTokens" , address )
92
+ if err != nil {
93
+ return nil , fmt .Errorf ("cannot pack calldata: %w" , err )
94
+ }
95
+
96
+ tokenManagerAddr := types .ShardAndHexToAddress (address .ShardId (), types .TokenManagerPureAddress )
97
+
98
+ ret , err := api .CallGetter (ctx , tokenManagerAddr , calldata )
99
+ if err != nil {
100
+ return nil , fmt .Errorf ("failed to call getter: %w" , err )
101
+ }
102
+
103
+ entries , err := abi .Unpack ("getTokens" , ret )
104
+ if err != nil {
105
+ return nil , fmt .Errorf ("failed to unpack response: %w" , err )
106
+ }
107
+
108
+ tokens , err := extractTokens (entries [0 ])
109
+ if err != nil {
110
+ return nil , fmt .Errorf ("failed to extract tokens: %w" , err )
111
+ }
112
+
113
+ res := make (map [types.TokenId ]types.Value )
114
+ for t := range tokens {
115
+ res [tokens [t ].Token ] = tokens [t ].Balance
116
+ }
117
+ return res , nil
118
+ }
119
+
120
+ func extractTokens (arg any ) ([]types.TokenBalance , error ) {
121
+ slice := reflect .ValueOf (arg )
122
+ tokens := make ([]types.TokenBalance , slice .Len ())
123
+ if slice .Len () >= types .TransactionMaxTokenSize {
124
+ return nil , types .NewVmError (types .ErrorPrecompileTokenArrayIsTooBig )
125
+ }
126
+ for i := range slice .Len () {
127
+ elem := slice .Index (i )
128
+ tokenId , ok := elem .FieldByIndex ([]int {0 }).Interface ().(types.Address )
129
+ if ! ok {
130
+ return nil , errors .New ("tokenId is not an Address type" )
131
+ }
132
+ tokens [i ].Token = types .TokenId (tokenId )
133
+
134
+ balanceBig , ok := elem .FieldByIndex ([]int {1 }).Interface ().(* big.Int )
135
+ if ! ok {
136
+ return nil , errors .New ("balance is not a big.Int" )
137
+ }
138
+ tokens [i ].Balance = types .NewValueFromBigMust (balanceBig )
139
+ }
140
+ return tokens , nil
141
+ }
142
+
143
+ func (api * LocalShardApi ) CallGetter (
144
+ ctx context.Context ,
145
+ address types.Address ,
146
+ calldata []byte ,
147
+ ) ([]byte , error ){
148
+ tx , err := api .db .CreateRoTx (ctx )
149
+ if err != nil {
150
+ return nil , err
151
+ }
152
+ defer tx .Rollback ()
153
+
154
+ block , _ , err := db .ReadLastBlock (tx , address .ShardId ())
155
+ if err != nil {
156
+ return nil , fmt .Errorf ("failed to read last block: %w" , err )
157
+ }
158
+
159
+ cfgAccessor , err := config .NewConfigReader (tx , & block .MainShardHash )
160
+ if err != nil {
161
+ return nil , fmt .Errorf ("failed to create config accessor: %w" , err )
162
+ }
163
+
164
+ es , err := execution .NewExecutionState (tx , address .ShardId (), execution.StateParams {
165
+ Block : block ,
166
+ ConfigAccessor : cfgAccessor ,
167
+ Mode : execution .ModeReadOnly ,
168
+ })
169
+ if err != nil {
170
+ return nil , err
171
+ }
172
+
173
+ extTxn := & types.ExternalTransaction {
174
+ FeeCredit : types .GasToValue (types .DefaultMaxGasInBlock .Uint64 ()),
175
+ MaxFeePerGas : types .MaxFeePerGasDefault ,
176
+ To : address ,
177
+ Data : calldata ,
178
+ }
179
+
180
+ txn := extTxn .ToTransaction ()
181
+
182
+ payer := execution .NewDummyPayer ()
183
+
184
+ es .AddInTransaction (txn )
185
+ res := es .HandleTransaction (ctx , txn , payer )
186
+ if res .Failed () {
187
+ return nil , fmt .Errorf ("transaction failed: %w" , res .GetError ())
188
+ }
189
+ return res .ReturnData , nil
190
+ }
191
+
192
+ func (api * LocalShardApi ) GetTokens1 (
193
+ ctx context.Context ,
194
+ address types.Address ,
195
+ blockReference rawapitypes.BlockReference ,
82
196
) (map [types.TokenId ]types.Value , error ) {
83
197
shardId := address .ShardId ()
84
198
if shardId != api .ShardId {
0 commit comments