diff --git a/docs/api/docs.go b/docs/api/docs.go index 976e34e..9283951 100644 --- a/docs/api/docs.go +++ b/docs/api/docs.go @@ -1548,6 +1548,9 @@ const docTemplate = `{ "deployer": { "type": "string" }, + "deposit_balance": { + "type": "number" + }, "eip_standard": { "type": "string" }, diff --git a/docs/api/swagger.json b/docs/api/swagger.json index 98bef7b..bd8b833 100644 --- a/docs/api/swagger.json +++ b/docs/api/swagger.json @@ -1537,6 +1537,9 @@ "deployer": { "type": "string" }, + "deposit_balance": { + "type": "number" + }, "eip_standard": { "type": "string" }, diff --git a/docs/api/swagger.yaml b/docs/api/swagger.yaml index 8709858..fc543c8 100644 --- a/docs/api/swagger.yaml +++ b/docs/api/swagger.yaml @@ -87,6 +87,8 @@ definitions: type: string deployer: type: string + deposit_balance: + type: number eip_standard: type: string event_identifiers: diff --git a/plugins/evm/dao/contract.go b/plugins/evm/dao/contract.go index 9b3f9a1..a85e82c 100644 --- a/plugins/evm/dao/contract.go +++ b/plugins/evm/dao/contract.go @@ -9,6 +9,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/itering/subscan/model" + "github.com/itering/subscan/pkg/go-web3/complex/types" + "github.com/itering/subscan/pkg/go-web3/dto" evmABI "github.com/itering/subscan/plugins/evm/abi" evmContract "github.com/itering/subscan/plugins/evm/contract" "github.com/itering/subscan/plugins/evm/feature/delegateProxy" @@ -54,10 +56,11 @@ type Contract struct { Precompile uint `json:"precompile"` CompileSettings datatypes.JSON `json:"CompileSettings"` - EipStandard string `json:"eip_standard" gorm:"size:100"` - ProxyImplementation string `json:"proxy_implementation" gorm:"size:64"` - ConstructorArguments string `json:"constructor_arguments" gorm:"type:string"` - DeployCodeHash string `json:"deploy_code_hash" gorm:"size:70;index:deploy_code_hash;default:'';not null"` + EipStandard string `json:"eip_standard" gorm:"size:100"` + ProxyImplementation string `json:"proxy_implementation" gorm:"size:64"` + ConstructorArguments string `json:"constructor_arguments" gorm:"type:string"` + DeployCodeHash string `json:"deploy_code_hash" gorm:"size:70;index:deploy_code_hash;default:'';not null"` + DepositBalance decimal.Decimal `json:"deposit_balance" gorm:"-"` } type ContractSampleJson struct { @@ -194,11 +197,12 @@ func (t *Transaction) NewContract(ctx context.Context) error { CreationCode: t.InputData, DeployAt: t.BlockTimestamp, BlockNum: t.BlockNum, + TxHash: t.Hash, Deployer: t.FromAddress, ExtrinsicIndex: t.ExtrinsicIndex, Precompile: t.Precompile, } - return sg.AddOrUpdateItem(ctx, contract, []string{"address"}, "creation_code", "deploy_at", "block_num", "deployer", "extrinsic_index", "precompile").Error + return sg.AddOrUpdateItem(ctx, contract, []string{"address"}, "creation_code", "deploy_at", "block_num", "tx_hash", "deployer", "extrinsic_index", "precompile").Error } func ContractAddr(ctx context.Context) (list []string) { @@ -285,12 +289,34 @@ func ContractsByAddr(ctx context.Context, contracts string) (contract *Contract) contract = &dbContract } + if balance, ok := latestEvmContractDepositBalance(ctx, contract.Address); ok { + contract.DepositBalance = balance + } + if len(contract.Abi) > 0 && contract.Abi.String() != "null" { contract.EventIdentifiers = findEventIdentifiers(ctx, contract.Abi) } return } +func latestEvmContractDepositBalance(ctx context.Context, contractAddress string) (decimal.Decimal, bool) { + if web3.RPC == nil || web3.RPC.Eth == nil { + return decimal.Zero, false + } + result, err := web3.RPC.Eth.Call(ctx, &dto.TransactionParameters{ + To: contractAddress, + Data: types.ComplexString("0xc399ec88"), // getDeposit() + }) + if err != nil || result == nil { + return decimal.Zero, false + } + balance, err := result.ToBigInt() + if err != nil || balance == nil { + return decimal.Zero, false + } + return decimal.NewFromBigInt(balance, 0), true +} + func backfillContractFromRuntimeCode(ctx context.Context, contractAddress string) *Contract { if web3.RPC == nil || web3.RPC.Eth == nil { return nil diff --git a/plugins/evm/http/contract_e2e_test.go b/plugins/evm/http/contract_e2e_test.go new file mode 100644 index 0000000..f31f5f4 --- /dev/null +++ b/plugins/evm/http/contract_e2e_test.go @@ -0,0 +1,52 @@ +package http + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/itering/subscan/plugins/evm/dao" + "github.com/shopspring/decimal" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type contractRouteMockServer struct { + MockServer +} + +func (m contractRouteMockServer) ContractsByAddr(_ context.Context, address string) *dao.Contract { + return &dao.Contract{ + Address: address, + VerifyStatus: "perfect", + DepositBalance: decimal.NewFromInt(2), + } +} + +func TestContractRouteReturnsDepositBalance(t *testing.T) { + originalSrv := srv + srv = contractRouteMockServer{} + t.Cleanup(func() { srv = originalSrv }) + + request := httptest.NewRequest( + http.MethodPost, + "/api/plugin/evm/contract", + strings.NewReader(`{"address":"0x0000000000000000000000000000000000000002"}`), + ) + recorder := httptest.NewRecorder() + require.NoError(t, contractHandle(recorder, request)) + require.Equal(t, http.StatusOK, recorder.Code) + + var response struct { + Code int `json:"code"` + Data struct { + DepositBalance decimal.Decimal `json:"deposit_balance"` + } `json:"data"` + } + require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &response)) + require.Zero(t, response.Code) + assert.True(t, response.Data.DepositBalance.Equal(decimal.NewFromInt(2))) +} diff --git a/ui-react/src/pages/contract/[id].tsx b/ui-react/src/pages/contract/[id].tsx index a8ab165..50bbb7b 100644 --- a/ui-react/src/pages/contract/[id].tsx +++ b/ui-react/src/pages/contract/[id].tsx @@ -1,7 +1,7 @@ import React from 'react' import { CardBody, Card, Tabs, Tab, Divider } from '@heroui/react' import { useRouter } from 'next/router' -import { formatBalanceAmount, getThemeColor } from '@/utils/text' +import { formatBalanceAmount, getThemeColor, getUTCTime } from '@/utils/text' import { unwrap, usePVMAccounts, usePVMContract } from '@/utils/api' import { useData } from '@/context' import { TxTable } from '@/components/tx' @@ -32,6 +32,7 @@ export default function Page() { const accountListData = unwrap(accountsData) const accountData = accountListData?.list?.[0] const contractData = unwrap(data) + const deployAt = Number(contractData?.deploy_at || 0) return ( @@ -62,10 +63,19 @@ export default function Page() {
Create At
-
- {contractData.tx_hash} -
+
{deployAt > 0 ? getUTCTime(deployAt) : '-'}
+ {contractData.tx_hash && ( + <> + +
+
Create Tx
+
+ {contractData.tx_hash} +
+
+ + )}
Balance
@@ -74,6 +84,13 @@ export default function Page() {
+
+
Deposit Balance
+
+ {formatBalanceAmount(new BigNumber(contractData.deposit_balance || 0), token?.decimals)} {token?.symbol} +
+
+ diff --git a/ui-react/src/utils/api.ts b/ui-react/src/utils/api.ts index dfdd1ea..9417a5a 100644 --- a/ui-react/src/utils/api.ts +++ b/ui-react/src/utils/api.ts @@ -629,6 +629,7 @@ export type pvmContractInfoType = { deploy_at: number; deploy_code_hash: string; deployer: string; + deposit_balance: string; eip_standard: string; event_identifiers: any | null; evm_version: string;