Skip to content

Commit dc8bb01

Browse files
authored
Merge pull request #157 from xpladev/feature/precompile
feat: precompile contracts
2 parents 3ee8052 + caf9e70 commit dc8bb01

28 files changed

+1487
-14
lines changed

app/keepers/keepers.go

+12
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import (
6565
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"
6666
slashingkeeper "github.com/cosmos/cosmos-sdk/x/slashing/keeper"
6767
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
68+
stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
6869
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
6970

7071
"github.com/CosmWasm/wasmd/x/wasm"
@@ -79,6 +80,7 @@ import (
7980
xplaauthkeeper "github.com/xpladev/xpla/x/auth/keeper"
8081
xplabankkeeper "github.com/xpladev/xpla/x/bank/keeper"
8182

83+
"github.com/xpladev/xpla/precompile"
8284
rewardkeeper "github.com/xpladev/xpla/x/reward/keeper"
8385
rewardtypes "github.com/xpladev/xpla/x/reward/types"
8486
xplastakingkeeper "github.com/xpladev/xpla/x/staking/keeper"
@@ -578,6 +580,16 @@ func NewAppKeeper(
578580
govModAddress,
579581
)
580582

583+
// Register the precompiled contracts
584+
precompile.RegistPrecompiledContract(
585+
appKeepers.AccountKeeper,
586+
appKeepers.BankKeeper,
587+
stakingkeeper.NewMsgServerImpl(appKeepers.StakingKeeper.Keeper),
588+
distrkeeper.NewMsgServerImpl(appKeepers.DistrKeeper),
589+
wasmkeeper.NewMsgServerImpl(&appKeepers.WasmKeeper),
590+
appKeepers.WasmKeeper,
591+
)
592+
581593
return appKeepers
582594
}
583595

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ replace (
261261
github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2
262262

263263
// xpla features
264-
github.com/ethereum/go-ethereum => github.com/xpladev/go-ethereum v1.10.26-xpla
264+
github.com/ethereum/go-ethereum => github.com/xpladev/go-ethereum v1.10.27-0.20241101091644-9889bb150ee0
265265

266266
// Fix upstream GHSA-h395-qcrw-5vmq vulnerability.
267267
// TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -1157,8 +1157,8 @@ github.com/xpladev/cosmos-sdk v0.50.10-xpla h1:7ZLmg0tjYmsHZaRn5XJx1BcITB1fuGe6P
11571157
github.com/xpladev/cosmos-sdk v0.50.10-xpla/go.mod h1:6Eesrx3ZE7vxBZWpK++30H+Uc7Q4ahQWCL7JKU/LEdU=
11581158
github.com/xpladev/ethermint v0.24.0-xpla h1:mBOkS7BPOYIQddkCn/hDJxx5MbcnzSpkombz8xBdaR8=
11591159
github.com/xpladev/ethermint v0.24.0-xpla/go.mod h1:8Zb1vDEyjCo+xbyZqExLXdG7aOH8AYiL2jW0nPsMUOk=
1160-
github.com/xpladev/go-ethereum v1.10.26-xpla h1:tz9WTV9Fhhu9Xcpxo7RcXBygKcUbb5Zps4PBOVFgRqQ=
1161-
github.com/xpladev/go-ethereum v1.10.26-xpla/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
1160+
github.com/xpladev/go-ethereum v1.10.27-0.20241101091644-9889bb150ee0 h1:aUVjsSerLJdRHWZhosPHx5orFm7qBElfhIpkeJ2oQ0o=
1161+
github.com/xpladev/go-ethereum v1.10.27-0.20241101091644-9889bb150ee0/go.mod h1:EYFyF19u3ezGLD4RqOkLq+ZCXzYbLoNDdZlMt7kyKFg=
11621162
github.com/xpladev/ledger-cosmos-go v0.13.3-xpla h1:TG+QZ/sudMH2iPF+aJnYs+P9ua6k6KGxBS7Sa6XwjF4=
11631163
github.com/xpladev/ledger-cosmos-go v0.13.3-xpla/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8=
11641164
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=

precompile/bank/IBank.abi

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"inputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"string","name":"denom","type":"string"}],"name":"balance","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromAddress","type":"address"},{"internalType":"address","name":"toAddress","type":"address"},{"internalType":"string","name":"denom","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"send","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"denom","type":"string"}],"name":"supplyOf","outputs":[{"internalType":"uint256","name":"supply","type":"uint256"}],"stateMutability":"view","type":"function"}]

precompile/bank/IBank.sol

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
address constant BANK_PRECOMPILE_ADDRESS = 0x1000000000000000000000000000000000000001;
5+
6+
IBank constant BANK_CONTRACT = IBank(
7+
BANK_PRECOMPILE_ADDRESS
8+
);
9+
10+
interface IBank {
11+
// Transactions
12+
function send(
13+
address fromAddress,
14+
address toAddress,
15+
string calldata denom,
16+
uint256 amount
17+
) external returns (bool success);
18+
19+
// Queries
20+
function balance(
21+
address addr,
22+
string memory denom
23+
) external view returns (uint256 balance);
24+
25+
function supplyOf(
26+
string memory denom
27+
) external view returns (uint256 supply);
28+
}

precompile/bank/bank.go

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package bank
2+
3+
import (
4+
"embed"
5+
"errors"
6+
7+
"github.com/ethereum/go-ethereum/accounts/abi"
8+
"github.com/ethereum/go-ethereum/common"
9+
"github.com/ethereum/go-ethereum/core/vm"
10+
11+
sdk "github.com/cosmos/cosmos-sdk/types"
12+
13+
"github.com/xpladev/ethermint/x/evm/statedb"
14+
15+
"github.com/xpladev/xpla/precompile/util"
16+
)
17+
18+
var _ vm.PrecompiledContract = PrecompiledBank{}
19+
20+
var (
21+
Address = common.HexToAddress(hexAddress)
22+
ABI = abi.ABI{}
23+
24+
//go:embed IBank.abi
25+
abiFS embed.FS
26+
)
27+
28+
type PrecompiledBank struct {
29+
bk BankKeeper
30+
}
31+
32+
func init() {
33+
var err error
34+
ABI, err = util.LoadABI(abiFS, abiFile)
35+
if err != nil {
36+
panic(err)
37+
}
38+
}
39+
40+
func NewPrecompiledBank(bk BankKeeper) PrecompiledBank {
41+
return PrecompiledBank{bk: bk}
42+
}
43+
44+
func (p PrecompiledBank) RequiredGas(input []byte) uint64 {
45+
// Implement the method as needed
46+
return 0
47+
}
48+
49+
func (p PrecompiledBank) Run(evm *vm.EVM, input []byte) ([]byte, error) {
50+
method, argsBz := util.SplitInput(input)
51+
52+
abiMethod, err := ABI.MethodById(method)
53+
if err != nil {
54+
return nil, err
55+
}
56+
57+
args, err := abiMethod.Inputs.Unpack(argsBz)
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
ctx := evm.StateDB.(*statedb.StateDB).GetContext()
63+
64+
switch MethodBank(abiMethod.Name) {
65+
case Balance:
66+
return p.balance(ctx, abiMethod, args)
67+
case Send:
68+
return p.send(ctx, evm.Origin, abiMethod, args)
69+
case Supply:
70+
return p.supplyOf(ctx, abiMethod, args)
71+
default:
72+
return nil, errors.New("method not found")
73+
}
74+
}
75+
76+
func (p PrecompiledBank) balance(ctx sdk.Context, method *abi.Method, args []interface{}) ([]byte, error) {
77+
78+
address, err := util.GetAccAddress(args[0])
79+
if err != nil {
80+
return nil, err
81+
}
82+
83+
denom, err := util.GetString(args[1])
84+
if err != nil {
85+
return nil, err
86+
}
87+
88+
coin := p.bk.GetBalance(ctx, address, denom)
89+
90+
return method.Outputs.Pack(coin.Amount.BigInt())
91+
}
92+
93+
func (p PrecompiledBank) supplyOf(ctx sdk.Context, method *abi.Method, args []interface{}) ([]byte, error) {
94+
denom, err := util.GetString(args[0])
95+
if err != nil {
96+
return nil, err
97+
}
98+
99+
coin := p.bk.GetSupply(ctx, denom)
100+
101+
return method.Outputs.Pack(coin.Amount.BigInt())
102+
}
103+
104+
func (p PrecompiledBank) send(ctx sdk.Context, sender common.Address, method *abi.Method, args []interface{}) ([]byte, error) {
105+
106+
fromAddress, err := util.GetAccAddress(args[0])
107+
if err != nil {
108+
return nil, err
109+
}
110+
111+
if err = util.ValidateSigner(fromAddress, sender); err != nil {
112+
return nil, err
113+
}
114+
115+
toAddress, err := util.GetAccAddress(args[1])
116+
if err != nil {
117+
return nil, err
118+
}
119+
120+
denom, err := util.GetString(args[2])
121+
if err != nil {
122+
return nil, err
123+
}
124+
125+
amount, err := util.GetBigInt(args[3])
126+
if err != nil {
127+
return nil, err
128+
}
129+
130+
err = p.bk.SendCoins(ctx, fromAddress, toAddress, sdk.NewCoins(sdk.NewCoin(denom, amount)))
131+
if err != nil {
132+
return nil, err
133+
}
134+
135+
return method.Outputs.Pack(true)
136+
}

precompile/bank/const.go

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package bank
2+
3+
const (
4+
hexAddress = "0x1000000000000000000000000000000000000001"
5+
abiFile = "IBank.abi"
6+
)
7+
8+
type MethodBank string
9+
10+
const (
11+
Balance MethodBank = "balance"
12+
Send MethodBank = "send"
13+
Supply MethodBank = "supplyOf"
14+
)

precompile/bank/expected_keepers.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package bank
2+
3+
import (
4+
"context"
5+
6+
sdk "github.com/cosmos/cosmos-sdk/types"
7+
)
8+
9+
type BankKeeper interface {
10+
GetBalance(context.Context, sdk.AccAddress, string) sdk.Coin
11+
GetSupply(context.Context, string) sdk.Coin
12+
SendCoins(context.Context, sdk.AccAddress, sdk.AccAddress, sdk.Coins) error
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"inputs":[{"internalType":"address","name":"delegatorAddress","type":"address"},{"internalType":"address","name":"validatorAddress","type":"address"}],"name":"withdrawDelegatorReward","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
address constant DISTRIBUTION_PRECOMPILE_ADDRESS = 0x1000000000000000000000000000000000000003;
5+
6+
IDistribution constant DISTRIBUTION_CONTRACT = IDistribution(
7+
DISTRIBUTION_PRECOMPILE_ADDRESS
8+
);
9+
10+
interface IDistribution {
11+
// Transactions
12+
function withdrawDelegatorReward(
13+
address delegatorAddress,
14+
address validatorAddress
15+
) external returns (uint256 amount);
16+
}

precompile/distribution/const.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package distribution
2+
3+
const (
4+
hexAddress = "0x1000000000000000000000000000000000000003"
5+
abiFile = "IDistribution.abi"
6+
)
7+
8+
type MethodDistribution string
9+
10+
const (
11+
WithdrawDelegatorReward MethodDistribution = "withdrawDelegatorReward"
12+
)
+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package distribution
2+
3+
import (
4+
"context"
5+
"embed"
6+
"errors"
7+
"math/big"
8+
9+
"github.com/ethereum/go-ethereum/accounts/abi"
10+
"github.com/ethereum/go-ethereum/common"
11+
"github.com/ethereum/go-ethereum/core/vm"
12+
13+
sdk "github.com/cosmos/cosmos-sdk/types"
14+
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
15+
16+
"github.com/xpladev/ethermint/x/evm/statedb"
17+
18+
"github.com/xpladev/xpla/precompile/util"
19+
)
20+
21+
var _ vm.PrecompiledContract = PrecompiledDistribution{}
22+
23+
var (
24+
Address = common.HexToAddress(hexAddress)
25+
ABI = abi.ABI{}
26+
27+
//go:embed IDistribution.abi
28+
abiFS embed.FS
29+
)
30+
31+
type PrecompiledDistribution struct {
32+
dk DistributionKeeper
33+
}
34+
35+
func init() {
36+
var err error
37+
ABI, err = util.LoadABI(abiFS, abiFile)
38+
if err != nil {
39+
panic(err)
40+
}
41+
}
42+
43+
func NewPrecompiledDistribution(dk DistributionKeeper) PrecompiledDistribution {
44+
return PrecompiledDistribution{dk: dk}
45+
}
46+
47+
func (p PrecompiledDistribution) RequiredGas(input []byte) uint64 {
48+
// Implement the method as needed
49+
return 0
50+
}
51+
52+
func (p PrecompiledDistribution) Run(evm *vm.EVM, input []byte) ([]byte, error) {
53+
method, argsBz := util.SplitInput(input)
54+
55+
abiMethod, err := ABI.MethodById(method)
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
args, err := abiMethod.Inputs.Unpack(argsBz)
61+
if err != nil {
62+
return nil, err
63+
}
64+
65+
ctx := evm.StateDB.(*statedb.StateDB).GetContext()
66+
67+
switch MethodDistribution(abiMethod.Name) {
68+
case WithdrawDelegatorReward:
69+
return p.withdrawDelegatorReward(ctx, evm.Origin, abiMethod, args)
70+
default:
71+
return nil, errors.New("method not found")
72+
}
73+
}
74+
75+
func (p PrecompiledDistribution) withdrawDelegatorReward(ctx context.Context, sender common.Address, method *abi.Method, args []interface{}) ([]byte, error) {
76+
delegatorAddress, err := util.GetAccAddress(args[0])
77+
if err != nil {
78+
return nil, err
79+
}
80+
81+
if err = util.ValidateSigner(delegatorAddress, sender); err != nil {
82+
return nil, err
83+
}
84+
85+
validatorAddress, err := util.GetAccAddress(args[1])
86+
if err != nil {
87+
return nil, err
88+
}
89+
90+
msg := distributiontypes.NewMsgWithdrawDelegatorReward(delegatorAddress.String(), sdk.ValAddress(validatorAddress.Bytes()).String())
91+
92+
res, err := p.dk.WithdrawDelegatorReward(ctx, msg)
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
amount := big.NewInt(0)
98+
if !res.Amount.IsZero() {
99+
amount = res.Amount[0].Amount.BigInt()
100+
}
101+
102+
return method.Outputs.Pack(amount)
103+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package distribution
2+
3+
import (
4+
"context"
5+
6+
distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
7+
)
8+
9+
type DistributionKeeper interface {
10+
WithdrawDelegatorReward(ctx context.Context, msg *distributiontypes.MsgWithdrawDelegatorReward) (*distributiontypes.MsgWithdrawDelegatorRewardResponse, error)
11+
}

0 commit comments

Comments
 (0)