diff --git a/changelog.md b/changelog.md index 179367a8fe..b17e5d66b2 100644 --- a/changelog.md +++ b/changelog.md @@ -20,11 +20,11 @@ * [3426](https://github.com/zeta-chain/node/pull/3426) - use protocol contracts V2 with Bitcoin deposits * [3326](https://github.com/zeta-chain/node/pull/3326) - improve error messages for cctx status object * [3418](https://github.com/zeta-chain/node/pull/3418) - orchestrator V2: TON observer-signer +* [3432](https://github.com/zeta-chain/node/pull/3432) - use protocol contracts V2 with Solana deposits * [3438](https://github.com/zeta-chain/node/pull/3438) - orchestrator V2: SOl observer-signer. Drop V1. * [3440](https://github.com/zeta-chain/node/pull/3440) - remove unused method `FilterSolanaInboundEvents` * [3428](https://github.com/zeta-chain/node/pull/3428) - zetaclient: converge EVM clients. - ### Fixes * [3374](https://github.com/zeta-chain/node/pull/3374) - remove minimum rent exempt check for SPL token withdrawals diff --git a/e2e/e2etests/test_solana_deposit_refund.go b/e2e/e2etests/test_solana_deposit_and_call_revert.go similarity index 100% rename from e2e/e2etests/test_solana_deposit_refund.go rename to e2e/e2etests/test_solana_deposit_and_call_revert.go diff --git a/pkg/contracts/solana/inbound.go b/pkg/contracts/solana/inbound.go index 1ab4543694..999674f87b 100644 --- a/pkg/contracts/solana/inbound.go +++ b/pkg/contracts/solana/inbound.go @@ -3,6 +3,7 @@ package solana import ( "fmt" + ethcommon "github.com/ethereum/go-ethereum/common" "github.com/gagliardetto/solana-go" "github.com/near/borsh-go" ) @@ -12,12 +13,15 @@ const ( MaxSignaturesPerTicker = 100 ) +// Deposit represents a deposit instruction from a Solana transaction to ZetaChain type Deposit struct { - Sender string - Amount uint64 - Memo []byte - Slot uint64 - Asset string + Sender string + Receiver string + Amount uint64 + Memo []byte + Slot uint64 + Asset string + IsCrossChainCall bool } // ParseInboundAsDeposit tries to parse an instruction as a 'deposit' or 'deposit_and_call'. @@ -63,12 +67,19 @@ func tryParseAsDeposit( return nil, err } + receiver, err := parseReceiver(inst.Receiver) + if err != nil { + return nil, err + } + return &Deposit{ - Sender: sender, - Amount: inst.Amount, - Memo: inst.Receiver[:], - Slot: slot, - Asset: "", // no asset for gas token SOL + Sender: sender, + Receiver: receiver, + Amount: inst.Amount, + Memo: []byte{}, + Slot: slot, + Asset: "", // no asset for gas token SOL + IsCrossChainCall: false, }, nil } @@ -93,17 +104,24 @@ func tryParseAsDepositAndCall( return nil, nil } + receiver, err := parseReceiver(instDepositAndCall.Receiver) + if err != nil { + return nil, err + } + // get the sender address (skip if unable to parse signer address) sender, err := getSignerDeposit(tx, &instruction) if err != nil { return nil, err } return &Deposit{ - Sender: sender, - Amount: instDepositAndCall.Amount, - Memo: append(instDepositAndCall.Receiver[:], instDepositAndCall.Memo...), - Slot: slot, - Asset: "", // no asset for gas token SOL + Sender: sender, + Receiver: receiver, + Amount: instDepositAndCall.Amount, + Memo: instDepositAndCall.Memo, + Slot: slot, + Asset: "", // no asset for gas token SOL + IsCrossChainCall: true, }, nil } @@ -150,12 +168,19 @@ func tryParseAsDepositSPL( return nil, err } + receiver, err := parseReceiver(inst.Receiver) + if err != nil { + return nil, err + } + return &Deposit{ - Sender: sender, - Amount: inst.Amount, - Memo: inst.Receiver[:], - Slot: slot, - Asset: spl, + Sender: sender, + Receiver: receiver, + Amount: inst.Amount, + Memo: []byte{}, + Slot: slot, + Asset: spl, + IsCrossChainCall: false, }, nil } @@ -180,17 +205,24 @@ func tryParseAsDepositSPLAndCall( return nil, nil } + receiver, err := parseReceiver(instDepositAndCall.Receiver) + if err != nil { + return nil, err + } + // get the sender and spl addresses sender, spl, err := getSignerAndSPLFromDepositSPLAccounts(tx, &instruction) if err != nil { return nil, err } return &Deposit{ - Sender: sender, - Amount: instDepositAndCall.Amount, - Memo: append(instDepositAndCall.Receiver[:], instDepositAndCall.Memo...), - Slot: slot, - Asset: spl, + Sender: sender, + Receiver: receiver, + Amount: instDepositAndCall.Amount, + Memo: instDepositAndCall.Memo, + Slot: slot, + Asset: spl, + IsCrossChainCall: true, }, nil } @@ -244,3 +276,13 @@ func getSignerAndSPLFromDepositSPLAccounts( return signer, spl, nil } + +// parseReceiver parses the receiver bytes into a Ethereum address string +func parseReceiver(receiver [20]byte) (string, error) { + addr := ethcommon.BytesToAddress(receiver[:ethcommon.AddressLength]) + if addr == (ethcommon.Address{}) { + return "", fmt.Errorf("invalid receiver address: %v", receiver) + } + + return addr.Hex(), nil +} diff --git a/pkg/contracts/solana/inbound_test.go b/pkg/contracts/solana/inbound_test.go index bce251d977..a69183cde2 100644 --- a/pkg/contracts/solana/inbound_test.go +++ b/pkg/contracts/solana/inbound_test.go @@ -1,7 +1,6 @@ package solana import ( - "encoding/hex" "encoding/json" "fmt" "os" @@ -55,14 +54,15 @@ func Test_ParseInboundAsDeposit(t *testing.T) { // solana e2e deployer account sender := "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ" // solana e2e user evm account - expectedMemo, err := hex.DecodeString("103fd9224f00ce3013e95629e52dfc31d805d68d") require.NoError(t, err) expectedDeposit := &Deposit{ - Sender: sender, - Amount: 12000000, - Memo: expectedMemo, - Slot: txResult.Slot, - Asset: "", + Sender: sender, + Receiver: "0x103FD9224F00ce3013e95629e52DFc31D805D68d", + Amount: 12000000, + Memo: []byte{}, + Slot: txResult.Slot, + Asset: "", + IsCrossChainCall: false, } t.Run("should parse inbound event deposit SOL", func(t *testing.T) { @@ -159,36 +159,15 @@ func Test_ParseInboundAsDepositAndCall(t *testing.T) { // expected result // solana e2e deployer account sender := "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ" - // example contract deployed during e2e test, read from tx result - expectedReceiver := []byte{ - 117, - 160, - 106, - 140, - 37, - 135, - 57, - 218, - 223, - 226, - 53, - 45, - 87, - 151, - 61, - 239, - 158, - 231, - 162, - 186, - } expectedMsg := []byte("hello lamports") expectedDeposit := &Deposit{ - Sender: sender, - Amount: 1200000, - Memo: append(expectedReceiver, expectedMsg...), - Slot: txResult.Slot, - Asset: "", + Sender: sender, + Receiver: "0x75A06a8C258739dADfe2352D57973deF9ee7A2ba", + Amount: 1200000, + Memo: expectedMsg, + Slot: txResult.Slot, + Asset: "", + IsCrossChainCall: true, } t.Run("should parse inbound event deposit SOL and call", func(t *testing.T) { @@ -284,15 +263,15 @@ func Test_ParseInboundAsDepositSPL(t *testing.T) { // expected result // solana e2e deployer account sender := "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ" - // solana e2e user evm account - expectedMemo, err := hex.DecodeString("103fd9224f00ce3013e95629e52dfc31d805d68d") require.NoError(t, err) expectedDeposit := &Deposit{ - Sender: sender, - Amount: 12000000, - Memo: expectedMemo, - Slot: txResult.Slot, - Asset: "BTmtL9Dh2DcwhPntEbjo3rSWpmz1EhXsmohSC7CGSEWw", // SPL address + Sender: sender, + Receiver: "0x103FD9224F00ce3013e95629e52DFc31D805D68d", + Amount: 12000000, + Memo: []byte{}, + Slot: txResult.Slot, + Asset: "BTmtL9Dh2DcwhPntEbjo3rSWpmz1EhXsmohSC7CGSEWw", // SPL address + IsCrossChainCall: false, } t.Run("should parse inbound event deposit SPL", func(t *testing.T) { @@ -389,14 +368,15 @@ func Test_ParseInboundAsDepositAndCallSPL(t *testing.T) { // solana e2e deployer account sender := "37yGiHAnLvWZUNVwu9esp74YQFqxU1qHCbABkDvRddUQ" // example contract deployed during e2e test, read from tx result - expectedReceiver := []byte{213, 254, 240, 66, 1, 154, 250, 238, 39, 131, 9, 45, 5, 2, 190, 192, 20, 31, 103, 209} expectedMsg := []byte("hello spl tokens") expectedDeposit := &Deposit{ - Sender: sender, - Amount: 12000000, - Memo: append(expectedReceiver, expectedMsg...), - Slot: txResult.Slot, - Asset: "7d4ehzE4WNgithQZMyQFDhmHyN6rQNTEC7re1bsRN7TX", // SPL address + Sender: sender, + Receiver: "0xd5Fef042019aFAEe2783092d0502bEc0141f67D1", + Amount: 12000000, + Memo: expectedMsg, + Slot: txResult.Slot, + Asset: "7d4ehzE4WNgithQZMyQFDhmHyN6rQNTEC7re1bsRN7TX", // SPL address, + IsCrossChainCall: true, } t.Run("should parse inbound event deposit SPL", func(t *testing.T) { diff --git a/testutil/contracts/Reverter.abi b/testutil/contracts/Reverter.abi index 7183768beb..0f9feacdde 100644 --- a/testutil/contracts/Reverter.abi +++ b/testutil/contracts/Reverter.abi @@ -4,6 +4,51 @@ "name": "Foo", "type": "error" }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "origin", + "type": "bytes" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "chainID", + "type": "uint256" + } + ], + "internalType": "struct Reverter.zContext", + "name": "context", + "type": "tuple" + }, + { + "internalType": "address", + "name": "zrc20", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "onCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { diff --git a/testutil/contracts/Reverter.bin b/testutil/contracts/Reverter.bin index cc4c1254cb..0226dd7916 100644 --- a/testutil/contracts/Reverter.bin +++ b/testutil/contracts/Reverter.bin @@ -1 +1 @@ -6080604052348015600f57600080fd5b5061027f8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063de43156e14610030575b600080fd5b61004a600480360381019061004591906101a5565b61004c565b005b6040517fbfb4ebcf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080fd5b600080fd5b600080fd5b6000606082840312156100a3576100a2610088565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100d7826100ac565b9050919050565b6100e7816100cc565b81146100f257600080fd5b50565b600081359050610104816100de565b92915050565b6000819050919050565b61011d8161010a565b811461012857600080fd5b50565b60008135905061013a81610114565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261016557610164610140565b5b8235905067ffffffffffffffff81111561018257610181610145565b5b60208301915083600182028301111561019e5761019d61014a565b5b9250929050565b6000806000806000608086880312156101c1576101c061007e565b5b600086013567ffffffffffffffff8111156101df576101de610083565b5b6101eb8882890161008d565b95505060206101fc888289016100f5565b945050604061020d8882890161012b565b935050606086013567ffffffffffffffff81111561022e5761022d610083565b5b61023a8882890161014f565b9250925050929550929590935056fea2646970667358221220bc9e93b2d3f5e1969fc6b9d1d00d7fb28bdcb33a4e9f24aeb392acacc895146464736f6c634300081a0033 +6080604052348015600f57600080fd5b506102ba8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80635bcfd6161461003b578063de43156e14610057575b600080fd5b610055600480360381019061005091906101e0565b610073565b005b610071600480360381019061006c91906101e0565b6100a5565b005b6040517fbfb4ebcf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6100b28585858585610073565b5050505050565b600080fd5b600080fd5b600080fd5b6000606082840312156100de576100dd6100c3565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610112826100e7565b9050919050565b61012281610107565b811461012d57600080fd5b50565b60008135905061013f81610119565b92915050565b6000819050919050565b61015881610145565b811461016357600080fd5b50565b6000813590506101758161014f565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101a05761019f61017b565b5b8235905067ffffffffffffffff8111156101bd576101bc610180565b5b6020830191508360018202830111156101d9576101d8610185565b5b9250929050565b6000806000806000608086880312156101fc576101fb6100b9565b5b600086013567ffffffffffffffff81111561021a576102196100be565b5b610226888289016100c8565b955050602061023788828901610130565b945050604061024888828901610166565b935050606086013567ffffffffffffffff811115610269576102686100be565b5b6102758882890161018a565b9250925050929550929590935056fea2646970667358221220a7ad1881a453cbf7569a6a918894fa032e56cd977fe96c70d0fb9cf9c97d6bc264736f6c634300081a0033 diff --git a/testutil/contracts/Reverter.go b/testutil/contracts/Reverter.go index 806ebeed0f..7d9cb13bc3 100644 --- a/testutil/contracts/Reverter.go +++ b/testutil/contracts/Reverter.go @@ -38,8 +38,8 @@ type ReverterzContext struct { // ReverterMetaData contains all meta data concerning the Reverter contract. var ReverterMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"name\":\"Foo\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"origin\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainID\",\"type\":\"uint256\"}],\"internalType\":\"structReverter.zContext\",\"name\":\"context\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"zrc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"onCrossChainCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x6080604052348015600f57600080fd5b5061027f8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063de43156e14610030575b600080fd5b61004a600480360381019061004591906101a5565b61004c565b005b6040517fbfb4ebcf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080fd5b600080fd5b600080fd5b6000606082840312156100a3576100a2610088565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100d7826100ac565b9050919050565b6100e7816100cc565b81146100f257600080fd5b50565b600081359050610104816100de565b92915050565b6000819050919050565b61011d8161010a565b811461012857600080fd5b50565b60008135905061013a81610114565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261016557610164610140565b5b8235905067ffffffffffffffff81111561018257610181610145565b5b60208301915083600182028301111561019e5761019d61014a565b5b9250929050565b6000806000806000608086880312156101c1576101c061007e565b5b600086013567ffffffffffffffff8111156101df576101de610083565b5b6101eb8882890161008d565b95505060206101fc888289016100f5565b945050604061020d8882890161012b565b935050606086013567ffffffffffffffff81111561022e5761022d610083565b5b61023a8882890161014f565b9250925050929550929590935056fea2646970667358221220bc9e93b2d3f5e1969fc6b9d1d00d7fb28bdcb33a4e9f24aeb392acacc895146464736f6c634300081a0033", + ABI: "[{\"inputs\":[],\"name\":\"Foo\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"origin\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainID\",\"type\":\"uint256\"}],\"internalType\":\"structReverter.zContext\",\"name\":\"context\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"zrc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"onCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"origin\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"chainID\",\"type\":\"uint256\"}],\"internalType\":\"structReverter.zContext\",\"name\":\"context\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"zrc20\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"name\":\"onCrossChainCall\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x6080604052348015600f57600080fd5b506102ba8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80635bcfd6161461003b578063de43156e14610057575b600080fd5b610055600480360381019061005091906101e0565b610073565b005b610071600480360381019061006c91906101e0565b6100a5565b005b6040517fbfb4ebcf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6100b28585858585610073565b5050505050565b600080fd5b600080fd5b600080fd5b6000606082840312156100de576100dd6100c3565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610112826100e7565b9050919050565b61012281610107565b811461012d57600080fd5b50565b60008135905061013f81610119565b92915050565b6000819050919050565b61015881610145565b811461016357600080fd5b50565b6000813590506101758161014f565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101a05761019f61017b565b5b8235905067ffffffffffffffff8111156101bd576101bc610180565b5b6020830191508360018202830111156101d9576101d8610185565b5b9250929050565b6000806000806000608086880312156101fc576101fb6100b9565b5b600086013567ffffffffffffffff81111561021a576102196100be565b5b610226888289016100c8565b955050602061023788828901610130565b945050604061024888828901610166565b935050606086013567ffffffffffffffff811115610269576102686100be565b5b6102758882890161018a565b9250925050929550929590935056fea2646970667358221220a7ad1881a453cbf7569a6a918894fa032e56cd977fe96c70d0fb9cf9c97d6bc264736f6c634300081a0033", } // ReverterABI is the input ABI used to generate the binding from. @@ -209,6 +209,27 @@ func (_Reverter *ReverterTransactorRaw) Transact(opts *bind.TransactOpts, method return _Reverter.Contract.contract.Transact(opts, method, params...) } +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. +// +// Solidity: function onCall((bytes,address,uint256) context, address zrc20, uint256 amount, bytes message) returns() +func (_Reverter *ReverterTransactor) OnCall(opts *bind.TransactOpts, context ReverterzContext, zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { + return _Reverter.contract.Transact(opts, "onCall", context, zrc20, amount, message) +} + +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. +// +// Solidity: function onCall((bytes,address,uint256) context, address zrc20, uint256 amount, bytes message) returns() +func (_Reverter *ReverterSession) OnCall(context ReverterzContext, zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { + return _Reverter.Contract.OnCall(&_Reverter.TransactOpts, context, zrc20, amount, message) +} + +// OnCall is a paid mutator transaction binding the contract method 0x5bcfd616. +// +// Solidity: function onCall((bytes,address,uint256) context, address zrc20, uint256 amount, bytes message) returns() +func (_Reverter *ReverterTransactorSession) OnCall(context ReverterzContext, zrc20 common.Address, amount *big.Int, message []byte) (*types.Transaction, error) { + return _Reverter.Contract.OnCall(&_Reverter.TransactOpts, context, zrc20, amount, message) +} + // OnCrossChainCall is a paid mutator transaction binding the contract method 0xde43156e. // // Solidity: function onCrossChainCall((bytes,address,uint256) context, address zrc20, uint256 amount, bytes message) returns() diff --git a/testutil/contracts/Reverter.json b/testutil/contracts/Reverter.json index 2e935c52bb..181dc6f30c 100644 --- a/testutil/contracts/Reverter.json +++ b/testutil/contracts/Reverter.json @@ -5,6 +5,51 @@ "name": "Foo", "type": "error" }, + { + "inputs": [ + { + "components": [ + { + "internalType": "bytes", + "name": "origin", + "type": "bytes" + }, + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "chainID", + "type": "uint256" + } + ], + "internalType": "struct Reverter.zContext", + "name": "context", + "type": "tuple" + }, + { + "internalType": "address", + "name": "zrc20", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "message", + "type": "bytes" + } + ], + "name": "onCall", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -51,5 +96,5 @@ "type": "function" } ], - "bin": "6080604052348015600f57600080fd5b5061027f8061001f6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063de43156e14610030575b600080fd5b61004a600480360381019061004591906101a5565b61004c565b005b6040517fbfb4ebcf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080fd5b600080fd5b600080fd5b6000606082840312156100a3576100a2610088565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100d7826100ac565b9050919050565b6100e7816100cc565b81146100f257600080fd5b50565b600081359050610104816100de565b92915050565b6000819050919050565b61011d8161010a565b811461012857600080fd5b50565b60008135905061013a81610114565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261016557610164610140565b5b8235905067ffffffffffffffff81111561018257610181610145565b5b60208301915083600182028301111561019e5761019d61014a565b5b9250929050565b6000806000806000608086880312156101c1576101c061007e565b5b600086013567ffffffffffffffff8111156101df576101de610083565b5b6101eb8882890161008d565b95505060206101fc888289016100f5565b945050604061020d8882890161012b565b935050606086013567ffffffffffffffff81111561022e5761022d610083565b5b61023a8882890161014f565b9250925050929550929590935056fea2646970667358221220bc9e93b2d3f5e1969fc6b9d1d00d7fb28bdcb33a4e9f24aeb392acacc895146464736f6c634300081a0033" + "bin": "6080604052348015600f57600080fd5b506102ba8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80635bcfd6161461003b578063de43156e14610057575b600080fd5b610055600480360381019061005091906101e0565b610073565b005b610071600480360381019061006c91906101e0565b6100a5565b005b6040517fbfb4ebcf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6100b28585858585610073565b5050505050565b600080fd5b600080fd5b600080fd5b6000606082840312156100de576100dd6100c3565b5b81905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610112826100e7565b9050919050565b61012281610107565b811461012d57600080fd5b50565b60008135905061013f81610119565b92915050565b6000819050919050565b61015881610145565b811461016357600080fd5b50565b6000813590506101758161014f565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126101a05761019f61017b565b5b8235905067ffffffffffffffff8111156101bd576101bc610180565b5b6020830191508360018202830111156101d9576101d8610185565b5b9250929050565b6000806000806000608086880312156101fc576101fb6100b9565b5b600086013567ffffffffffffffff81111561021a576102196100be565b5b610226888289016100c8565b955050602061023788828901610130565b945050604061024888828901610166565b935050606086013567ffffffffffffffff811115610269576102686100be565b5b6102758882890161018a565b9250925050929550929590935056fea2646970667358221220a7ad1881a453cbf7569a6a918894fa032e56cd977fe96c70d0fb9cf9c97d6bc264736f6c634300081a0033" } diff --git a/testutil/contracts/Reverter.sol b/testutil/contracts/Reverter.sol index d84eaa0b30..131034ebf9 100644 --- a/testutil/contracts/Reverter.sol +++ b/testutil/contracts/Reverter.sol @@ -17,6 +17,15 @@ contract Reverter { uint256 amount, bytes calldata message ) external { + onCall(context, zrc20, amount, message); + } + + function onCall( + zContext calldata context, + address zrc20, + uint256 amount, + bytes calldata message + ) public { revert Foo(); } } \ No newline at end of file diff --git a/zetaclient/chains/solana/observer/inbound.go b/zetaclient/chains/solana/observer/inbound.go index 250e29f856..7d724a02f4 100644 --- a/zetaclient/chains/solana/observer/inbound.go +++ b/zetaclient/chains/solana/observer/inbound.go @@ -185,17 +185,18 @@ func FilterInboundEvents( } else if deposit != nil { seenDeposit = true events = append(events, &clienttypes.InboundEvent{ - SenderChainID: senderChainID, - Sender: deposit.Sender, - Receiver: "", // receiver will be pulled out from memo later - TxOrigin: deposit.Sender, - Amount: deposit.Amount, - Memo: deposit.Memo, - BlockNumber: deposit.Slot, // instead of using block, Solana explorer uses slot for indexing - TxHash: tx.Signatures[0].String(), - Index: 0, // hardcode to 0 for Solana, not a EVM smart contract call - CoinType: coin.CoinType_Gas, - Asset: deposit.Asset, + SenderChainID: senderChainID, + Sender: deposit.Sender, + Receiver: deposit.Receiver, + TxOrigin: deposit.Sender, + Amount: deposit.Amount, + Memo: deposit.Memo, + BlockNumber: deposit.Slot, // instead of using block, Solana explorer uses slot for indexing + TxHash: tx.Signatures[0].String(), + Index: 0, // hardcode to 0 for Solana, not a EVM smart contract call + CoinType: coin.CoinType_Gas, + Asset: deposit.Asset, + IsCrossChainCall: deposit.IsCrossChainCall, }) logger.Info().Msgf("FilterInboundEvents: deposit detected in sig %s instruction %d", tx.Signatures[0], i) } @@ -211,17 +212,18 @@ func FilterInboundEvents( } else if deposit != nil { seenDepositSPL = true events = append(events, &clienttypes.InboundEvent{ - SenderChainID: senderChainID, - Sender: deposit.Sender, - Receiver: "", // receiver will be pulled out from memo later - TxOrigin: deposit.Sender, - Amount: deposit.Amount, - Memo: deposit.Memo, - BlockNumber: deposit.Slot, // instead of using block, Solana explorer uses slot for indexing - TxHash: tx.Signatures[0].String(), - Index: 0, // hardcode to 0 for Solana, not a EVM smart contract call - CoinType: coin.CoinType_ERC20, - Asset: deposit.Asset, + SenderChainID: senderChainID, + Sender: deposit.Sender, + Receiver: deposit.Receiver, + TxOrigin: deposit.Sender, + Amount: deposit.Amount, + Memo: deposit.Memo, + BlockNumber: deposit.Slot, // instead of using block, Solana explorer uses slot for indexing + TxHash: tx.Signatures[0].String(), + Index: 0, // hardcode to 0 for Solana, not a EVM smart contract call + CoinType: coin.CoinType_ERC20, + Asset: deposit.Asset, + IsCrossChainCall: deposit.IsCrossChainCall, }) logger.Info().Msgf("FilterInboundEvents: SPL deposit detected in sig %s instruction %d", tx.Signatures[0], i) } @@ -235,19 +237,6 @@ func FilterInboundEvents( // BuildInboundVoteMsgFromEvent builds a MsgVoteInbound from an inbound event func (ob *Observer) BuildInboundVoteMsgFromEvent(event *clienttypes.InboundEvent) *crosschaintypes.MsgVoteInbound { - // prepare logger fields - lf := map[string]any{ - logs.FieldMethod: "BuildInboundVoteMsgFromEvent", - logs.FieldTx: event.TxHash, - } - - // decode event memo bytes to get the receiver - err := event.DecodeMemo() - if err != nil { - ob.Logger().Inbound.Info().Fields(lf).Msgf("invalid memo bytes: %s", hex.EncodeToString(event.Memo)) - return nil - } - // check if the event is processable if !ob.IsEventProcessable(*event) { return nil @@ -269,9 +258,10 @@ func (ob *Observer) BuildInboundVoteMsgFromEvent(event *clienttypes.InboundEvent event.CoinType, event.Asset, 0, // not a smart contract call - crosschaintypes.ProtocolContractVersion_V1, - false, // not relevant for v1 + crosschaintypes.ProtocolContractVersion_V2, + false, // not used crosschaintypes.InboundStatus_SUCCESS, + crosschaintypes.WithCrossChainCall(event.IsCrossChainCall), ) } diff --git a/zetaclient/chains/solana/observer/inbound_test.go b/zetaclient/chains/solana/observer/inbound_test.go index fbe496855e..8c96410bd4 100644 --- a/zetaclient/chains/solana/observer/inbound_test.go +++ b/zetaclient/chains/solana/observer/inbound_test.go @@ -2,6 +2,7 @@ package observer_test import ( "context" + "encoding/hex" "testing" "github.com/rs/zerolog" @@ -76,19 +77,19 @@ func Test_FilterInboundEvents(t *testing.T) { // expected result sender := "HgTpiVRvjUPUcWLzdmCgdadu1GceJNgBWLoN9r66p8o3" - expectedMemo := []byte{109, 163, 11, 250, 101, 232, 90, 22, 176, 91, 206, 56, 70, 51, 158, 210, 188, 116, 99, 22} eventExpected := &clienttypes.InboundEvent{ - SenderChainID: chain.ChainId, - Sender: sender, - Receiver: "", - TxOrigin: sender, - Amount: 100000000, - Memo: expectedMemo, - BlockNumber: txResult.Slot, - TxHash: txHash, - Index: 0, // not a EVM smart contract call - CoinType: coin.CoinType_Gas, - Asset: "", // no asset for gas token SOL + SenderChainID: chain.ChainId, + Sender: sender, + Receiver: "0x6dA30bFA65E85a16b05bCE3846339ed2BC746316", + TxOrigin: sender, + Amount: 100000000, + Memo: []byte{}, + BlockNumber: txResult.Slot, + TxHash: txHash, + Index: 0, // not a EVM smart contract call + CoinType: coin.CoinType_Gas, + Asset: "", // no asset for gas token SOL + IsCrossChainCall: false, } t.Run("should filter inbound event deposit SOL", func(t *testing.T) { @@ -135,21 +136,14 @@ func Test_BuildInboundVoteMsgFromEvent(t *testing.T) { t.Run("should return vote msg for valid event", func(t *testing.T) { sender := sample.SolanaAddress(t) receiver := sample.EthAddress() - event := sample.InboundEvent(chain.ChainId, sender, "", 1280, receiver.Bytes()) + message := sample.Bytes() + event := sample.InboundEvent(chain.ChainId, sender, receiver.Hex(), 1280, message) msg := ob.BuildInboundVoteMsgFromEvent(event) require.NotNil(t, msg) require.Equal(t, sender, msg.Sender) require.Equal(t, receiver.Hex(), msg.Receiver) - }) - - t.Run("should return nil if failed to decode memo", func(t *testing.T) { - sender := sample.SolanaAddress(t) - memo := []byte("a memo too short") - event := sample.InboundEvent(chain.ChainId, sender, sender, 1280, memo) - - msg := ob.BuildInboundVoteMsgFromEvent(event) - require.Nil(t, msg) + require.Equal(t, hex.EncodeToString(message), msg.Message) }) t.Run("should return nil if event is not processable", func(t *testing.T) { diff --git a/zetaclient/types/event.go b/zetaclient/types/event.go index 4cb3e1abde..8367ef2ecb 100644 --- a/zetaclient/types/event.go +++ b/zetaclient/types/event.go @@ -67,6 +67,9 @@ type InboundEvent struct { // Asset is the asset of the inbound Asset string + + // IsCrossChainCall is true if the inbound is a cross-chain call + IsCrossChainCall bool } // DecodeMemo decodes the receiver from the memo bytes