diff --git a/app/receipt.go b/app/receipt.go index 216287c68b..356744dbcb 100644 --- a/app/receipt.go +++ b/app/receipt.go @@ -42,56 +42,10 @@ func (app *App) AddCosmosEventsToEVMReceiptIfApplicable(ctx sdk.Context, tx sdk. // check if there is a ERC20 pointer to contractAddr pointerAddr, _, exists := app.EvmKeeper.GetERC20CW20Pointer(ctx, contractAddr) if exists { - action, found := GetAttributeValue(wasmEvent, "action") - if !found { - continue - } - var topics []common.Hash - switch action { - case "mint", "burn", "send", "transfer", "transfer_from", "send_from", "burn_from": - topics = []common.Hash{ - ERC20TransferTopic, - app.GetEvmAddressAttribute(ctx, wasmEvent, "from"), - app.GetEvmAddressAttribute(ctx, wasmEvent, "to"), - } - logs = append(logs, ðtypes.Log{ - Address: pointerAddr, - Index: uint(len(logs)), - Topics: topics, - Data: common.BigToHash(GetAmountAttribute(wasmEvent)).Bytes(), - }) - case "increase_allowance", "decrease_allowance": - ownerStr, found := GetAttributeValue(wasmEvent, "owner") - if !found { - continue - } - spenderStr, found := GetAttributeValue(wasmEvent, "spender") - if !found { - continue - } - topics := []common.Hash{ - ERC20ApprovalTopic, - app.GetEvmAddressAttribute(ctx, wasmEvent, "owner"), - app.GetEvmAddressAttribute(ctx, wasmEvent, "spender"), - } - res, err := app.WasmKeeper.QuerySmart( - ctx, - sdk.MustAccAddressFromBech32(contractAddr), - []byte(fmt.Sprintf("{\"allowance\":{\"owner\":\"%s\",\"spender\":\"%s\"}}", ownerStr, spenderStr)), - ) - if err != nil { - continue - } - allowanceResponse := &AllowanceResponse{} - if err := json.Unmarshal(res, allowanceResponse); err != nil { - continue - } - logs = append(logs, ðtypes.Log{ - Address: pointerAddr, - Index: uint(len(logs)), - Topics: topics, - Data: common.BigToHash(allowanceResponse.Allowance.BigInt()).Bytes(), - }) + log, eligible := app.translateCW20Event(ctx, wasmEvent, pointerAddr, contractAddr) + if eligible { + log.Index = uint(len(logs)) + logs = append(logs, log) } continue } @@ -128,13 +82,70 @@ func (app *App) AddCosmosEventsToEVMReceiptIfApplicable(ctx sdk.Context, tx sdk. } _ = app.EvmKeeper.SetTransientReceipt(ctx, txHash, receipt) } - if d := app.EvmKeeper.GetEVMTxDeferredInfo(ctx); d != nil { + if d, found := app.EvmKeeper.GetEVMTxDeferredInfo(ctx); found { app.EvmKeeper.AppendToEvmTxDeferredInfo(ctx, bloom, txHash, d.Surplus) } else { app.EvmKeeper.AppendToEvmTxDeferredInfo(ctx, bloom, txHash, sdk.ZeroInt()) } } +func (app *App) translateCW20Event(ctx sdk.Context, wasmEvent abci.Event, pointerAddr common.Address, contractAddr string) (*ethtypes.Log, bool) { + action, found := GetAttributeValue(wasmEvent, "action") + if !found { + return nil, false + } + var topics []common.Hash + switch action { + case "mint", "burn", "send", "transfer", "transfer_from", "send_from", "burn_from": + topics = []common.Hash{ + ERC20TransferTopic, + app.GetEvmAddressAttribute(ctx, wasmEvent, "from"), + app.GetEvmAddressAttribute(ctx, wasmEvent, "to"), + } + amount, found := GetAmountAttribute(wasmEvent) + if !found { + return nil, false + } + return ðtypes.Log{ + Address: pointerAddr, + Topics: topics, + Data: common.BigToHash(amount).Bytes(), + }, true + case "increase_allowance", "decrease_allowance": + ownerStr, found := GetAttributeValue(wasmEvent, "owner") + if !found { + return nil, false + } + spenderStr, found := GetAttributeValue(wasmEvent, "spender") + if !found { + return nil, false + } + topics := []common.Hash{ + ERC20ApprovalTopic, + app.GetEvmAddressAttribute(ctx, wasmEvent, "owner"), + app.GetEvmAddressAttribute(ctx, wasmEvent, "spender"), + } + res, err := app.WasmKeeper.QuerySmart( + ctx, + sdk.MustAccAddressFromBech32(contractAddr), + []byte(fmt.Sprintf("{\"allowance\":{\"owner\":\"%s\",\"spender\":\"%s\"}}", ownerStr, spenderStr)), + ) + if err != nil { + return nil, false + } + allowanceResponse := &AllowanceResponse{} + if err := json.Unmarshal(res, allowanceResponse); err != nil { + return nil, false + } + return ðtypes.Log{ + Address: pointerAddr, + Topics: topics, + Data: common.BigToHash(allowanceResponse.Allowance.BigInt()).Bytes(), + }, true + } + return nil, false +} + func (app *App) GetEvmAddressAttribute(ctx sdk.Context, event abci.Event, attribute string) common.Hash { addrStr, found := GetAttributeValue(event, attribute) if found { @@ -165,13 +176,13 @@ func GetAttributeValue(event abci.Event, attribute string) (string, bool) { return "", false } -func GetAmountAttribute(event abci.Event) *big.Int { +func GetAmountAttribute(event abci.Event) (*big.Int, bool) { amount, found := GetAttributeValue(event, "amount") if found { amountInt, ok := sdk.NewIntFromString(amount) if ok { - return amountInt.BigInt() + return amountInt.BigInt(), true } } - return big.NewInt(0) + return nil, false } diff --git a/app/receipt_test.go b/app/receipt_test.go index e36dfcdfde..2597dfb79d 100644 --- a/app/receipt_test.go +++ b/app/receipt_test.go @@ -70,7 +70,8 @@ func TestEvmEventsForCw20(t *testing.T) { require.Equal(t, 1, len(receipt.Logs)) require.NotEmpty(t, receipt.LogsBloom) require.Equal(t, mockPointerAddr.Hex(), receipt.Logs[0].Address) - require.NotNil(t, testkeeper.EVMTestApp.EvmKeeper.GetEVMTxDeferredInfo(ctx)) + _, found := testkeeper.EVMTestApp.EvmKeeper.GetEVMTxDeferredInfo(ctx) + require.True(t, found) // calling from wasmd precompile abi, err := wasmd.GetABI() @@ -112,7 +113,8 @@ func TestEvmEventsForCw20(t *testing.T) { require.Equal(t, 1, len(receipt.Logs)) require.NotEmpty(t, receipt.LogsBloom) require.Equal(t, mockPointerAddr.Hex(), receipt.Logs[0].Address) - require.NotNil(t, testkeeper.EVMTestApp.EvmKeeper.GetEVMTxDeferredInfo(ctx)) + _, found = testkeeper.EVMTestApp.EvmKeeper.GetEVMTxDeferredInfo(ctx) + require.True(t, found) // test approval message payload = []byte(fmt.Sprintf("{\"increase_allowance\":{\"spender\":\"%s\",\"amount\":\"100\"}}", recipient.String())) @@ -136,7 +138,8 @@ func TestEvmEventsForCw20(t *testing.T) { require.Equal(t, 1, len(receipt.Logs)) require.NotEmpty(t, receipt.LogsBloom) require.Equal(t, mockPointerAddr.Hex(), receipt.Logs[0].Address) - require.NotNil(t, testkeeper.EVMTestApp.EvmKeeper.GetEVMTxDeferredInfo(ctx)) + _, found = testkeeper.EVMTestApp.EvmKeeper.GetEVMTxDeferredInfo(ctx) + require.True(t, found) require.Equal(t, common.HexToHash("0x64").Bytes(), receipt.Logs[0].Data) } diff --git a/contracts/test/ERC20toCW20PointerTest.js b/contracts/test/ERC20toCW20PointerTest.js index 8dce53130d..7f6e84b6e2 100644 --- a/contracts/test/ERC20toCW20PointerTest.js +++ b/contracts/test/ERC20toCW20PointerTest.js @@ -109,6 +109,10 @@ describe("ERC20 to CW20 Pointer", function () { }; const logs = await ethers.provider.getLogs(filter); expect(logs.length).to.equal(1); + expect(logs[0]["address"]).to.equal(await pointer.getAddress()); + expect(logs[0]["topics"][0]).to.equal(ethers.id("Transfer(address,address,uint256)")); + expect(logs[0]["topics"][1].substring(26)).to.equal(sender.evmAddress.substring(2).toLowerCase()); + expect(logs[0]["topics"][2].substring(26)).to.equal(recipient.evmAddress.substring(2).toLowerCase()); const cleanupTx = await pointer.connect(recipient.signer).transfer(sender.evmAddress, 1); await cleanupTx.wait(); @@ -150,6 +154,10 @@ describe("ERC20 to CW20 Pointer", function () { }; const logs = await ethers.provider.getLogs(filter); expect(logs.length).to.equal(1); + expect(logs[0]["address"]).to.equal(await pointer.getAddress()); + expect(logs[0]["topics"][0]).to.equal(ethers.id("Approval(address,address,uint256)")); + expect(logs[0]["topics"][1].substring(26)).to.equal(owner.substring(2).toLowerCase()); + expect(logs[0]["topics"][2].substring(26)).to.equal(spender.substring(2).toLowerCase()); }); it("should lower approval", async function () { diff --git a/x/evm/artifacts/README b/x/evm/artifacts/README index 2f32d818d7..2972e92bef 100644 --- a/x/evm/artifacts/README +++ b/x/evm/artifacts/README @@ -1,5 +1,5 @@ The source files are under contracts/src. The artifacts should be updated whenever the source files change. To update run the following (with NativeSeiTokensERC20 as an example): -- `solc --overwrite @openzeppelin=contracts/lib/openzeppelin-contracts --bin -o x/evm/artifacts/cw20 contracts/src/CW721ERC721Pointer.sol` +- `solc --overwrite @openzeppelin=contracts/lib/openzeppelin-contracts --bin -o x/evm/artifacts/cw721 contracts/src/CW721ERC721Pointer.sol` - `solc --overwrite @openzeppelin=contracts/lib/openzeppelin-contracts --abi -o x/evm/artifacts/cw721 contracts/src/CW721ERC721Pointer.sol` - (clean up any artifact that is not CW721ERC721Pointer.bin/abi) - `abigen --abi=x/evm/artifacts/cw721/CW721ERC721Pointer.abi --pkg=cw721 --out=x/evm/artifacts/cw721/cw721.go` \ No newline at end of file diff --git a/x/evm/keeper/deferred.go b/x/evm/keeper/deferred.go index 477b6a57e0..8d8aeb0885 100644 --- a/x/evm/keeper/deferred.go +++ b/x/evm/keeper/deferred.go @@ -64,14 +64,17 @@ func (k *Keeper) AppendToEvmTxDeferredInfo(ctx sdk.Context, bloom ethtypes.Bloom prefix.NewStore(ctx.TransientStore(k.transientStoreKey), types.DeferredInfoPrefix).Set(key, bz) } -func (k *Keeper) GetEVMTxDeferredInfo(ctx sdk.Context) *types.DeferredInfo { +func (k *Keeper) GetEVMTxDeferredInfo(ctx sdk.Context) (*types.DeferredInfo, bool) { key := make([]byte, 8) binary.BigEndian.PutUint64(key, uint64(ctx.TxIndex())) val := &types.DeferredInfo{} bz := prefix.NewStore(ctx.TransientStore(k.transientStoreKey), types.DeferredInfoPrefix).Get(key) if bz == nil { - return nil + return nil, false } - _ = val.Unmarshal(bz) - return val + if err := val.Unmarshal(bz); err != nil { + ctx.Logger().Error(fmt.Sprintf("failed to unmarshal EVM deferred info: %s", err)) + return nil, false + } + return val, true }