Skip to content

Commit

Permalink
feat: improve README and emit events
Browse files Browse the repository at this point in the history
  • Loading branch information
emidev98 committed Oct 28, 2023
1 parent aa7b8f4 commit 5ef2281
Show file tree
Hide file tree
Showing 17 changed files with 1,895 additions and 390 deletions.
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<h1 align="center">CosmWasm Lifecycle Module</h1>

![IMG](./docs/logo.jpg)

### Abstract

CosmWasm Lifecycle blockchain module leverages [CosmoSDK's lifecycle](https://docs.cosmos.network/main/build/building-modules/beginblock-endblock) to facilitate the execution of smart contracts at the initiation and conclusion of each block. Given the necessity for swift and resource-light execution in both stages, this module mandates [Gov](https://docs.cosmos.network/main/build/modules/gov) voting and demands a collateral deposit for each smart contract on an individual basis. This collateral deposit will be burned if the smart contract fails to execute multiple times.


### Usecases

Automatic execution of smart contracts is very useful because it enables multiple usecases like for example:
- laverage consensus of multiple parties to do oracle data voting,
- decentralized LP rebalancing by a protocol itself,
- automatic disputes resolution,
- rewards claiming and restaking,

As you can see there are multiple use cases that will be enabled with this module.

### Drawbacks

Automatic smart contract executions in consensus is a double edge sword because too many smart contract executions or too many operations in the smart contracts can slowdown the block production significantly.

### Solution to the drawbacks

<table>
<tr>
<td align="center"><img src="./docs/icons/governance.jpg" height="150px"></td>
<td align="center"><img src="./docs/icons/collateral.jpg" height="150px"></td>
<td align="center"><img src="./docs/icons/technology.jpg" height="150px"></td>
</tr>
<tr>
<th><span align="center">Governance</span></th>
<th><span align="center">Collateral</span></th>
<th><span align="center">Technology</span><th>
</tr>
<tr>
<td>
The first messure to the issues is to involve `chain governance` to collectively vote on enabling each smart contract execution at begin or end block. That way the overall community can decide if the usecase and optimization of the smart contract is good enough for the required computation.
</td>
<td>
A secondary measure is to `require a collateral deposit` for each smart contract that will be executed on block lifecycle. This deposit will burned if the smart contract fails the execution many times (which is defined in the module params).
</td>
<td>
The latest measure is to allow smart contracts execution each `n number of blocks`. Which means that execution at end and begin block decreases computation complexity because it does not have to load all the wasm environment and try an execution each time.
</td>
</tr>
</table>

Binary file added docs/icons/collateral.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/icons/governance.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/icons/technology.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
54 changes: 51 additions & 3 deletions proto/emidev98/cosmwasmlifecycle/events.proto
Original file line number Diff line number Diff line change
@@ -1,18 +1,66 @@
syntax = "proto3";
package emidev98.cosmwasmlifecycle;

import "cosmos/base/v1beta1/coin.proto";
import "cosmos_proto/cosmos.proto";
import "emidev98/cosmwasmlifecycle/execution_type.proto";
import "gogoproto/gogo.proto";

option go_package = "github.com/emidev98/cosmwasm-lifecycle/x/cosmwasmlifecycle/types";

// TODO: add remaining events on msgs
message ContractDeleteEvent {
// This event is executed when a contract is registered using MsgRegisterContractProposal
message RegisterContractEvent {
string module_name = 1;
string contract_address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
cosmos.base.v1beta1.Coin contract_deposit = 3 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Coin"
];
ExecutionType execution_type = 4;
int64 block_frequency = 5;
}

// This event is executed when a contract is modified using MsgModifyContractProposal
message ModifyContractEvent {
string module_name = 1;
string contract_address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
ExecutionType new_execution_type = 3;
int64 new_block_frequency = 4;
}

// This event is executed when a contract is removed using MsgRemoveContractProposal
message RemoveContractEvent {
string module_name = 1;
string contract_address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
string refund_account = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
cosmos.base.v1beta1.Coin refund_amount = 4 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Coin"
];
}

// This event is executed when a contract is funded using MsgFundExistentContract
message FundExistentContractEvent {
string module_name = 1;
string contract_address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
string sender_address = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
cosmos.base.v1beta1.Coin deposit_amount = 4 [
(gogoproto.nullable) = false,
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Coin"
];
}

// This event is executed each time a contract returns an error and it's striked
message ContractStrikeEvent {
string module_name = 1;
string contract_address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
int64 current_strike = 3;
string strike_reason = 4;
}
}

