diff --git a/blockindex/contractstaking/event_handler.go b/blockindex/contractstaking/event_handler.go index dd95f9ba20..5d4fe76493 100644 --- a/blockindex/contractstaking/event_handler.go +++ b/blockindex/contractstaking/event_handler.go @@ -429,12 +429,20 @@ func (eh *contractStakingEventHandler) handleTransferEvent(event eventParam) err if err != nil { return err } - tokenID, err := event.IndexedFieldUint256("tokenId") + tokenIDParam, err := event.IndexedFieldUint256("tokenId") if err != nil { return err } - eh.tokenOwner[tokenID.Uint64()] = to + tokenID := tokenIDParam.Uint64() + // cache token owner for stake event + eh.tokenOwner[tokenID] = to + // update bucket owner if token exists + if bi, ok := eh.dirty.getBucketInfo(tokenID); ok { + bi.Owner = to + return eh.dirty.updateBucketInfo(tokenID, bi) + } + return nil } diff --git a/blockindex/contractstaking/indexer_test.go b/blockindex/contractstaking/indexer_test.go index c8d594871f..d5d2e03838 100644 --- a/blockindex/contractstaking/indexer_test.go +++ b/blockindex/contractstaking/indexer_test.go @@ -169,7 +169,7 @@ func TestContractStakingIndexerThreadSafe(t *testing.T) { for i := 2; i < 1000; i++ { height := uint64(i) handler := newContractStakingEventHandler(indexer.cache, height) - stake(r, handler, owner, delegate, 1, 10, 100, height) + stake(r, handler, owner, delegate, int64(i), 10, 100, height) err := indexer.commit(handler) r.NoError(err) } @@ -293,6 +293,7 @@ func TestContractStakingIndexerBucketInfo(t *testing.T) { owner := identityset.Address(0) delegate := identityset.Address(1) height++ + createHeight := height handler = newContractStakingEventHandler(indexer.cache, height) stake(r, handler, owner, delegate, 1, 10, 100, height) r.NoError(err) @@ -312,6 +313,16 @@ func TestContractStakingIndexerBucketInfo(t *testing.T) { r.EqualValues(10, indexer.CandidateVotes(delegate).Uint64()) r.EqualValues(1, indexer.TotalBucketCount()) + // transfer + newOwner := identityset.Address(2) + height++ + handler = newContractStakingEventHandler(indexer.cache, height) + transfer(r, handler, newOwner, int64(bucket.Index)) + r.NoError(indexer.commit(handler)) + bucket, ok = indexer.Bucket(bucket.Index) + r.True(ok) + r.EqualValues(newOwner, bucket.Owner) + // unlock height++ handler = newContractStakingEventHandler(indexer.cache, height) @@ -320,13 +331,13 @@ func TestContractStakingIndexerBucketInfo(t *testing.T) { bucket, ok = indexer.Bucket(bucket.Index) r.True(ok) r.EqualValues(1, bucket.Index) - r.EqualValues(owner, bucket.Owner) + r.EqualValues(newOwner, bucket.Owner) r.EqualValues(delegate, bucket.Candidate) r.EqualValues(10, bucket.StakedAmount.Int64()) r.EqualValues(100, bucket.StakedDurationBlockNumber) r.EqualValues(height, bucket.StakeStartBlockHeight) r.False(bucket.AutoStake) - r.EqualValues(height-1, bucket.CreateBlockHeight) + r.EqualValues(createHeight, bucket.CreateBlockHeight) r.EqualValues(maxBlockNumber, bucket.UnstakeStartBlockHeight) r.EqualValues(_testStakingContractAddress, bucket.ContractAddress) r.EqualValues(10, indexer.CandidateVotes(delegate).Uint64()) @@ -340,13 +351,13 @@ func TestContractStakingIndexerBucketInfo(t *testing.T) { bucket, ok = indexer.Bucket(bucket.Index) r.True(ok) r.EqualValues(1, bucket.Index) - r.EqualValues(owner, bucket.Owner) + r.EqualValues(newOwner, bucket.Owner) r.EqualValues(delegate, bucket.Candidate) r.EqualValues(10, bucket.StakedAmount.Int64()) r.EqualValues(10, bucket.StakedDurationBlockNumber) - r.EqualValues(height-2, bucket.StakeStartBlockHeight) + r.EqualValues(createHeight, bucket.StakeStartBlockHeight) r.True(bucket.AutoStake) - r.EqualValues(height-2, bucket.CreateBlockHeight) + r.EqualValues(createHeight, bucket.CreateBlockHeight) r.EqualValues(maxBlockNumber, bucket.UnstakeStartBlockHeight) r.EqualValues(_testStakingContractAddress, bucket.ContractAddress) r.EqualValues(10, indexer.CandidateVotes(delegate).Uint64()) @@ -361,13 +372,13 @@ func TestContractStakingIndexerBucketInfo(t *testing.T) { bucket, ok = indexer.Bucket(bucket.Index) r.True(ok) r.EqualValues(1, bucket.Index) - r.EqualValues(owner, bucket.Owner) + r.EqualValues(newOwner, bucket.Owner) r.EqualValues(delegate, bucket.Candidate) r.EqualValues(10, bucket.StakedAmount.Int64()) r.EqualValues(10, bucket.StakedDurationBlockNumber) r.EqualValues(height, bucket.StakeStartBlockHeight) r.False(bucket.AutoStake) - r.EqualValues(height-3, bucket.CreateBlockHeight) + r.EqualValues(createHeight, bucket.CreateBlockHeight) r.EqualValues(height, bucket.UnstakeStartBlockHeight) r.EqualValues(_testStakingContractAddress, bucket.ContractAddress) r.EqualValues(0, indexer.CandidateVotes(delegate).Uint64()) @@ -611,3 +622,11 @@ func expandBucketType(r *require.Assertions, handler *contractStakingEventHandle }) r.NoError(err) } + +func transfer(r *require.Assertions, handler *contractStakingEventHandler, owner address.Address, token int64) { + err := handler.handleTransferEvent(eventParam{ + "to": common.BytesToAddress(owner.Bytes()), + "tokenId": big.NewInt(token), + }) + r.NoError(err) +} diff --git a/e2etest/contract_staking_test.go b/e2etest/contract_staking_test.go index 4d7e296986..2cc2b3b68d 100644 --- a/e2etest/contract_staking_test.go +++ b/e2etest/contract_staking_test.go @@ -1715,6 +1715,62 @@ func TestContractStaking(t *testing.T) { r.True(ok) r.EqualValues(identityset.Address(delegateIdx).String(), bt.Candidate.String()) }) + + t.Run("transfer token", func(t *testing.T) { + t.Run("transferFrom", func(t *testing.T) { + delegateIdx := 5 + bt := simpleStake(_delegates[delegateIdx], big.NewInt(10), big.NewInt(10)) + tokenID := bt.Index + r.EqualValues(identityset.Address(delegateIdx).String(), bt.Candidate.String()) + from := common.BytesToAddress(identityset.Address(_adminID).Bytes()) + newOwnerIdx := 25 + to := common.BytesToAddress(identityset.Address(newOwnerIdx).Bytes()) + data, err := lsdABI.Pack("transferFrom", from, to, big.NewInt(int64(tokenID))) + r.NoError(err) + param = callParam{ + contractAddr: contractAddresses, + bytecode: hex.EncodeToString(data), + amount: big.NewInt(0), + gasLimit: 1000000, + gasPrice: big.NewInt(0), + sk: identityset.PrivateKey(adminID), + } + receipts, _ = writeContract(bc, sf, dao, ap, []*callParam{¶m}, r) + r.Len(receipts, 1) + r.EqualValues("", receipts[0].ExecutionRevertMsg()) + r.EqualValues(iotextypes.ReceiptStatus_Success, receipts[0].Status) + bt, ok := indexer.Bucket(uint64(tokenID)) + r.True(ok) + r.EqualValues(identityset.Address(newOwnerIdx).String(), bt.Owner.String()) + }) + + t.Run("safeTransferFrom", func(t *testing.T) { + delegateIdx := 5 + bt := simpleStake(_delegates[delegateIdx], big.NewInt(10), big.NewInt(10)) + tokenID := bt.Index + r.EqualValues(identityset.Address(delegateIdx).String(), bt.Candidate.String()) + from := common.BytesToAddress(identityset.Address(_adminID).Bytes()) + newOwnerIdx := 25 + to := common.BytesToAddress(identityset.Address(newOwnerIdx).Bytes()) + data, err := lsdABI.Pack("safeTransferFrom", from, to, big.NewInt(int64(tokenID))) + r.NoError(err) + param = callParam{ + contractAddr: contractAddresses, + bytecode: hex.EncodeToString(data), + amount: big.NewInt(0), + gasLimit: 1000000, + gasPrice: big.NewInt(0), + sk: identityset.PrivateKey(adminID), + } + receipts, _ = writeContract(bc, sf, dao, ap, []*callParam{¶m}, r) + r.Len(receipts, 1) + r.EqualValues("", receipts[0].ExecutionRevertMsg()) + r.EqualValues(iotextypes.ReceiptStatus_Success, receipts[0].Status) + bt, ok := indexer.Bucket(uint64(tokenID)) + r.True(ok) + r.EqualValues(identityset.Address(newOwnerIdx).String(), bt.Owner.String()) + }) + }) } func prepareContractStakingBlockchain(ctx context.Context, cfg config.Config, r *require.Assertions) (blockchain.Blockchain, factory.Factory, blockdao.BlockDAO, actpool.ActPool, *contractstaking.Indexer) {