Skip to content

Commit

Permalink
Merge pull request #255 from PeggyJV/collin/gov-multicall
Browse files Browse the repository at this point in the history
feat:Support parsing multicall in governance functions
  • Loading branch information
cbrit committed Feb 20, 2024
2 parents e46f770 + bcdc28e commit d373270
Show file tree
Hide file tree
Showing 20 changed files with 6,064 additions and 2,694 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ jobs:
strategy:
fail-fast: false
matrix:
test_type: [ "AaveV2Stablecoin", "CellarV1", "CellarV2", "CellarV2_2", "ScheduledCorkProposal", "ScheduledAxelarCorkProposal" ]
test_type: [ "AaveV2Stablecoin", "CellarV1", "CellarV2", "CellarV2_2", "ScheduledCorkProposal", "ScheduledAxelarCorkProposal", "ScheduledCorkMulticallProposal"]
steps:
- name: Set up Go 1.19
uses: actions/setup-go@v2
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "steward"
authors = []
version = "4.1.3"
version = "4.2.0"
edition = "2021"

[dependencies]
Expand Down
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.DEFAULT_GOAL := e2e_cork_test

VALIDATOR_IMAGE := "ghcr.io/peggyjv/sommelier-sommelier:main"
SOMMELIER_VERSION := "v7.0.1"
VALIDATOR_IMAGE := "ghcr.io/peggyjv/sommelier-sommelier:$(SOMMELIER_VERSION)"
ORCHESTRATOR_IMAGE := "ghcr.io/peggyjv/gravity-bridge-orchestrator:main"

go_protos:
Expand All @@ -20,7 +21,7 @@ e2e_build_images: e2e_clean_slate
e2e_clean_slate:
@scripts/clean_slate.sh

e2e_cork_test: e2e_aave_v2_stablecoin_test e2e_cellar_v1_test e2e_cellar_v2_test e2e_cellar_v2_2_test e2e_scheduled_cork_proposal_test e2e_scheduled_axelar_cork_proposal_test
e2e_cork_test: e2e_aave_v2_stablecoin_test e2e_cellar_v1_test e2e_cellar_v2_test e2e_cellar_v2_2_test e2e_scheduled_cork_proposal_test e2e_scheduled_axelar_cork_proposal_test e2e_scheduled_cork_proposal_multicall_test

# Because of the way `make` works, using the e2e_clean_slate as as a prerequisite for
# the individual tests doesn't work when `e2e_cork_test` runs the test targets in series,
Expand Down Expand Up @@ -49,6 +50,10 @@ e2e_scheduled_axelar_cork_proposal_test:
@scripts/clean_slate.sh
@E2E_SKIP_CLEANUP=true integration_tests/integration_tests.test -test.failfast -test.v -test.run IntegrationTestSuite -testify.m TestScheduledAxelarCorkProposal || make -s fail

e2e_scheduled_cork_proposal_multicall_test:
@scripts/clean_slate.sh
@E2E_SKIP_CLEANUP=true integration_tests/integration_tests.test -test.failfast -test.v -test.run IntegrationTestSuite -testify.m TestScheduledCorkMulticallProposal || make -s fail

fail:
@echo 'test failed; dumping container logs into ./testdata for review'
@mkdir -p ./testdata
Expand Down
312 changes: 311 additions & 1 deletion integration_tests/cellar_v2_2_abi.go

Large diffs are not rendered by default.

52 changes: 52 additions & 0 deletions integration_tests/ethereum/contracts/MockCellarV2.2.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "adaptor",
"type": "address"
}
],
"name": "AddAdaptorToCatalogue",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint32",
"name": "position",
"type": "uint32"
}
],
"name": "AddPositionToCatalogue",
"type": "event"
},
{
"anonymous": false,
"inputs": [
Expand Down Expand Up @@ -54,6 +80,32 @@
"name": "OwnerUpdated",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "adaptor",
"type": "address"
}
],
"name": "addAdaptorToCatalogue",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "position",
"type": "uint32"
}
],
"name": "addPositionToCatalogue",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down
12 changes: 12 additions & 0 deletions integration_tests/ethereum/contracts/MockCellarV2.2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,16 @@ contract CellarV2_2 is Owned {

emit CallOnAdaptor(data);
}

event AddAdaptorToCatalogue(address adaptor);

function addAdaptorToCatalogue(address adaptor) external onlyOwner {
emit AddAdaptorToCatalogue(adaptor);
}

event AddPositionToCatalogue(uint32 position);