// This event is executed when a contract reaches the maximum number of strikes
// the contract is removed and the funds are burn
message ForceRemoveContractEvent {
string module_name = 1;
string contract_address = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
}
24 changes: 0 additions & 24 deletions readme.md

This file was deleted.

74 changes: 70 additions & 4 deletions x/cosmwasmlifecycle/keeper/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,72 @@ import (
"github.com/emidev98/cosmwasm-lifecycle/x/cosmwasmlifecycle/types"
)

// Emits an event to notify that a contract has been
// registerd for execution on begin and end block
func EmitRegisterContractEvent(
ctx sdk.Context,
contractAddress sdk.AccAddress,
contractDeposit sdk.Coin,
executionType types.ExecutionType,
blockFrequency int64,
) error {
return ctx.EventManager().EmitTypedEvent(&types.RegisterContractEvent{
ModuleName: types.ModuleName,
ContractAddress: contractAddress.String(),
ContractDeposit: contractDeposit,
ExecutionType: executionType,
BlockFrequency: blockFrequency,
})
}

// Emits an event to notify that a contract has been
// mondified its execution on begin and end block
func EmitModifyContractEvent(
ctx sdk.Context,
contractAddress sdk.AccAddress,
executionType types.ExecutionType,
blockFrequency int64,
) error {
return ctx.EventManager().EmitTypedEvent(&types.ModifyContractEvent{
ModuleName: types.ModuleName,
ContractAddress: contractAddress.String(),
NewExecutionType: executionType,
NewBlockFrequency: blockFrequency,
})
}

// Emits an event to notify that a contract has been
// remove from its execution on begin and end block
func EmitRemoveContractEvent(
ctx sdk.Context,
contractAddress sdk.AccAddress,
refundAccount sdk.AccAddress,
refundAmount sdk.Coin,
) error {
return ctx.EventManager().EmitTypedEvent(&types.RemoveContractEvent{
ModuleName: types.ModuleName,
ContractAddress: contractAddress.String(),
RefundAccount: refundAccount.String(),
RefundAmount: refundAmount,
})
}

// Emits an event to notify that the contract has been striked
// including the address, current strike and the reason
func EmitFundExistentContractEvent(
ctx sdk.Context,
contractAddress sdk.AccAddress,
senderAddress sdk.AccAddress,
depositAmount sdk.Coin,
) error {
return ctx.EventManager().EmitTypedEvent(&types.FundExistentContractEvent{
ModuleName: types.ModuleName,
ContractAddress: contractAddress.String(),
SenderAddress: senderAddress.String(),
DepositAmount: depositAmount,
})
}

// Emits an event to notify that the contract has been striked
// including the address, current strike and the reason
func EmitContractStrikeEvent(
Expand All @@ -21,13 +87,13 @@ func EmitContractStrikeEvent(
})
}

