Skip to content

Commit

Permalink
Use owner event to populate ERC721 transfer topic (#1932)
Browse files Browse the repository at this point in the history
* Use owner event to populate ERC721 transfer topic

* more test

* rebase and revert owner replacement
  • Loading branch information
codchen authored Nov 20, 2024
1 parent d5a2f2d commit dddd24b
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 13 deletions.
32 changes: 29 additions & 3 deletions app/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func (app *App) AddCosmosEventsToEVMReceiptIfApplicable(ctx sdk.Context, tx sdk.
// check if there is a ERC721 pointer to contract Addr
pointerAddr, _, exists = app.EvmKeeper.GetERC721CW721Pointer(wasmToEvmEventCtx, contractAddr)
if exists {
log, eligible := app.translateCW721Event(wasmToEvmEventCtx, wasmEvent, pointerAddr, contractAddr)
log, eligible := app.translateCW721Event(wasmToEvmEventCtx, wasmEvent, pointerAddr, contractAddr, response)
if eligible {
log.Index = uint(len(logs))
logs = append(logs, log)
Expand Down Expand Up @@ -173,7 +173,7 @@ func (app *App) translateCW20Event(ctx sdk.Context, wasmEvent abci.Event, pointe
return nil, false
}

func (app *App) translateCW721Event(ctx sdk.Context, wasmEvent abci.Event, pointerAddr common.Address, contractAddr string) (*ethtypes.Log, bool) {
func (app *App) translateCW721Event(ctx sdk.Context, wasmEvent abci.Event, pointerAddr common.Address, contractAddr string, response sdk.DeliverTxHookInput) (*ethtypes.Log, bool) {
action, found := GetAttributeValue(wasmEvent, "action")
if !found {
return nil, false
Expand All @@ -185,9 +185,35 @@ func (app *App) translateCW721Event(ctx sdk.Context, wasmEvent abci.Event, point
if tokenID == nil {
return nil, false
}
sender := common.Hash{}
// unfortunately CW721 transfer events differ from ERC721 transfer events
// in that CW721 include sender (which can be different than owner) whereas
// ERC721 always include owner. The following logic refer to the owner
// event emitted before the transfer and use that instead to populate the
// synthetic ERC721 event.
ownerEvents := GetEventsOfType(response, wasmtypes.EventTypeCW721PreTransferOwner)
for _, ownerEvent := range ownerEvents {
if len(ownerEvent.Attributes) != 3 ||
string(ownerEvent.Attributes[0].Key) != wasmtypes.AttributeKeyContractAddr ||
string(ownerEvent.Attributes[0].Value) != contractAddr {
continue
}
tokenIDStr, _ := GetAttributeValue(wasmEvent, "token_id")
if string(ownerEvent.Attributes[1].Key) != wasmtypes.AttributeKeyTokenId ||
string(ownerEvent.Attributes[1].Value) != tokenIDStr ||
string(ownerEvent.Attributes[2].Key) != wasmtypes.AttributeKeyOwner {
continue
}
ownerAcc, err := sdk.AccAddressFromBech32(string(ownerEvent.Attributes[2].Value))
if err != nil {
continue
}
owner := app.EvmKeeper.GetEVMAddressOrDefault(ctx, ownerAcc)
sender = common.BytesToHash(owner[:])
}
topics = []common.Hash{
ERC721TransferTopic,
app.GetEvmAddressAttribute(ctx, wasmEvent, "sender"),
sender,
app.GetEvmAddressAttribute(ctx, wasmEvent, "recipient"),
common.BigToHash(tokenID),
}
Expand Down
75 changes: 65 additions & 10 deletions app/receipt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ func TestEvmEventsForCw721(t *testing.T) {
amt := sdk.NewCoins(sdk.NewCoin("usei", sdk.NewInt(1000000000000)))
k.BankKeeper().MintCoins(ctx, "evm", amt)
k.BankKeeper().SendCoinsFromModuleToAccount(ctx, "evm", creator, amt)
recipient, _ := testkeeper.MockAddressPair()
privKeyRecipient := testkeeper.MockPrivateKey()
recipient, _ := testkeeper.PrivateKeyToAddresses(privKeyRecipient)
payload := []byte(fmt.Sprintf("{\"mint\":{\"token_id\":\"1\",\"owner\":\"%s\"}}", recipient.String()))
msg := &wasmtypes.MsgExecuteContract{
Sender: creator.String(),
Expand Down Expand Up @@ -320,18 +321,20 @@ func TestEvmEventsForCw721(t *testing.T) {
require.True(t, found)
require.Equal(t, common.HexToHash("0x1").Bytes(), receipt.Logs[0].Data)

// revoke all
payload = []byte(fmt.Sprintf("{\"revoke_all\":{\"operator\":\"%s\"}}", recipient.String()))
// transfer on behalf
k.BankKeeper().MintCoins(ctx, "evm", amt)
k.BankKeeper().SendCoinsFromModuleToAccount(ctx, "evm", recipient, amt)
payload = []byte(fmt.Sprintf("{\"transfer_nft\":{\"token_id\":\"2\",\"recipient\":\"%s\"}}", recipient.String()))
msg = &wasmtypes.MsgExecuteContract{
Sender: creator.String(),
Sender: recipient.String(),
Contract: contractAddr.String(),
Msg: payload,
}
txBuilder = testkeeper.EVMTestApp.GetTxConfig().NewTxBuilder()
txBuilder.SetMsgs(msg)
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("usei", sdk.NewInt(1000000))))
txBuilder.SetGasLimit(300000)
tx = signTx(txBuilder, privKey, k.AccountKeeper().GetAccount(ctx, creator))
tx = signTx(txBuilder, privKeyRecipient, k.AccountKeeper().GetAccount(ctx, recipient))
txbz, err = testkeeper.EVMTestApp.GetTxConfig().TxEncoder()(tx)
require.Nil(t, err)
sum = sha256.Sum256(txbz)
Expand All @@ -342,12 +345,66 @@ func TestEvmEventsForCw721(t *testing.T) {
require.Equal(t, 1, len(receipt.Logs))
require.NotEmpty(t, receipt.LogsBloom)
require.Equal(t, mockPointerAddr.Hex(), receipt.Logs[0].Address)
_, found = testkeeper.EVMTestApp.EvmKeeper.GetEVMTxDeferredInfo(ctx)
require.True(t, found)
require.Equal(t, uint32(0), receipt.Logs[0].Index)
ownerHash := receipt.Logs[0].Topics[1]
// make sure that the owner is set correctly to the creator, not the spender.
creatorEvmAddr := testkeeper.EVMTestApp.EvmKeeper.GetEVMAddressOrDefault(ctx, creator)
require.Equal(t, common.BytesToHash(creatorEvmAddr[:]).Hex(), ownerHash)
tokenIdHash = receipt.Logs[0].Topics[3]
require.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000002", tokenIdHash)
require.Equal(t, common.HexToHash("0x0").Bytes(), receipt.Logs[0].Data)

// burn
// transfer back
payload = []byte(fmt.Sprintf("{\"transfer_nft\":{\"token_id\":\"2\",\"recipient\":\"%s\"}}", creator.String()))
msg = &wasmtypes.MsgExecuteContract{
Sender: recipient.String(),
Contract: contractAddr.String(),
Msg: payload,
}
txBuilder = testkeeper.EVMTestApp.GetTxConfig().NewTxBuilder()
txBuilder.SetMsgs(msg)
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("usei", sdk.NewInt(1000000))))
txBuilder.SetGasLimit(300000)
tx = signTx(txBuilder, privKeyRecipient, k.AccountKeeper().GetAccount(ctx, recipient))
txbz, err = testkeeper.EVMTestApp.GetTxConfig().TxEncoder()(tx)
require.Nil(t, err)
sum = sha256.Sum256(txbz)
res = testkeeper.EVMTestApp.DeliverTx(ctx.WithEventManager(sdk.NewEventManager()), abci.RequestDeliverTx{Tx: txbz}, tx, sum)
require.Equal(t, uint32(0), res.Code)

// burn on behalf
payload = []byte("{\"burn\":{\"token_id\":\"2\"}}")
msg = &wasmtypes.MsgExecuteContract{
Sender: recipient.String(),
Contract: contractAddr.String(),
Msg: payload,
}
txBuilder = testkeeper.EVMTestApp.GetTxConfig().NewTxBuilder()
txBuilder.SetMsgs(msg)
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("usei", sdk.NewInt(1000000))))
txBuilder.SetGasLimit(300000)
tx = signTx(txBuilder, privKeyRecipient, k.AccountKeeper().GetAccount(ctx, recipient))
txbz, err = testkeeper.EVMTestApp.GetTxConfig().TxEncoder()(tx)
require.Nil(t, err)
sum = sha256.Sum256(txbz)
res = testkeeper.EVMTestApp.DeliverTx(ctx.WithEventManager(sdk.NewEventManager()), abci.RequestDeliverTx{Tx: txbz}, tx, sum)
require.Equal(t, uint32(0), res.Code)
receipt, err = testkeeper.EVMTestApp.EvmKeeper.GetTransientReceipt(ctx, common.BytesToHash(sum[:]))
require.Nil(t, err)
require.Equal(t, 1, len(receipt.Logs))
require.NotEmpty(t, receipt.LogsBloom)
require.Equal(t, mockPointerAddr.Hex(), receipt.Logs[0].Address)
require.Equal(t, uint32(0), receipt.Logs[0].Index)
ownerHash = receipt.Logs[0].Topics[1]
// make sure that the owner is set correctly to the creator, not the spender.
creatorEvmAddr = testkeeper.EVMTestApp.EvmKeeper.GetEVMAddressOrDefault(ctx, creator)
require.Equal(t, common.BytesToHash(creatorEvmAddr[:]).Hex(), ownerHash)
tokenIdHash = receipt.Logs[0].Topics[3]
require.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000002", tokenIdHash)
require.Equal(t, common.HexToHash("0x0").Bytes(), receipt.Logs[0].Data)

// revoke all
payload = []byte(fmt.Sprintf("{\"revoke_all\":{\"operator\":\"%s\"}}", recipient.String()))
msg = &wasmtypes.MsgExecuteContract{
Sender: creator.String(),
Contract: contractAddr.String(),
Expand All @@ -370,8 +427,6 @@ func TestEvmEventsForCw721(t *testing.T) {
require.Equal(t, mockPointerAddr.Hex(), receipt.Logs[0].Address)
_, found = testkeeper.EVMTestApp.EvmKeeper.GetEVMTxDeferredInfo(ctx)
require.True(t, found)
tokenIdHash = receipt.Logs[0].Topics[3]
require.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000002", tokenIdHash)
require.Equal(t, common.HexToHash("0x0").Bytes(), receipt.Logs[0].Data)
}

Expand Down

0 comments on commit dddd24b

Please sign in to comment.