services/cctp-relayer/relayer/synapse_test.go
package relayer_test
import (
"context"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/synapsecns/sanguine/services/cctp-relayer/attestation"
"github.com/synapsecns/sanguine/services/cctp-relayer/contracts/cctp"
"github.com/synapsecns/sanguine/services/cctp-relayer/relayer"
relayTypes "github.com/synapsecns/sanguine/services/cctp-relayer/types"
omniClient "github.com/synapsecns/sanguine/services/omnirpc/client"
)
func (s *CCTPRelayerSuite) TestHandleCircleRequestSent() {
// setup
originChain := s.testBackends[0]
destChain := s.testBackends[1]
_, originSynapseCCTP := s.deployManager.GetSynapseCCTP(s.GetTestContext(), originChain)
_, originMockUsdc := s.deployManager.GetMockMintBurnTokenType(s.GetTestContext(), originChain)
// create a new relayer
mockAPI := attestation.NewMockCircleAPI()
omniRPCClient := omniClient.NewOmnirpcClient(s.testOmnirpc, s.metricsHandler, omniClient.WithCaptureReqRes())
relay, err := relayer.NewCCTPRelayer(s.GetTestContext(), s.GetTestConfig(), s.testStore, omniRPCClient, s.metricsHandler, mockAPI)
s.Nil(err)
// mint token
opts := originChain.GetTxContext(s.GetTestContext(), nil)
amount := big.NewInt(1000000000000000000)
tx, err := originMockUsdc.MintPublic(opts.TransactOpts, opts.From, amount)
s.Nil(err)
originChain.WaitForConfirmation(s.GetTestContext(), tx)
// approve token
tx, err = originMockUsdc.Approve(opts.TransactOpts, originSynapseCCTP.Address(), amount)
s.Nil(err)
originChain.WaitForConfirmation(s.GetTestContext(), tx)
// send token
tx, err = originSynapseCCTP.SendCircleToken(opts.TransactOpts, opts.From, big.NewInt(int64(destChain.GetChainID())), originMockUsdc.Address(), amount, 0, []byte{})
s.Nil(err)
originChain.WaitForConfirmation(s.GetTestContext(), tx)
// handle send request
msg, err := relay.FetchAndProcessSentEvent(s.GetTestContext(), tx.Hash(), uint32(originChain.GetChainID()))
s.Nil(err)
s.Equal(msg.OriginTxHash, tx.Hash().String())
s.Equal(msg.State, relayTypes.Pending)
// verify that the request is stored in the db
var storedMsg relayTypes.Message
err = s.testStore.DB().Where("origin_tx_hash = ?", msg.OriginTxHash).First(&storedMsg).Error
s.Nil(err)
s.Equal(*msg, storedMsg)
}
func (s *CCTPRelayerSuite) TestStoreCircleRequestFulfilled() {
// setup
originChain := s.testBackends[0]
destChain := s.testBackends[1]
// create a new relayer
mockAPI := attestation.NewMockCircleAPI()
omniRPCClient := omniClient.NewOmnirpcClient(s.testOmnirpc, s.metricsHandler, omniClient.WithCaptureReqRes())
relay, err := relayer.NewCCTPRelayer(s.GetTestContext(), s.GetTestConfig(), s.testStore, omniRPCClient, s.metricsHandler, mockAPI)
s.Nil(err)
// inject a fulfilled event to be handled by relayer
blockNumber, err := destChain.BlockNumber(s.GetTestContext())
s.Nil(err)
message1 := s.mockMessage(uint32(originChain.GetChainID()), uint32(destChain.GetChainID()), uint32(blockNumber))
fulfilledLog := &types.Log{
TxHash: common.HexToHash(message1.DestTxHash),
BlockNumber: blockNumber,
}
var requestID [32]byte
requestIDBytes := common.Hex2Bytes(message1.RequestID)
copy(requestID[:], requestIDBytes)
fulfilledEvent := &cctp.SynapseCCTPEventsCircleRequestFulfilled{
OriginDomain: message1.OriginChainID,
RequestID: requestID,
}
err = relay.StoreCircleRequestFulfilled(s.GetTestContext(), fulfilledLog, fulfilledEvent, uint32(destChain.GetChainID()))
s.Nil(err)
// verify that the request is stored as Complete in the db
var storedMsg relayTypes.Message
err = s.testStore.DB().Where("request_id = ?", message1.RequestID).First(&storedMsg).Error
s.Nil(err)
s.Equal(message1.DestTxHash, storedMsg.DestTxHash)
s.Equal(message1.OriginChainID, storedMsg.OriginChainID)
s.Equal(message1.DestChainID, storedMsg.DestChainID)
s.Equal(message1.RequestID, storedMsg.RequestID)
s.Equal(message1.BlockNumber, storedMsg.BlockNumber)
s.Equal(relayTypes.Complete, storedMsg.State)
// store a Submitted message
message2 := s.mockMessage(uint32(originChain.GetChainID()), uint32(destChain.GetChainID()), uint32(blockNumber))
err = s.testStore.DB().Create(&message2).Error
s.Nil(err)
// process the corresponding fulfilled event
requestIDBytes = common.Hex2Bytes(message2.RequestID)
copy(requestID[:], requestIDBytes)
fulfilledEvent = &cctp.SynapseCCTPEventsCircleRequestFulfilled{
RequestID: requestID,
}
err = relay.StoreCircleRequestFulfilled(s.GetTestContext(), &types.Log{TxHash: common.HexToHash(message2.DestTxHash)}, fulfilledEvent, uint32(destChain.GetChainID()))
s.Nil(err)
// verify that the message is stored as Complete in the db
err = s.testStore.DB().Where("request_id = ?", message2.RequestID).First(&storedMsg).Error
s.Nil(err)
message2.State = relayTypes.Complete
s.Equal(message2, storedMsg)
}
func (s *CCTPRelayerSuite) TestSubmitReceiveCircleToken() {
// create a new relayer
mockAPI := attestation.NewMockCircleAPI()
omniRPCClient := omniClient.NewOmnirpcClient(s.testOmnirpc, s.metricsHandler, omniClient.WithCaptureReqRes())
relay, err := relayer.NewCCTPRelayer(s.GetTestContext(), s.GetTestConfig(), s.testStore, omniRPCClient, s.metricsHandler, mockAPI)
s.Nil(err)
// build test msg
testHash := "0x5dba62229dba62f233dca8f3fd14488fdc45d2a86537da2dea7a5683b5e7f622"
originChain := s.testBackends[0]
originChainID, err := originChain.ChainID(s.GetTestContext())
s.Nil(err)
destChain := s.testBackends[1]
destChainID, err := destChain.ChainID(s.GetTestContext())
s.Nil(err)
msg := relayTypes.Message{
OriginTxHash: testHash,
OriginChainID: uint32(originChainID.Int64()),
DestChainID: uint32(destChainID.Int64()),
Message: []byte{},
MessageHash: testHash,
FormattedRequest: []byte{},
}
// submit ReceiveCircleToken()
// nolint: wrapcheck
err = relay.SubmitReceiveMessage(s.GetTestContext(), &msg)
s.Nil(err)
// verify that the attested request is stored in the db
var storedMsg relayTypes.Message
err = s.testStore.DB().Where("origin_tx_hash = ?", msg.OriginTxHash).First(&storedMsg).Error
s.Nil(err)
msg.State = relayTypes.Submitted
s.Equal(msg, storedMsg)
}
func (s *CCTPRelayerSuite) TestBridgeSynapseCCTP() {
// create a new relayer
mockAPI := attestation.NewMockCircleAPI()
omniRPCClient := omniClient.NewOmnirpcClient(s.testOmnirpc, s.metricsHandler, omniClient.WithCaptureReqRes())
relay, err := relayer.NewCCTPRelayer(s.GetTestContext(), s.GetTestConfig(), s.testStore, omniRPCClient, s.metricsHandler, mockAPI)
s.Nil(err)
// start relayer
ctx, cancel := context.WithCancel(s.GetTestContext())
defer cancel()
//nolint:errcheck
go relay.Run(ctx)
// mint some USDC on send chain
originChain := s.testBackends[0]
destChain := s.testBackends[1]
_, originMockUsdc := s.deployManager.GetMockMintBurnTokenType(s.GetTestContext(), originChain)
originChainID, err := originChain.ChainID(s.GetTestContext())
s.Nil(err)
bridgeAmount := big.NewInt(1000000000) // 1000 USDC
opts := originChain.GetTxContext(s.GetTestContext(), nil)
tx, err := originMockUsdc.MintPublic(opts.TransactOpts, opts.From, bridgeAmount)
s.Nil(err)
originChain.WaitForConfirmation(s.GetTestContext(), tx)
// approve USDC for spending
_, originSynapseCCTP := s.deployManager.GetSynapseCCTP(s.GetTestContext(), originChain)
tx, err = originMockUsdc.Approve(opts.TransactOpts, originSynapseCCTP.Address(), bridgeAmount)
s.Nil(err)
originChain.WaitForConfirmation(s.GetTestContext(), tx)
// send USDC from originChain
destChainID, err := destChain.ChainID(s.GetTestContext())
s.Nil(err)
tx, err = originSynapseCCTP.SendCircleToken(opts.TransactOpts, opts.From, destChainID, originMockUsdc.Address(), bridgeAmount, 0, []byte{})
s.Nil(err)
originChain.WaitForConfirmation(s.GetTestContext(), tx)
// TODO: figure out why log is not streamed properly by relayer.
// for now, inject the log manually
receipt, err := originChain.TransactionReceipt(s.GetTestContext(), tx.Hash())
s.Nil(err)
var sentLog *types.Log
for _, log := range receipt.Logs {
if log.Topics[0] == cctp.CircleRequestSentTopic {
sentLog = log
break
}
}
s.Require().NotNil(sentLog)
_, err = relay.HandleLog(s.GetTestContext(), sentLog, uint32(originChainID.Int64()))
s.Require().Nil(err)
// verify that the confirmed request is stored in the backend
s.Eventually(func() bool {
var storedMsg relayTypes.Message
// TODO: shuld make this check for completion
err = s.testStore.DB().Where("state = ?", relayTypes.Submitted).Last(&storedMsg).Error
if err != nil {
return false
}
return storedMsg.OriginTxHash == tx.Hash().String()
})
// TODO: verify USDC is credited on recv chain
// _, recvMockUsdc := c.deployManager.GetMockMintBurnTokenType(c.GetTestContext(), destChain)
// c.Nil(err)
// expectedBalance := bridgeAmount
// c.Eventually(func() bool {
// balance, err := recvMockUsdc.BalanceOf(nil, opts.From)
// c.Nil(err)
// return c.Equal(expectedBalance, balance)
// })
}