function addPositionToCatalogue(uint32 position) external onlyOwner {
emit AddPositionToCatalogue(position);
}
}
243 changes: 243 additions & 0 deletions integration_tests/proposal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,249 @@ func (s *IntegrationTestSuite) TestScheduledCorkProposal() {
}, 3*time.Minute, 10*time.Second, "cellar event never seen")
}

func (s *IntegrationTestSuite) TestScheduledCorkMulticallProposal() {
s.checkCellarExists(vaultCellar)

orch := s.chain.orchestrators[0]
orchClientCtx, err := s.chain.clientContext("tcp://localhost:26657", orch.keyring, "orch", orch.address())
s.Require().NoError(err)
currentHeight, err := s.GetLatestBlockHeight(orchClientCtx)
s.Require().NoError(err)
protoJson := fmt.Sprintf(`
{
"call": {
"CellarV22": {
"call_type": {
"Multicall": {
"function_calls": [
{
"function": {
"AddAdaptorToCatalogue": {
"adaptor": "%s"
}
}
},
{
"function": {
"AddPositionToCatalogue": {
"position_id": 1
}
}
}
]
}
}
}
}
}
`, adaptorContract.Hex())

targetBlockHeight := currentHeight + 90
proposal := corktypes.NewScheduledCorkProposal(
"scheduled cork proposal test",
"description",
uint64(targetBlockHeight),
v2_2Cellar.String(),
protoJson,
)

proposalMsg, err := govtypesv1beta1.NewMsgSubmitProposal(
proposal,
sdk.Coins{
{
Denom: testDenom,
Amount: math.NewInt(1000000),
},
},
orch.address(),
)
s.Require().NoError(err, "Unable to create governance proposal")

s.T().Log("Submit proposal")
submitProposalResponse, err := s.chain.sendMsgs(*orchClientCtx, proposalMsg)
s.Require().NoError(err)
s.Require().Zero(submitProposalResponse.Code, "raw log: %s", submitProposalResponse.RawLog)

s.T().Log("Check proposal was submitted correctly")
govQueryClient := govtypesv1beta1.NewQueryClient(orchClientCtx)
var proposalID uint64
s.Require().Eventually(func() bool {
proposalsQueryResponse, err := govQueryClient.Proposals(context.Background(), &govtypesv1beta1.QueryProposalsRequest{})
if err != nil {
s.T().Logf("error querying proposals: %e", err)
return false
}

if len(proposalsQueryResponse.Proposals) == 0 {
return false
}

for _, p := range proposalsQueryResponse.Proposals {
if p.Content.TypeUrl == "/cork.v2.ScheduledCorkProposal" {
s.Require().Equal(govtypesv1beta1.StatusVotingPeriod, p.Status, "proposal not in voting period")
proposalID = p.ProposalId
return true
}
}

return false
}, time.Second*30, time.Second*5, "proposal submission was never found")

s.T().Log("Vote for proposal")
// wait so the client for val0 will be aware of the latest tx sequence
time.Sleep(time.Second * 10)
for _, val := range s.chain.validators {
kr, err := val.keyring()
s.Require().NoError(err)
localClientCtx, err := s.chain.clientContext("tcp://localhost:26657", &kr, "val", val.address())
s.Require().NoError(err)

voteMsg := govtypesv1beta1.NewMsgVote(val.address(), proposalID, govtypesv1beta1.OptionYes)
voteResponse, err := s.chain.sendMsgs(*localClientCtx, voteMsg)
s.Require().NoError(err)
s.Require().Zero(voteResponse.Code, "Vote error: %s", voteResponse.RawLog)
}

s.T().Log("Waiting for proposal to be approved..")
s.Require().Eventually(func() bool {
proposalQueryResponse, _ := govQueryClient.Proposal(context.Background(), &govtypesv1beta1.QueryProposalRequest{ProposalId: proposalID})
return govtypesv1beta1.StatusPassed == proposalQueryResponse.Proposal.Status
}, time.Second*30, time.Second*5, "proposal was never accepted")
s.T().Log("Proposal approved!")

s.T().Log("Waiting for scheduled cork to be created by steward")
corkQueryClient := corktypes.NewQueryClient(orchClientCtx)
s.Require().Eventually(func() bool {
proposalQueryResponse, _ := corkQueryClient.QueryScheduledCorks(context.Background(), &corktypes.QueryScheduledCorksRequest{})
return len(proposalQueryResponse.Corks) > 0
}, time.Second*120, time.Second*2, "corks never scheduled")

s.T().Log("wait for scheduled height")
s.Require().Eventuallyf(func() bool {
currentHeight, err := s.GetLatestBlockHeight(orchClientCtx)
if err != nil {
s.T().Logf("error quering latest height (probably transient): %s", err)
return false
}
if currentHeight >= targetBlockHeight {
return true
} else {
res, err := corkQueryClient.QueryScheduledCorks(context.Background(), &types.QueryScheduledCorksRequest{})
if err != nil {
s.T().Logf("error: %s", err)
return false
}

s.T().Logf("call: %s, height: %d, address: %s", hex.EncodeToString(res.Corks[0].Cork.EncodedContractCall), res.Corks[0].BlockHeight, res.Corks[0].Cork.TargetContractAddress)
// verify that the scheduled corks have not yet been consumed
s.Require().Len(res.Corks, len(s.chain.validators))
}

return false
}, 3*time.Minute, 10*time.Second, "never reached scheduled height")

s.T().Logf("checking for cellar events")
s.Require().Eventuallyf(func() bool {
s.T().Log("querying add adaptor cellar event...")
ethClient, err := ethclient.Dial(fmt.Sprintf("http://%s", s.ethResource.GetHostPort("8545/tcp")))
if err != nil {
return false
}

// For non-anonymous events, the first log topic is a keccak256 hash of the
// event signature.
eventSignature := []byte("AddAdaptorToCatalogue(address)")
mockEventSignatureTopic := crypto.Keccak256Hash(eventSignature)
query := ethereum.FilterQuery{
FromBlock: nil,
ToBlock: nil,
Addresses: []common.Address{
v2_2Cellar,
},
Topics: [][]common.Hash{
{
mockEventSignatureTopic,
},
},
}

logs, err := ethClient.FilterLogs(context.Background(), query)
if err != nil {
ethClient.Close()
return false
}

vault_abi, err := CellarV22MetaData.GetAbi()
s.Require().NoError(err)

if len(logs) > 0 {
s.T().Logf("found %d logs!", len(logs))
for _, log := range logs {
if len(log.Data) > 0 {
var event CellarV22AddAdaptorToCatalogue
err := vault_abi.UnpackIntoInterface(&event, "AddAdaptorToCatalogue", log.Data)
s.Require().NoError(err, "failed to unpack AddAdaptorToCatalogue event from log data")
s.Require().Equal(common.HexToAddress(adaptorContract.Hex()), event.Adaptor)

return true
}
}
}

return false
}, 3*time.Minute, 10*time.Second, "add adaptor cellar event never seen")

s.Require().Eventuallyf(func() bool {
s.T().Log("querying add position cellar event...")
ethClient, err := ethclient.Dial(fmt.Sprintf("http://%s", s.ethResource.GetHostPort("8545/tcp")))
if err != nil {
return false
}

// For non-anonymous events, the first log topic is a keccak256 hash of the
// event signature.
eventSignature := []byte("AddPositionToCatalogue(uint32)")
mockEventSignatureTopic := crypto.Keccak256Hash(eventSignature)
query := ethereum.FilterQuery{
FromBlock: nil,
ToBlock: nil,
Addresses: []common.Address{
v2_2Cellar,
},
Topics: [][]common.Hash{
{
mockEventSignatureTopic,
},
},
}

logs, err := ethClient.FilterLogs(context.Background(), query)
if err != nil {
ethClient.Close()
return false
}

vault_abi, err := CellarV22MetaData.GetAbi()
s.Require().NoError(err)

if len(logs) > 0 {
s.T().Logf("found %d logs!", len(logs))
for _, log := range logs {
if len(log.Data) > 0 {
var event CellarV22AddPositionToCatalogue
err := vault_abi.UnpackIntoInterface(&event, "AddPositionToCatalogue", log.Data)
s.Require().NoError(err, "failed to unpack AddPositionToCatalogue event from log data")
s.Require().Equal(uint32(1), event.Position)

return true
}
}
}

return false
}, 3*time.Minute, 10*time.Second, "add position cellar event never seen")
}

func (s *IntegrationTestSuite) TestScheduledAxelarCorkProposal() {
s.checkCellarExists(vaultCellar)

Expand Down
2 changes: 1 addition & 1 deletion proto/steward/v4/cellar_v1.proto
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ message CellarV1 {
SetLiquidityLimit set_liquidity_limit = 10;
// Represents function `setShareLockPeriod()`
SetShareLockPeriod set_share_lock_period = 11;
// Represents function `setRebalanceDeviation(uint265)`
// Represents function `setRebalanceDeviation(uint256)`
SetRebalanceDeviation set_rebalance_deviation = 12;
}

Expand Down
Loading

0 comments on commit d373270

Please sign in to comment.