Commit bacba66c authored by Ralph Pichler's avatar Ralph Pichler Committed by GitHub

chequebook: better event parsing (#1360)

parent b2145c85
......@@ -157,7 +157,7 @@ func initChequeStoreCashout(
chainID int64,
overlayEthAddress common.Address,
transactionService transaction.Service,
) (chequebook.ChequeStore, chequebook.CashoutService, error) {
) (chequebook.ChequeStore, chequebook.CashoutService) {
chequeStore := chequebook.NewChequeStore(
stateStore,
swapBackend,
......@@ -168,18 +168,14 @@ func initChequeStoreCashout(
chequebook.RecoverCheque,
)
cashout, err := chequebook.NewCashoutService(
cashout := chequebook.NewCashoutService(
stateStore,
chequebook.NewSimpleSwapBindings,
swapBackend,
transactionService,
chequeStore,
)
if err != nil {
return nil, nil, err
}
return chequeStore, cashout, nil
return chequeStore, cashout
}
// InitSwap will initialize and register the swap service.
......
......@@ -222,7 +222,7 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
return nil, err
}
chequeStore, cashoutService, err = initChequeStoreCashout(
chequeStore, cashoutService = initChequeStoreCashout(
stateStore,
swapBackend,
chequebookFactory,
......@@ -230,9 +230,6 @@ func NewBee(addr string, swarmAddress swarm.Address, publicKey ecdsa.PublicKey,
overlayEthAddress,
transactionService,
)
if err != nil {
return nil, err
}
}
p2ps, err := libp2p.New(p2pCtx, signer, networkID, swarmAddress, addr, addressbook, stateStore, logger, tracer, libp2p.Options{
......
......@@ -9,7 +9,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethersphere/sw3-bindings/v3/simpleswapfactory"
)
......@@ -19,8 +18,6 @@ type SimpleSwapBinding interface {
Issuer(*bind.CallOpts) (common.Address, error)
TotalPaidOut(*bind.CallOpts) (*big.Int, error)
PaidOut(*bind.CallOpts, common.Address) (*big.Int, error)
ParseChequeCashed(types.Log) (*simpleswapfactory.ERC20SimpleSwapChequeCashed, error)
ParseChequeBounced(types.Log) (*simpleswapfactory.ERC20SimpleSwapChequeBounced, error)
}
type SimpleSwapBindingFunc = func(common.Address, bind.ContractBackend) (SimpleSwapBinding, error)
......@@ -44,7 +41,6 @@ func NewERC20Bindings(address common.Address, backend bind.ContractBackend) (ERC
// SimpleSwapFactoryBinding is the interface for the generated go bindings for SimpleSwapFactory
type SimpleSwapFactoryBinding interface {
ParseSimpleSwapDeployed(types.Log) (*simpleswapfactory.SimpleSwapFactorySimpleSwapDeployed, error)
DeployedContracts(*bind.CallOpts, common.Address) (bool, error)
ERC20Address(*bind.CallOpts) (common.Address, error)
}
......
......@@ -9,14 +9,11 @@ import (
"errors"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethersphere/bee/pkg/settlement/swap/transaction"
"github.com/ethersphere/bee/pkg/storage"
"github.com/ethersphere/sw3-bindings/v3/simpleswapfactory"
)
var (
......@@ -33,12 +30,10 @@ type CashoutService interface {
}
type cashoutService struct {
store storage.StateStorer
simpleSwapBindingFunc SimpleSwapBindingFunc
backend transaction.Backend
transactionService transaction.Service
chequebookABI abi.ABI
chequeStore ChequeStore
store storage.StateStorer
backend transaction.Backend
transactionService transaction.Service
chequeStore ChequeStore
}
// CashoutStatus is the action plus its result
......@@ -65,28 +60,28 @@ type cashoutAction struct {
TxHash common.Hash
Cheque SignedCheque // the cheque that was used to cashout which may be different from the latest cheque
}
type chequeCashedEvent struct {
Beneficiary common.Address
Recipient common.Address
Caller common.Address
TotalPayout *big.Int
CumulativePayout *big.Int
CallerPayout *big.Int
}
// NewCashoutService creates a new CashoutService
func NewCashoutService(
store storage.StateStorer,
simpleSwapBindingFunc SimpleSwapBindingFunc,
backend transaction.Backend,
transactionService transaction.Service,
chequeStore ChequeStore,
) (CashoutService, error) {
chequebookABI, err := abi.JSON(strings.NewReader(simpleswapfactory.ERC20SimpleSwapABI))
if err != nil {
return nil, err
}
) CashoutService {
return &cashoutService{
store: store,
simpleSwapBindingFunc: simpleSwapBindingFunc,
backend: backend,
transactionService: transactionService,
chequebookABI: chequebookABI,
chequeStore: chequeStore,
}, nil
store: store,
backend: backend,
transactionService: transactionService,
chequeStore: chequeStore,
}
}
// cashoutActionKey computes the store key for the last cashout action for the chequebook
......@@ -101,7 +96,7 @@ func (s *cashoutService) CashCheque(ctx context.Context, chequebook, recipient c
return common.Hash{}, err
}
callData, err := s.chequebookABI.Pack("cashChequeBeneficiary", recipient, cheque.CumulativePayout, cheque.Signature)
callData, err := chequebookABI.Pack("cashChequeBeneficiary", recipient, cheque.CumulativePayout, cheque.Signature)
if err != nil {
return common.Hash{}, err
}
......@@ -188,25 +183,24 @@ func (s *cashoutService) parseCashChequeBeneficiaryReceipt(chequebookAddress com
Bounced: false,
}
binding, err := s.simpleSwapBindingFunc(chequebookAddress, s.backend)
var cashedEvent chequeCashedEvent
err := transaction.FindSingleEvent(&chequebookABI, receipt, chequebookAddress, chequeCashedEventType, &cashedEvent)
if err != nil {
return nil, err
}
for _, log := range receipt.Logs {
if log.Address != chequebookAddress {
continue
}
if event, err := binding.ParseChequeCashed(*log); err == nil {
result.Beneficiary = event.Beneficiary
result.Caller = event.Caller
result.CallerPayout = event.CallerPayout
result.TotalPayout = event.TotalPayout
result.CumulativePayout = event.CumulativePayout
result.Recipient = event.Recipient
} else if _, err := binding.ParseChequeBounced(*log); err == nil {
result.Bounced = true
}
result.Beneficiary = cashedEvent.Beneficiary
result.Caller = cashedEvent.Caller
result.CallerPayout = cashedEvent.CallerPayout
result.TotalPayout = cashedEvent.TotalPayout
result.CumulativePayout = cashedEvent.CumulativePayout
result.Recipient = cashedEvent.Recipient
err = transaction.FindSingleEvent(&chequebookABI, receipt, chequebookAddress, chequeBouncedEventType, nil)
if err == nil {
result.Bounced = true
} else if !errors.Is(err, transaction.ErrEventNotFound) {
return nil, err
}
return result, nil
......
......@@ -6,11 +6,9 @@ package chequebook_test
import (
"context"
"errors"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethersphere/bee/pkg/settlement/swap/chequebook"
......@@ -22,11 +20,16 @@ import (
"github.com/ethersphere/sw3-bindings/v3/simpleswapfactory"
)
var (
chequebookABI = transaction.ParseABIUnchecked(simpleswapfactory.ERC20SimpleSwapABI)
chequeCashedEventType = chequebookABI.Events["ChequeCashed"]
chequeBouncedEventType = chequebookABI.Events["ChequeBounced"]
)
func TestCashout(t *testing.T) {
chequebookAddress := common.HexToAddress("abcd")
recipientAddress := common.HexToAddress("efff")
txHash := common.HexToHash("dddd")
log1Topic := common.HexToHash("eeee")
totalPayout := big.NewInt(100)
cumulativePayout := big.NewInt(500)
......@@ -40,25 +43,8 @@ func TestCashout(t *testing.T) {
}
store := storemock.NewStateStore()
cashoutService, err := chequebook.NewCashoutService(
cashoutService := chequebook.NewCashoutService(
store,
func(common.Address, bind.ContractBackend) (chequebook.SimpleSwapBinding, error) {
return &simpleSwapBindingMock{
parseChequeCashed: func(l types.Log) (*simpleswapfactory.ERC20SimpleSwapChequeCashed, error) {
if l.Topics[0] != log1Topic {
t.Fatalf("parsing wrong log. wanted %v, got %v", log1Topic, l.Topics[0])
}
return &simpleswapfactory.ERC20SimpleSwapChequeCashed{
Beneficiary: cheque.Beneficiary,
Recipient: recipientAddress,
Caller: cheque.Beneficiary,
TotalPayout: totalPayout,
CumulativePayout: cumulativePayout,
CallerPayout: big.NewInt(0),
}, nil
},
}, nil
},
backendmock.New(
backendmock.WithTransactionByHashFunc(func(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
if hash != txHash {
......@@ -70,12 +56,19 @@ func TestCashout(t *testing.T) {
if hash != txHash {
t.Fatalf("fetching receipt for transaction. wanted %v, got %v", txHash, hash)
}
logData, err := chequeCashedEventType.Inputs.NonIndexed().Pack(totalPayout, cumulativePayout, big.NewInt(0))
if err != nil {
t.Fatal(err)
}
return &types.Receipt{
Status: types.ReceiptStatusSuccessful,
Logs: []*types.Log{
{
Address: chequebookAddress,
Topics: []common.Hash{log1Topic},
Topics: []common.Hash{chequeCashedEventType.ID, cheque.Beneficiary.Hash(), recipientAddress.Hash(), cheque.Beneficiary.Hash()},
Data: logData,
},
},
}, nil
......@@ -101,9 +94,6 @@ func TestCashout(t *testing.T) {
}),
),
)
if err != nil {
t.Fatal(err)
}
returnedTxHash, err := cashoutService.CashCheque(context.Background(), chequebookAddress, recipientAddress)
if err != nil {
......@@ -154,8 +144,6 @@ func TestCashoutBounced(t *testing.T) {
chequebookAddress := common.HexToAddress("abcd")
recipientAddress := common.HexToAddress("efff")
txHash := common.HexToHash("dddd")
log1Topic := common.HexToHash("eeee")
logBouncedTopic := common.HexToHash("bbbb")
totalPayout := big.NewInt(100)
cumulativePayout := big.NewInt(500)
......@@ -169,31 +157,8 @@ func TestCashoutBounced(t *testing.T) {
}
store := storemock.NewStateStore()
cashoutService, err := chequebook.NewCashoutService(
cashoutService := chequebook.NewCashoutService(
store,
func(common.Address, bind.ContractBackend) (chequebook.SimpleSwapBinding, error) {
return &simpleSwapBindingMock{
parseChequeCashed: func(l types.Log) (*simpleswapfactory.ERC20SimpleSwapChequeCashed, error) {
if l.Topics[0] != log1Topic {
return nil, errors.New("")
}
return &simpleswapfactory.ERC20SimpleSwapChequeCashed{
Beneficiary: cheque.Beneficiary,
Recipient: recipientAddress,
Caller: cheque.Beneficiary,
TotalPayout: totalPayout,
CumulativePayout: cumulativePayout,
CallerPayout: big.NewInt(0),
}, nil
},
parseChequeBounced: func(l types.Log) (*simpleswapfactory.ERC20SimpleSwapChequeBounced, error) {
if l.Topics[0] != logBouncedTopic {
t.Fatalf("parsing wrong bounced log. wanted %v, got %v", logBouncedTopic, l.Topics[0])
}
return &simpleswapfactory.ERC20SimpleSwapChequeBounced{}, nil
},
}, nil
},
backendmock.New(
backendmock.WithTransactionByHashFunc(func(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) {
if hash != txHash {
......@@ -205,16 +170,23 @@ func TestCashoutBounced(t *testing.T) {
if hash != txHash {
t.Fatalf("fetching receipt for transaction. wanted %v, got %v", txHash, hash)
}
chequeCashedLogData, err := chequeCashedEventType.Inputs.NonIndexed().Pack(totalPayout, cumulativePayout, big.NewInt(0))
if err != nil {
t.Fatal(err)
}
return &types.Receipt{
Status: types.ReceiptStatusSuccessful,
Logs: []*types.Log{
{
Address: chequebookAddress,
Topics: []common.Hash{log1Topic},
Topics: []common.Hash{chequeCashedEventType.ID, cheque.Beneficiary.Hash(), recipientAddress.Hash(), cheque.Beneficiary.Hash()},
Data: chequeCashedLogData,
},
{
Address: chequebookAddress,
Topics: []common.Hash{logBouncedTopic},
Topics: []common.Hash{chequeBouncedEventType.ID},
},
},
}, nil
......@@ -240,9 +212,6 @@ func TestCashoutBounced(t *testing.T) {
}),
),
)
if err != nil {
t.Fatal(err)
}
returnedTxHash, err := cashoutService.CashCheque(context.Background(), chequebookAddress, recipientAddress)
if err != nil {
......@@ -305,11 +274,8 @@ func TestCashoutStatusReverted(t *testing.T) {
}
store := storemock.NewStateStore()
cashoutService, err := chequebook.NewCashoutService(
cashoutService := chequebook.NewCashoutService(
store,
func(common.Address, bind.ContractBackend) (chequebook.SimpleSwapBinding, error) {
return &simpleSwapBindingMock{}, nil
},
backendmock.New(
backendmock.WithTransactionByHashFunc(func(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
if hash != txHash {
......@@ -340,9 +306,6 @@ func TestCashoutStatusReverted(t *testing.T) {
}),
),
)
if err != nil {
t.Fatal(err)
}
returnedTxHash, err := cashoutService.CashCheque(context.Background(), chequebookAddress, recipientAddress)
if err != nil {
......@@ -387,11 +350,8 @@ func TestCashoutStatusPending(t *testing.T) {
}
store := storemock.NewStateStore()
cashoutService, err := chequebook.NewCashoutService(
cashoutService := chequebook.NewCashoutService(
store,
func(common.Address, bind.ContractBackend) (chequebook.SimpleSwapBinding, error) {
return &simpleSwapBindingMock{}, nil
},
backendmock.New(
backendmock.WithTransactionByHashFunc(func(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
if hash != txHash {
......@@ -414,9 +374,6 @@ func TestCashoutStatusPending(t *testing.T) {
}),
),
)
if err != nil {
t.Fatal(err)
}
returnedTxHash, err := cashoutService.CashCheque(context.Background(), chequebookAddress, recipientAddress)
if err != nil {
......
......@@ -12,7 +12,6 @@ import (
"strings"
"sync"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethersphere/bee/pkg/settlement/swap/transaction"
......@@ -33,6 +32,11 @@ var (
ErrOutOfFunds = errors.New("chequebook out of funds")
// ErrInsufficientFunds is the error when the chequebook has not enough free funds for a user action
ErrInsufficientFunds = errors.New("insufficient token balance")
chequebookABI = transaction.ParseABIUnchecked(simpleswapfactory.ERC20SimpleSwapABI)
chequeCashedEventType = chequebookABI.Events["ChequeCashed"]
chequeBouncedEventType = chequebookABI.Events["ChequeBounced"]
erc20ABI = transaction.ParseABIUnchecked(simpleswapfactory.ERC20ABI)
)
// Service is the main interface for interacting with the nodes chequebook.
......@@ -63,12 +67,10 @@ type service struct {
transactionService transaction.Service
address common.Address
chequebookABI abi.ABI
chequebookInstance SimpleSwapBinding
ownerAddress common.Address
erc20Address common.Address
erc20ABI abi.ABI
erc20Instance ERC20Binding
store storage.StateStorer
......@@ -78,16 +80,6 @@ type service struct {
// New creates a new chequebook service for the provided chequebook contract.
func New(backend transaction.Backend, transactionService transaction.Service, address, erc20Address, ownerAddress common.Address, store storage.StateStorer, chequeSigner ChequeSigner, simpleSwapBindingFunc SimpleSwapBindingFunc, erc20BindingFunc ERC20BindingFunc) (Service, error) {
chequebookABI, err := abi.JSON(strings.NewReader(simpleswapfactory.ERC20SimpleSwapABI))
if err != nil {
return nil, err
}
erc20ABI, err := abi.JSON(strings.NewReader(simpleswapfactory.ERC20ABI))
if err != nil {
return nil, err
}
chequebookInstance, err := simpleSwapBindingFunc(address, backend)
if err != nil {
return nil, err
......@@ -102,11 +94,9 @@ func New(backend transaction.Backend, transactionService transaction.Service, ad
backend: backend,
transactionService: transactionService,
address: address,
chequebookABI: chequebookABI,
chequebookInstance: chequebookInstance,
ownerAddress: ownerAddress,
erc20Address: erc20Address,
erc20ABI: erc20ABI,
erc20Instance: erc20Instance,
store: store,
chequeSigner: chequeSigner,
......@@ -133,7 +123,7 @@ func (s *service) Deposit(ctx context.Context, amount *big.Int) (hash common.Has
return common.Hash{}, ErrInsufficientFunds
}
callData, err := s.erc20ABI.Pack("transfer", s.address, amount)
callData, err := erc20ABI.Pack("transfer", s.address, amount)
if err != nil {
return common.Hash{}, err
}
......@@ -365,7 +355,7 @@ func (s *service) Withdraw(ctx context.Context, amount *big.Int) (hash common.Ha
return common.Hash{}, ErrInsufficientFunds
}
callData, err := s.chequebookABI.Pack("withdraw", amount)
callData, err := chequebookABI.Pack("withdraw", amount)
if err != nil {
return common.Hash{}, err
}
......
......@@ -10,19 +10,12 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethersphere/bee/pkg/settlement/swap/chequebook"
"github.com/ethersphere/sw3-bindings/v3/simpleswapfactory"
)
type simpleSwapFactoryBindingMock struct {
erc20Address func(*bind.CallOpts) (common.Address, error)
deployedContracts func(*bind.CallOpts, common.Address) (bool, error)
parseSimpleSwapDeployed func(types.Log) (*simpleswapfactory.SimpleSwapFactorySimpleSwapDeployed, error)
}
func (m *simpleSwapFactoryBindingMock) ParseSimpleSwapDeployed(l types.Log) (*simpleswapfactory.SimpleSwapFactorySimpleSwapDeployed, error) {
return m.parseSimpleSwapDeployed(l)
erc20Address func(*bind.CallOpts) (common.Address, error)
deployedContracts func(*bind.CallOpts, common.Address) (bool, error)
}
func (m *simpleSwapFactoryBindingMock) DeployedContracts(o *bind.CallOpts, a common.Address) (bool, error) {
......@@ -33,12 +26,10 @@ func (m *simpleSwapFactoryBindingMock) ERC20Address(o *bind.CallOpts) (common.Ad
}
type simpleSwapBindingMock struct {
balance func(*bind.CallOpts) (*big.Int, error)
issuer func(*bind.CallOpts) (common.Address, error)
totalPaidOut func(o *bind.CallOpts) (*big.Int, error)
paidOut func(*bind.CallOpts, common.Address) (*big.Int, error)
parseChequeCashed func(types.Log) (*simpleswapfactory.ERC20SimpleSwapChequeCashed, error)
parseChequeBounced func(types.Log) (*simpleswapfactory.ERC20SimpleSwapChequeBounced, error)
balance func(*bind.CallOpts) (*big.Int, error)
issuer func(*bind.CallOpts) (common.Address, error)
totalPaidOut func(o *bind.CallOpts) (*big.Int, error)
paidOut func(*bind.CallOpts, common.Address) (*big.Int, error)
}
func (m *simpleSwapBindingMock) Balance(o *bind.CallOpts) (*big.Int, error) {
......@@ -53,14 +44,6 @@ func (m *simpleSwapBindingMock) TotalPaidOut(o *bind.CallOpts) (*big.Int, error)
return m.totalPaidOut(o)
}
func (m *simpleSwapBindingMock) ParseChequeCashed(l types.Log) (*simpleswapfactory.ERC20SimpleSwapChequeCashed, error) {
return m.parseChequeCashed(l)
}
func (m *simpleSwapBindingMock) ParseChequeBounced(l types.Log) (*simpleswapfactory.ERC20SimpleSwapChequeBounced, error) {
return m.parseChequeBounced(l)
}
func (m *simpleSwapBindingMock) PaidOut(o *bind.CallOpts, c common.Address) (*big.Int, error) {
return m.paidOut(o, c)
}
......
......@@ -7,13 +7,11 @@ package chequebook
import (
"bytes"
"errors"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethersphere/bee/pkg/settlement/swap/transaction"
"github.com/ethersphere/sw3-bindings/v3/simpleswapfactory"
"golang.org/x/net/context"
......@@ -22,6 +20,9 @@ import (
var (
ErrInvalidFactory = errors.New("not a valid factory contract")
ErrNotDeployedByFactory = errors.New("chequebook not deployed by factory")
factoryABI = transaction.ParseABIUnchecked(simpleswapfactory.SimpleSwapFactoryABI)
simpleSwapDeployedEventType = factoryABI.Events["SimpleSwapDeployed"]
)
// Factory is the main interface for interacting with the chequebook factory.
......@@ -43,17 +44,15 @@ type factory struct {
transactionService transaction.Service
address common.Address
ABI abi.ABI
instance SimpleSwapFactoryBinding
}
type simpleSwapDeployedEvent struct {
ContractAddress common.Address
}
// NewFactory creates a new factory service for the provided factory contract.
func NewFactory(backend transaction.Backend, transactionService transaction.Service, address common.Address, simpleSwapFactoryBindingFunc SimpleSwapFactoryBindingFunc) (Factory, error) {
ABI, err := abi.JSON(strings.NewReader(simpleswapfactory.SimpleSwapFactoryABI))
if err != nil {
return nil, err
}
instance, err := simpleSwapFactoryBindingFunc(address, backend)
if err != nil {
return nil, err
......@@ -63,14 +62,13 @@ func NewFactory(backend transaction.Backend, transactionService transaction.Serv
backend: backend,
transactionService: transactionService,
address: address,
ABI: ABI,
instance: instance,
}, nil
}
// Deploy deploys a new chequebook and returns once the transaction has been submitted.
func (c *factory) Deploy(ctx context.Context, issuer common.Address, defaultHardDepositTimeoutDuration *big.Int) (common.Hash, error) {
callData, err := c.ABI.Pack("deploySimpleSwap", issuer, big.NewInt(0).Set(defaultHardDepositTimeoutDuration))
callData, err := factoryABI.Pack("deploySimpleSwap", issuer, big.NewInt(0).Set(defaultHardDepositTimeoutDuration))
if err != nil {
return common.Hash{}, err
}
......@@ -98,32 +96,13 @@ func (c *factory) WaitDeployed(ctx context.Context, txHash common.Hash) (common.
return common.Address{}, err
}
chequebookAddress, err := c.parseDeployReceipt(receipt)
var event simpleSwapDeployedEvent
err = transaction.FindSingleEvent(&factoryABI, receipt, c.address, simpleSwapDeployedEventType, &event)
if err != nil {
return common.Address{}, err
return common.Address{}, fmt.Errorf("contract deployment failed: %w", err)
}
return chequebookAddress, nil
}
// parseDeployReceipt parses the address of the deployed chequebook from the receipt.
func (c *factory) parseDeployReceipt(receipt *types.Receipt) (address common.Address, err error) {
if receipt.Status != 1 {
return common.Address{}, transaction.ErrTransactionReverted
}
for _, log := range receipt.Logs {
if log.Address != c.address {
continue
}
if event, err := c.instance.ParseSimpleSwapDeployed(*log); err == nil {
address = event.ContractAddress
break
}
}
if (address == common.Address{}) {
return common.Address{}, errors.New("contract deployment failed")
}
return address, nil
return event.ContractAddress, nil
}
// VerifyBytecode checks that the factory is valid.
......
......@@ -5,7 +5,6 @@
package chequebook_test
import (
"bytes"
"context"
"errors"
"math/big"
......@@ -21,6 +20,11 @@ import (
"github.com/ethersphere/sw3-bindings/v3/simpleswapfactory"
)
var (
factoryABI = transaction.ParseABIUnchecked(simpleswapfactory.SimpleSwapFactoryABI)
simpleSwapDeployedEvent = factoryABI.Events["SimpleSwapDeployed"]
)
func newTestFactory(t *testing.T, factoryAddress common.Address, backend transaction.Backend, transactionService transaction.Service, simpleSwapFactoryBinding chequebook.SimpleSwapFactoryBinding) (chequebook.Factory, error) {
return chequebook.NewFactory(backend, transactionService, factoryAddress,
func(addr common.Address, b bind.ContractBackend) (chequebook.SimpleSwapFactoryBinding, error) {
......@@ -181,7 +185,6 @@ func TestFactoryDeploy(t *testing.T) {
defaultTimeout := big.NewInt(1)
deployTransactionHash := common.HexToHash("0xffff")
deployAddress := common.HexToAddress("0xdddd")
logData := common.Hex2Bytes("0xcccc")
factory, err := newTestFactory(
t,
factoryAddress,
......@@ -200,6 +203,10 @@ func TestFactoryDeploy(t *testing.T) {
if txHash != deployTransactionHash {
t.Fatalf("waiting for wrong transaction. wanted %x, got %x", deployTransactionHash, txHash)
}
logData, err := simpleSwapDeployedEvent.Inputs.NonIndexed().Pack(deployAddress)
if err != nil {
t.Fatal(err)
}
return &types.Receipt{
Status: 1,
Logs: []*types.Log{
......@@ -208,22 +215,15 @@ func TestFactoryDeploy(t *testing.T) {
},
{
Address: factoryAddress,
Topics: []common.Hash{simpleSwapDeployedEvent.ID},
Data: logData,
},
},
}, nil
}),
),
&simpleSwapFactoryBindingMock{
parseSimpleSwapDeployed: func(log types.Log) (*simpleswapfactory.SimpleSwapFactorySimpleSwapDeployed, error) {
if !bytes.Equal(log.Data, logData) {
t.Fatal("trying to parse wrong log")
}
return &simpleswapfactory.SimpleSwapFactorySimpleSwapDeployed{
ContractAddress: deployAddress,
}, nil
},
})
nil,
)
if err != nil {
t.Fatal(err)
}
......
......@@ -6,9 +6,12 @@ package transaction
import (
"context"
"fmt"
"math/big"
"strings"
"time"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
......@@ -65,3 +68,12 @@ func WaitSynced(ctx context.Context, backend Backend, maxDelay time.Duration) er
}
}
}
// ParseABIUnchecked will parse a valid json abi. Only use this with string constants known to be correct.
func ParseABIUnchecked(json string) abi.ABI {
cabi, err := abi.JSON(strings.NewReader(json))
if err != nil {
panic(fmt.Sprintf("error creating ABI for contract: %v", err))
}
return cabi
}
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package transaction
import (
"errors"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
var (
ErrEventNotFound = errors.New("event not found")
ErrNoTopic = errors.New("no topic")
)
// ParseEvent will parse the specified abi event from the given log
func ParseEvent(a *abi.ABI, eventName string, c interface{}, e types.Log) error {
if len(e.Topics) == 0 {
return ErrNoTopic
}
if len(e.Data) > 0 {
if err := a.UnpackIntoInterface(c, eventName, e.Data); err != nil {
return err
}
}
var indexed abi.Arguments
for _, arg := range a.Events[eventName].Inputs {
if arg.Indexed {
indexed = append(indexed, arg)
}
}
return abi.ParseTopics(c, indexed, e.Topics[1:])
}
// FindSingleEvent will find the first event of the given kind.
func FindSingleEvent(abi *abi.ABI, receipt *types.Receipt, contractAddress common.Address, event abi.Event, out interface{}) error {
if receipt.Status != 1 {
return ErrTransactionReverted
}
for _, log := range receipt.Logs {
if log.Address != contractAddress {
continue
}
if len(log.Topics) == 0 {
continue
}
if log.Topics[0] != event.ID {
continue
}
return ParseEvent(abi, event.Name, out, *log)
}
return ErrEventNotFound
}
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package transaction_test
import (
"errors"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethersphere/bee/pkg/settlement/swap/transaction"
"github.com/ethersphere/sw3-bindings/v3/simpleswapfactory"
)
var (
erc20ABI = transaction.ParseABIUnchecked(simpleswapfactory.ERC20ABI)
)
type transferEvent struct {
From common.Address
To common.Address
Value *big.Int
}
func newTransferLog(address common.Address, from common.Address, to common.Address, value *big.Int) *types.Log {
return &types.Log{
Topics: []common.Hash{
erc20ABI.Events["Transfer"].ID,
from.Hash(),
to.Hash(),
},
Data: value.FillBytes(make([]byte, 32)),
Address: address,
}
}
func TestParseEvent(t *testing.T) {
from := common.HexToAddress("00")
to := common.HexToAddress("01")
value := big.NewInt(0)
t.Run("ok", func(t *testing.T) {
var event transferEvent
err := transaction.ParseEvent(&erc20ABI, "Transfer", &event, *newTransferLog(common.Address{}, from, to, value))
if err != nil {
t.Fatal(err)
}
if event.From != from {
t.Fatalf("parsed wrong from. wanted %x, got %x", from, event.From)
}
if event.To != to {
t.Fatalf("parsed wrong to. wanted %x, got %x", to, event.To)
}
if value.Cmp(event.Value) != 0 {
t.Fatalf("parsed wrong value. wanted %d, got %d", value, event.Value)
}
})
t.Run("no topic", func(t *testing.T) {
var event transferEvent
err := transaction.ParseEvent(&erc20ABI, "Transfer", &event, types.Log{
Topics: []common.Hash{},
})
if !errors.Is(err, transaction.ErrNoTopic) {
t.Fatalf("expected error %v, got %v", transaction.ErrNoTopic, err)
}
})
}
func TestFindSingleEvent(t *testing.T) {
contractAddress := common.HexToAddress("abcd")
from := common.HexToAddress("00")
to := common.HexToAddress("01")
value := big.NewInt(0)
t.Run("ok", func(t *testing.T) {
var event transferEvent
err := transaction.FindSingleEvent(
&erc20ABI,
&types.Receipt{
Logs: []*types.Log{
newTransferLog(from, to, from, value), // event from different contract
{Topics: []common.Hash{{}}, Address: contractAddress}, // different event from same contract
newTransferLog(contractAddress, from, to, value),
},
Status: 1,
},
contractAddress,
erc20ABI.Events["Transfer"],
&event,
)
if err != nil {
t.Fatal(err)
}
if event.From != from {
t.Fatalf("parsed wrong from. wanted %x, got %x", from, event.From)
}
if event.To != to {
t.Fatalf("parsed wrong to. wanted %x, got %x", to, event.To)
}
if value.Cmp(event.Value) != 0 {
t.Fatalf("parsed wrong value. wanted %d, got %d", value, event.Value)
}
})
t.Run("not found", func(t *testing.T) {
var event transferEvent
err := transaction.FindSingleEvent(
&erc20ABI,
&types.Receipt{
Logs: []*types.Log{
newTransferLog(from, to, from, value), // event from different contract
{Topics: []common.Hash{{}}, Address: contractAddress}, // different event from same contract
},
Status: 1,
},
contractAddress,
erc20ABI.Events["Transfer"],
&event,
)
if !errors.Is(err, transaction.ErrEventNotFound) {
t.Fatalf("wanted error %v, got %v", transaction.ErrEventNotFound, err)
}
})
t.Run("Reverted", func(t *testing.T) {
var event transferEvent
err := transaction.FindSingleEvent(
&erc20ABI,
&types.Receipt{Status: 0},
contractAddress,
erc20ABI.Events["Transfer"],
&event,
)
if !errors.Is(err, transaction.ErrTransactionReverted) {
t.Fatalf("wanted error %v, got %v", transaction.ErrTransactionReverted, err)
}
})
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment