Skip to content

Commit

Permalink
Allow CW->ERC pointers to be called through wasmd precompile
Browse files Browse the repository at this point in the history
  • Loading branch information
codchen committed Jul 30, 2024
1 parent 9355f52 commit de1c3bf
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 9 deletions.
3 changes: 3 additions & 0 deletions app/receipt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@ func TestEvmEventsForCw20(t *testing.T) {
tx = txBuilder.GetTx()
txbz, err = testkeeper.EVMTestApp.GetTxConfig().TxEncoder()(tx)
require.Nil(t, err)
sum = sha256.Sum256(txbz)
res = testkeeper.EVMTestApp.DeliverTx(ctx.WithEventManager(sdk.NewEventManager()).WithTxIndex(1), abci.RequestDeliverTx{Tx: txbz}, tx, sum)
require.Equal(t, uint32(0), res.Code)
receipt, err = testkeeper.EVMTestApp.EvmKeeper.GetTransientReceipt(ctx, signedTx.Hash())
require.Nil(t, err)
fmt.Println(receipt.Logs)
require.Equal(t, 1, len(receipt.Logs))
require.NotEmpty(t, receipt.LogsBloom)
require.Equal(t, mockPointerAddr.Hex(), receipt.Logs[0].Address)
Expand Down Expand Up @@ -225,6 +227,7 @@ func TestEvmEventsForCw721(t *testing.T) {
tx = txBuilder.GetTx()
txbz, err = testkeeper.EVMTestApp.GetTxConfig().TxEncoder()(tx)
require.Nil(t, err)
sum = sha256.Sum256(txbz)
res = testkeeper.EVMTestApp.DeliverTx(ctx.WithEventManager(sdk.NewEventManager()).WithTxIndex(1), abci.RequestDeliverTx{Tx: txbz}, tx, sum)
require.Equal(t, uint32(0), res.Code)
receipt, err = testkeeper.EVMTestApp.EvmKeeper.GetTransientReceipt(ctx, signedTx.Hash())
Expand Down
33 changes: 33 additions & 0 deletions contracts/test/CW20toERC20PointerTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,39 @@ describe("CW20 to ERC20 Pointer", function () {
const balanceAfter = respAfter.data.balance;
expect(balanceAfter).to.equal((parseInt(balanceBefore) - 100).toString());
});

it("should transfer if called through wasmd precompile", async function() {
const WasmPrecompileContract = '0x0000000000000000000000000000000000001002';
const contractABIPath = '../../precompiles/wasmd/abi.json';
const contractABI = require(contractABIPath);
wasmd = new ethers.Contract(WasmPrecompileContract, contractABI, accounts[0].signer);

const respBefore = await queryWasm(pointer, "balance", {address: accounts[1].seiAddress});
const balanceBefore = respBefore.data.balance;

const encoder = new TextEncoder();

const transferMsg = { transfer: { recipient: accounts[1].seiAddress, amount: "100" } };
const transferStr = JSON.stringify(transferMsg);
const transferBz = encoder.encode(transferStr);

const coins = [];
const coinsStr = JSON.stringify(coins);
const coinsBz = encoder.encode(coinsStr);

const blockNumber = await ethers.provider.getBlockNumber();
const response = await wasmd.execute(pointer, transferBz, coinsBz);
const receipt = await response.wait();
expect(receipt.status).to.equal(1);
const filter = {
fromBlock: blockNumber,
toBlock: 'latest',
address: tokenAddr,
topics: [ethers.id("Transfer(address,address,uint256)")]
};
const logs = await ethers.provider.getLogs(filter);
expect(logs.length).to.equal(1);
});
});
});
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ require (
replace (
github.com/CosmWasm/wasmd => github.com/sei-protocol/sei-wasmd v0.2.3
github.com/confio/ics23/go => github.com/cosmos/cosmos-sdk/ics23/go v0.8.0
github.com/cosmos/cosmos-sdk => github.com/sei-protocol/sei-cosmos v0.3.29
github.com/cosmos/cosmos-sdk => github.com/sei-protocol/sei-cosmos v0.3.30
github.com/cosmos/iavl => github.com/sei-protocol/sei-iavl v0.1.9
github.com/cosmos/ibc-go/v3 => github.com/sei-protocol/sei-ibc-go/v3 v3.3.2
github.com/ethereum/go-ethereum => github.com/sei-protocol/go-ethereum v1.13.5-sei-22
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1347,8 +1347,8 @@ github.com/sei-protocol/go-ethereum v1.13.5-sei-22 h1:t/m1qXER+DEMrcpqgoYmUxifkA
github.com/sei-protocol/go-ethereum v1.13.5-sei-22/go.mod h1:kcRZmuzRn1lVejiFNTz4l4W7imnpq1bDAnuKS/RyhbQ=
github.com/sei-protocol/goutils v0.0.2 h1:Bfa7Sv+4CVLNM20QcpvGb81B8C5HkQC/kW1CQpIbXDA=
github.com/sei-protocol/goutils v0.0.2/go.mod h1:iYE2DuJfEnM+APPehr2gOUXfuLuPsVxorcDO+Tzq9q8=
github.com/sei-protocol/sei-cosmos v0.3.29 h1:QQUTsqtLq9kpXCDZ+KHzEJKzdkgT7YlmXcZ3ygGFWQw=
github.com/sei-protocol/sei-cosmos v0.3.29/go.mod h1:og/KbejR/zSQ8otapODEDU9zYNhFSUDbq9+tgeYePyU=
github.com/sei-protocol/sei-cosmos v0.3.30 h1:7yt7NV2pY5mUu45VShzLKKOJP7/Ns4HAtTcK85R8utk=
github.com/sei-protocol/sei-cosmos v0.3.30/go.mod h1:og/KbejR/zSQ8otapODEDU9zYNhFSUDbq9+tgeYePyU=
github.com/sei-protocol/sei-db v0.0.40 h1:s6B3u9u0r2Ypd67P8Lrz2IR/QU/FXwtS2X/fnYEix2g=
github.com/sei-protocol/sei-db v0.0.40/go.mod h1:F/ZKZA8HJPcUzSZPA8yt6pfwlGriJ4RDR4eHKSGLStI=
github.com/sei-protocol/sei-iavl v0.1.9 h1:y4mVYftxLNRs6533zl7N0/Ch+CzRQc04JDfHolIxgBE=
Expand Down
12 changes: 9 additions & 3 deletions precompiles/wasmd/wasmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const (

const WasmdAddress = "0x0000000000000000000000000000000000001002"

var Address = common.HexToAddress(WasmdAddress)

// Embed abi json file to the executable binary. Needed when importing as dependency.
//
//go:embed abi.json
Expand All @@ -51,15 +53,19 @@ type ExecuteMsg struct {
Coins []byte `json:"coins"`
}

func GetABI() abi.ABI {
return pcommon.MustGetABI(f, "abi.json")
}

func NewPrecompile(evmKeeper pcommon.EVMKeeper, wasmdKeeper pcommon.WasmdKeeper, wasmdViewKeeper pcommon.WasmdViewKeeper, bankKeeper pcommon.BankKeeper) (*pcommon.DynamicGasPrecompile, error) {
newAbi := pcommon.MustGetABI(f, "abi.json")
newAbi := GetABI()

executor := &PrecompileExecutor{
wasmdKeeper: wasmdKeeper,
wasmdViewKeeper: wasmdViewKeeper,
evmKeeper: evmKeeper,
bankKeeper: bankKeeper,
address: common.HexToAddress(WasmdAddress),
address: Address,
}

for name, m := range newAbi.Methods {
Expand All @@ -74,7 +80,7 @@ func NewPrecompile(evmKeeper pcommon.EVMKeeper, wasmdKeeper pcommon.WasmdKeeper,
executor.QueryID = m.ID
}
}
return pcommon.NewDynamicGasPrecompile(newAbi, executor, common.HexToAddress(WasmdAddress), "wasmd"), nil
return pcommon.NewDynamicGasPrecompile(newAbi, executor, Address, "wasmd"), nil
}

func (p PrecompileExecutor) Execute(ctx sdk.Context, method *abi.Method, caller common.Address, callingContract common.Address, args []interface{}, value *big.Int, readOnly bool, evm *vm.EVM, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
Expand Down
1 change: 1 addition & 0 deletions precompiles/wasmd/wasmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func TestExecute(t *testing.T) {
testApp.BankKeeper.SendCoins(ctx, mockAddr, testApp.EvmKeeper.GetSeiAddressOrDefault(ctx, common.HexToAddress(wasmd.WasmdAddress)), amts)
// circular interop
statedb.WithCtx(statedb.Ctx().WithIsEVM(false))
testApp.EvmKeeper.SetCode(statedb.Ctx(), mockEVMAddr, []byte{1, 2, 3})
res, _, err := p.RunAndCalculateGas(&evm, mockEVMAddr, mockEVMAddr, append(p.GetExecutor().(*wasmd.PrecompileExecutor).ExecuteID, args...), suppliedGas, big.NewInt(1000_000_000_000_000), nil, false)
require.Equal(t, "sei does not support CW->EVM->CW call pattern", string(res))
require.Equal(t, vm.ErrExecutionReverted, err)
Expand Down
21 changes: 19 additions & 2 deletions x/evm/keeper/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (k *Keeper) HandleInternalEVMDelegateCall(ctx sdk.Context, req *types.MsgIn
}

func (k *Keeper) CallEVM(ctx sdk.Context, from common.Address, to *common.Address, val *sdk.Int, data []byte) (retdata []byte, reterr error) {
if ctx.IsEVM() {
if ctx.IsEVM() && !ctx.EVMEntryViaWasmdPrecompile() {
return nil, errors.New("sei does not support EVM->CW->EVM call pattern")
}
if to == nil && len(data) > params.MaxInitCodeSize {
Expand All @@ -87,7 +87,7 @@ func (k *Keeper) CallEVM(ctx sdk.Context, from common.Address, to *common.Addres
value = val.BigInt()
}
// This call was not part of an existing StateTransition, so it should trigger one
executionCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx))
executionCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)).WithEVMEntryViaWasmdPrecompile(false)
stateDB := state.NewDBImpl(executionCtx, k, false)
gp := k.GetGasPool()
evmMsg := &core.Message{
Expand Down Expand Up @@ -118,6 +118,23 @@ func (k *Keeper) CallEVM(ctx sdk.Context, from common.Address, to *common.Addres
if res.Err != nil {
vmErr = res.Err.Error()
}
existingReceipt, err := k.GetTransientReceipt(ctx, ctx.TxSum())
if err == nil {
for _, l := range existingReceipt.Logs {
stateDB.AddLog(&ethtypes.Log{
Address: common.HexToAddress(l.Address),
Topics: utils.Map(l.Topics, common.HexToHash),
Data: l.Data,
})
}
if existingReceipt.VmError != "" {
vmErr = fmt.Sprintf("%s\n%s\n", existingReceipt.VmError, vmErr)
}
}
existingDeferredInfo, found := k.GetEVMTxDeferredInfo(ctx)
if found {
surplus = surplus.Add(existingDeferredInfo.Surplus)
}
receipt, err := k.WriteReceipt(ctx, stateDB, evmMsg, ethtypes.LegacyTxType, ctx.TxSum(), res.UsedGas, vmErr)
if err != nil {
return nil, err
Expand Down
30 changes: 29 additions & 1 deletion x/evm/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"github.com/ethereum/go-ethereum/core"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/sei-protocol/sei-chain/precompiles/wasmd"
"github.com/sei-protocol/sei-chain/utils"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/erc20"
"github.com/sei-protocol/sei-chain/x/evm/artifacts/erc721"
"github.com/sei-protocol/sei-chain/x/evm/state"
Expand All @@ -45,6 +47,11 @@ func (server msgServer) EVMTransaction(goCtx context.Context, msg *types.MsgEVMT
return &types.MsgEVMTransactionResponse{}, nil
}
ctx := sdk.UnwrapSDKContext(goCtx)
tx, _ := msg.AsTransaction()
isWasmdPrecompileCall := tx.To() != nil && (tx.To().Cmp(wasmd.Address) == 0)
if isWasmdPrecompileCall {
ctx = ctx.WithEVMEntryViaWasmdPrecompile(true)
}
// EVM has a special case here, mainly because for an EVM transaction the gas limit is set on EVM payload level, not on top-level GasWanted field
// as normal transactions (because existing eth client can't). As a result EVM has its own dedicated ante handler chain. The full sequence is:

Expand All @@ -56,7 +63,6 @@ func (server msgServer) EVMTransaction(goCtx context.Context, msg *types.MsgEVMT
ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx))

stateDB := state.NewDBImpl(ctx, &server, false)
tx, _ := msg.AsTransaction()
emsg := server.GetEVMMessage(ctx, tx, msg.Derived.SenderEVMAddr)
gp := server.GetGasPool()

Expand All @@ -81,6 +87,27 @@ func (server msgServer) EVMTransaction(goCtx context.Context, msg *types.MsgEVMT
)
return
}
extraSurplus := sdk.ZeroInt()
if isWasmdPrecompileCall {
syntheticReceipt, err := server.GetTransientReceipt(ctx, ctx.TxSum())
if err == nil {
for _, l := range syntheticReceipt.Logs {
stateDB.AddLog(&ethtypes.Log{
Address: common.HexToAddress(l.Address),
Topics: utils.Map(l.Topics, common.HexToHash),
Data: l.Data,
})
}
if syntheticReceipt.VmError != "" {
serverRes.VmError = fmt.Sprintf("%s\n%s\n", serverRes.VmError, syntheticReceipt.VmError)
}
server.DeleteTransientReceipt(ctx, ctx.TxSum())
}
syntheticDeferredInfo, found := server.GetEVMTxDeferredInfo(ctx)
if found {
extraSurplus = extraSurplus.Add(syntheticDeferredInfo.Surplus)
}
}
receipt, rerr := server.WriteReceipt(ctx, stateDB, emsg, uint32(tx.Type()), tx.Hash(), serverRes.GasUsed, serverRes.VmError)
if rerr != nil {
err = rerr
Expand Down Expand Up @@ -117,6 +144,7 @@ func (server msgServer) EVMTransaction(goCtx context.Context, msg *types.MsgEVMT
)
return
}
surplus = surplus.Add(extraSurplus)
bloom := ethtypes.Bloom{}
bloom.SetBytes(receipt.LogsBloom)
server.AppendToEvmTxDeferredInfo(ctx, bloom, tx.Hash(), surplus)
Expand Down
5 changes: 5 additions & 0 deletions x/evm/keeper/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ func (k *Keeper) GetTransientReceipt(ctx sdk.Context, txHash common.Hash) (*type
return r, nil
}

func (k *Keeper) DeleteTransientReceipt(ctx sdk.Context, txHash common.Hash) {
store := ctx.TransientStore(k.transientStoreKey)
store.Delete(types.ReceiptKey(txHash))
}

// GetReceipt returns a data structure that stores EVM specific transaction metadata.
// Many EVM applications (e.g. MetaMask) relies on being on able to query receipt
// by EVM transaction hash (not Sei transaction hash) to function properly.
Expand Down

0 comments on commit de1c3bf

Please sign in to comment.