// Emits an event to notify that the contract has been striked
// including the address, current strike and the reason
func EmitContractDeleteEvent(
// Emits an event to notify that the contract has received the
// last strike that make it be removed from the state execution
func EmitForceRemoveContractEvent(
ctx sdk.Context,
contractAddress sdk.AccAddress,
) error {
return ctx.EventManager().EmitTypedEvent(&types.ContractDeleteEvent{
return ctx.EventManager().EmitTypedEvent(&types.ForceRemoveContractEvent{
ModuleName: types.ModuleName,
ContractAddress: contractAddress.String(),
})
Expand Down
2 changes: 1 addition & 1 deletion x/cosmwasmlifecycle/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func (k Keeper) deleteContractAndBurnDeposit(ctx sdk.Context, contractAddr sdk.A
return err
}

err = EmitContractDeleteEvent(ctx, contractAddr)
err = EmitForceRemoveContractEvent(ctx, contractAddr)
if err != nil {
return err
}
Expand Down
85 changes: 72 additions & 13 deletions x/cosmwasmlifecycle/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,33 @@ func (s msgServer) RegisterContract(ctx context.Context, msg *types.MsgRegisterC
}

sdkCtx := sdk.UnwrapSDKContext(ctx)
contractAddr, err := sdk.AccAddressFromBech32(msg.GetContractAddr())
if err != nil {
return nil, err
}

_, found := s.Keeper.GetContract(sdkCtx, sdk.MustAccAddressFromBech32(msg.GetContractAddr()))
if found {
return nil, types.ErrorContractAlreadyExists
}

contract := types.NewCleanContract(msg.GetExecutionType(), msg.GetExecutionBlocksFrequency(), msg.ContractDeposit)

// store contract
s.Keeper.SetContract(sdkCtx,
sdk.AccAddress(msg.GetContractAddr()),
contract,
s.Keeper.SetContract(sdkCtx, contractAddr, contract)

// Construct and e mit the contract register event
err = EmitRegisterContractEvent(
sdkCtx,
contractAddr,
msg.ContractDeposit,
msg.GetExecutionType(),
msg.GetExecutionBlocksFrequency(),
)
// TODO: emit contract enabled event
if err != nil {
return nil, err
}

return res, err
}

Expand All @@ -60,7 +75,12 @@ func (s msgServer) ModifyContract(ctx context.Context, msg *types.MsgModifyContr
}

sdkCtx := sdk.UnwrapSDKContext(ctx)
contract, found := s.Keeper.GetContract(sdkCtx, sdk.MustAccAddressFromBech32(msg.GetContractAddr()))
contractAddr, err := sdk.AccAddressFromBech32(msg.GetContractAddr())
if err != nil {
return nil, err
}

contract, found := s.Keeper.GetContract(sdkCtx, contractAddr)
if !found {
return nil, types.ErrorContractNotFoundWithAddress
}
Expand Down Expand Up @@ -93,9 +113,20 @@ func (s msgServer) ModifyContract(ctx context.Context, msg *types.MsgModifyContr
}
// Override the execution frequency
contract.ExecutionFrequency = msg.GetExecutionBlocksFrequency()

// store contract
s.Keeper.SetContract(sdkCtx, sdk.MustAccAddressFromBech32(msg.GetContractAddr()), contract)
// TODO: emit contract modify event
s.Keeper.SetContract(sdkCtx, contractAddr, contract)

// Construct and e mit the contract modification event
err = EmitModifyContractEvent(
sdkCtx,
contractAddr,
msg.GetExecutionType(),
msg.GetExecutionBlocksFrequency(),
)
if err != nil {
return nil, err
}

return res, err
}
Expand All @@ -106,30 +137,49 @@ func (s msgServer) RemoveContract(ctx context.Context, msg *types.MsgRemoveContr
return nil, types.ErrorInvalidAuthority
}
sdkCtx := sdk.UnwrapSDKContext(ctx)
contract, found := s.Keeper.GetContract(sdkCtx, sdk.MustAccAddressFromBech32(msg.GetContractAddr()))
contractAddr, err := sdk.AccAddressFromBech32(msg.GetContractAddr())
if err != nil {
return nil, err
}

contract, found := s.Keeper.GetContract(sdkCtx, contractAddr)
if !found {
return nil, types.ErrorContractNotFoundWithAddress
}

s.Keeper.DeleteContract(sdkCtx, sdk.AccAddress(msg.GetContractAddr()))
refundAccount := sdk.AccAddress(msg.DepositRefundAccount)
s.Keeper.DeleteContract(sdkCtx, contractAddr)
err = s.bankKeeper.SendCoinsFromModuleToAccount(
sdkCtx,
types.ModuleName,
sdk.AccAddress(msg.DepositRefundAccount),
refundAccount,
sdk.NewCoins(contract.Deposit),
)
if err != nil {
return nil, err
}
// TODO: emit contract removed event

// Construct and e mit the contract modification event
err = EmitRemoveContractEvent(
sdkCtx,
contractAddr,
refundAccount,
contract.Deposit,
)
if err != nil {
return nil, err
}
return res, err
}

// Fund existent contract execution.
func (s msgServer) FundExistentContract(ctx context.Context, msg *types.MsgFundExistentContract) (res *types.MsgFundExistentContractResponse, err error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
contract, found := s.Keeper.GetContract(sdkCtx, sdk.MustAccAddressFromBech32(msg.GetContractAddr()))
contractAddr, err := sdk.AccAddressFromBech32(msg.GetContractAddr())
if err != nil {
return nil, err
}
contract, found := s.Keeper.GetContract(sdkCtx, contractAddr)
if !found {
return nil, types.ErrorContractNotFoundWithAddress
}
Expand All @@ -139,7 +189,16 @@ func (s msgServer) FundExistentContract(ctx context.Context, msg *types.MsgFundE
}
contract.Deposit = contract.Deposit.Add(msg.Deposit)
// store contract
s.Keeper.SetContract(sdkCtx, sdk.MustAccAddressFromBech32(msg.GetContractAddr()), contract)
s.Keeper.SetContract(sdkCtx, contractAddr, contract)

err = EmitFundExistentContractEvent(sdkCtx,
contractAddr,
sdk.MustAccAddressFromBech32(msg.Sender),
msg.Deposit,
)
if err != nil {
return nil, err
}

return res, err
}
Loading

0 comments on commit 5ef2281

Please sign in to comment.