Skip to content

Commit e29f4a9

Browse files
committed
Problem: dynamic erc20 precompiles are heavy
Closes: #505 re-design bank precompile to support solidity erc20 contract. solc optimize fix readonly comment solc version add to app basic mock test framework test transfer test precompile through erc20 contract more failure test cases cleanup fix go lint use decimals from Display denom add to AvailableStaticPrecompiles add boilerplates use new precompile base fix executor fix test fix lint gas cost changed after register new precompile use cosmos-sdk gas config
1 parent 9db4662 commit e29f4a9

File tree

12 files changed

+1224
-11
lines changed

12 files changed

+1224
-11
lines changed

evmd/app.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import (
44
"encoding/json"
55
"errors"
66
"fmt"
7-
precompiletypes "github.com/cosmos/evm/precompiles/types"
87
"io"
98

9+
precompiletypes "github.com/cosmos/evm/precompiles/types"
10+
1011
"os"
1112

1213
"github.com/spf13/cast"
@@ -492,6 +493,7 @@ func NewExampleApp(
492493
*app.StakingKeeper,
493494
app.DistrKeeper,
494495
app.BankKeeper,
496+
app.BankKeeper,
495497
&app.Erc20Keeper,
496498
&app.TransferKeeper,
497499
app.IBCKeeper.ChannelKeeper,

precompiles/bank2/ERC20.bin

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
60a06040523461022357610ee28038038061001981610227565b9283398101906040818303126102235780516001600160401b0381116102235781019082601f830112156102235781516001600160401b03811161020f5761006a601f8201601f1916602001610227565b9381855260208285010111610223576020815f92828096018388015e8501015201516001600160a01b03811681036102235781516001600160401b03811161020f575f54600181811c91168015610205575b60208210146101f157601f811161018f575b50602092601f821160011461013057928192935f92610125575b50508160011b915f199060031b1c1916175f555b608052604051610c95908161024d8239608051818181610198015281816106680152610a940152f35b015190505f806100e8565b601f198216935f8052805f20915f5b868110610177575083600195961061015f575b505050811b015f556100fc565b01515f1960f88460031b161c191690555f8080610152565b9192602060018192868501518155019401920161013f565b5f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563601f830160051c810191602084106101e7575b601f0160051c01905b8181106101dc57506100ce565b5f81556001016101cf565b90915081906101c6565b634e487b7160e01b5f52602260045260245ffd5b90607f16906100bc565b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761020f5760405256fe60806040526004361015610011575f80fd5b5f3560e01c806306fdde03146100c4578063095ea7b3146100bf57806318160ddd146100ba57806323b872dd146100b5578063313ce567146100b057806370a08231146100ab57806376cdb03b146100a657806395d89b41146100a1578063a9059cbb1461009c578063c370b042146100975763dd62ed3e14610092575f80fd5b61090a565b6108d1565b610704565b61068c565b61061e565b610560565b6104d9565b6103bd565b61033a565b610216565b610112565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080809581815201938051918291828752018686015e5f8582860101520116010190565b346101cc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc576101c86101bc61016a6101966101546107c6565b6040519283915f6020840152602183019061099b565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101835282610780565b7f0000000000000000000000000000000000000000000000000000000000000000610b83565b604051918291826100c9565b0390f35b5f80fd5b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101cc57565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101cc57565b346101cc5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc5761024d6101d0565b602435331561030e5773ffffffffffffffffffffffffffffffffffffffff82169182156102e2576102a88291335f52600160205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b5560405190815233907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590602090a3602060405160018152f35b7f94280d62000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7fe602df05000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b346101cc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc5760206103b061016a61019661037b6107c6565b6040519283917f030000000000000000000000000000000000000000000000000000000000000087840152602183019061099b565b0151604051908152602090f35b346101cc5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc576103f46101d0565b6103fc6101f3565b6044359073ffffffffffffffffffffffffffffffffffffffff83165f5260016020526104493360405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8410610489575b61047d93506109ad565b60405160018152602090f35b8284106104a5576104a08361047d95033383610bdb565b610473565b82847ffb8f41b2000000000000000000000000000000000000000000000000000000005f523360045260245260445260645ffd5b346101cc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc57602061054f61016a61019661051a6107c6565b6040519283917f020000000000000000000000000000000000000000000000000000000000000087840152602183019061099b565b01516040515f9190911a8152602090f35b346101cc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc576101c8602061060c7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006101966105c36101d0565b61016a6105ce6107c6565b6040519485937f04000000000000000000000000000000000000000000000000000000000000008986015260601b166021840152603583019061099b565b01516040519081529081906020820190565b346101cc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101cc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc576101c86101bc61016a6101966106ce6107c6565b6040519283917f01000000000000000000000000000000000000000000000000000000000000006020840152602183019061099b565b346101cc5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc5761074861073e6101d0565b60243590336109ad565b602060405160018152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176107c157604052565b610753565b604051905f5f548060011c916001821680156108c7575b60208410811461089a57838652859260208401919081156108635750600114610810575b5061080e92500383610780565b565b5f80805291507f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5635b84831061084c575061080e9350015f610801565b805482840152869350602090920191600101610838565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001682525061080e93151560051b0190505f610801565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b92607f16926107dd565b346101cc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc576101c86101bc6107c6565b346101cc5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101cc5760206109926109466101d0565b73ffffffffffffffffffffffffffffffffffffffff6109636101f3565b91165f526001835260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b54604051908152f35b805191908290602001825e015f815290565b9073ffffffffffffffffffffffffffffffffffffffff8216918215610afa5773ffffffffffffffffffffffffffffffffffffffff8216938415610ace57610ab87fffffffffffffffffffffffffffffffffffffffff000000000000000000000000610a92610ac99461016a7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9784610a436107c6565b916040519687957f0500000000000000000000000000000000000000000000000000000000000000602088015260601b16602186015260601b166035840152866049840152606983019061099b565b7f0000000000000000000000000000000000000000000000000000000000000000610c49565b506040519081529081906020820190565b0390a3565b7fec442f05000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7f96c6fd1e000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b3d15610b7e573d9067ffffffffffffffff82116107c15760405191610b7360207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610780565b82523d5f602084013e565b606090565b5f918291602082519201905afa610b98610b26565b9015610ba15790565b610bd7906040519182917f0bcb658c000000000000000000000000000000000000000000000000000000008352600483016100c9565b0390fd5b73ffffffffffffffffffffffffffffffffffffffff1690811561030e5773ffffffffffffffffffffffffffffffffffffffff8116156102e257610c46915f52600160205260405f209073ffffffffffffffffffffffffffffffffffffffff165f5260205260405f2090565b55565b5f91829182602083519301915af1610b98610b2656fea26469706673582212206c349a091a8d116c46a490fccb327923675dd9203b2a952fe99cdb4b058beab664736f6c634300081e0033

precompiles/bank2/ERC20.sol

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
library BankPrecompile {
6+
error BankError(bytes);
7+
8+
enum BankMethod {
9+
NAME,
10+
SYMBOL,
11+
DECIMALS,
12+
TOTAL_SUPPLY,
13+
BALANCE_OF,
14+
TRANSFER_FROM
15+
}
16+
17+
function name(address bank, string memory denom) internal view returns (string memory) {
18+
bytes memory result = _staticcall_bank(bank, abi.encodePacked(uint8(BankMethod.NAME), denom));
19+
return string(result);
20+
}
21+
22+
function symbol(address bank, string memory denom) internal view returns (string memory) {
23+
bytes memory result = _staticcall_bank(bank, abi.encodePacked(uint8(BankMethod.SYMBOL), denom));
24+
return string(result);
25+
}
26+
27+
function decimals(address bank, string memory denom) internal view returns (uint8) {
28+
bytes memory data = _staticcall_bank(bank, abi.encodePacked(uint8(BankMethod.DECIMALS), denom));
29+
30+
uint8 result;
31+
assembly {
32+
result := byte(0, mload(add(data, 0x20)))
33+
}
34+
return result;
35+
}
36+
37+
function totalSupply(address bank, string memory denom) internal view returns (uint256) {
38+
bytes memory data = _staticcall_bank(bank, abi.encodePacked(uint8(BankMethod.TOTAL_SUPPLY), denom));
39+
40+
uint256 result;
41+
assembly {
42+
result := mload(add(data, 0x20))
43+
}
44+
return result;
45+
}
46+
47+
function balanceOf(address bank, address account, string memory denom) internal view returns (uint256) {
48+
bytes memory data = _staticcall_bank(bank, abi.encodePacked(uint8(BankMethod.BALANCE_OF), account, denom));
49+
50+
uint256 result;
51+
assembly {
52+
result := mload(add(data, 0x20))
53+
}
54+
return result;
55+
}
56+
57+
function transferFrom(address bank, address from, address to, uint256 amount, string memory denom) internal returns (bool) {
58+
_call_bank(bank, abi.encodePacked(uint8(BankMethod.TRANSFER_FROM), from, to, amount, denom));
59+
return true;
60+
}
61+
62+
function _staticcall_bank(address bank, bytes memory _calldata) internal view returns (bytes memory) {
63+
(bool success, bytes memory data) = bank.staticcall(_calldata);
64+
if (!success) {
65+
revert BankError(data);
66+
}
67+
68+
return data;
69+
}
70+
71+
function _call_bank(address bank, bytes memory _calldata) internal returns (bytes memory) {
72+
(bool success, bytes memory data) = bank.call(_calldata);
73+
if (!success) {
74+
revert BankError(data);
75+
}
76+
77+
return data;
78+
}
79+
}
80+
81+
interface IERC20 {
82+
event Transfer(address indexed from, address indexed to, uint256 value);
83+
event Approval(address indexed owner, address indexed spender, uint256 value);
84+
function totalSupply() external view returns (uint256);
85+
function balanceOf(address account) external view returns (uint256);
86+
function transfer(address to, uint256 value) external returns (bool);
87+
function allowance(address owner, address spender) external view returns (uint256);
88+
function approve(address spender, uint256 value) external returns (bool);
89+
function transferFrom(address from, address to, uint256 value) external returns (bool);
90+
}
91+
92+
interface IERC20Metadata is IERC20 {
93+
function name() external view returns (string memory);
94+
function symbol() external view returns (string memory);
95+
function decimals() external view returns (uint8);
96+
}
97+
98+
interface IERC20Errors {
99+
error ERC20InvalidSender(address sender);
100+
error ERC20InvalidReceiver(address receiver);
101+
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
102+
error ERC20InvalidApprover(address approver);
103+
error ERC20InvalidSpender(address spender);
104+
}
105+
106+
contract ERC20 is IERC20, IERC20Metadata, IERC20Errors {
107+
using BankPrecompile for address;
108+
109+
string public denom;
110+
mapping(address account => mapping(address spender => uint256)) public allowance;
111+
112+
address public immutable bank;
113+
114+
constructor(string memory denom_, address bank_) {
115+
denom = denom_;
116+
bank = bank_;
117+
}
118+
119+
function name() public view returns (string memory) {
120+
return bank.name(denom);
121+
}
122+
123+
function symbol() public view returns (string memory) {
124+
return bank.symbol(denom);
125+
}
126+
127+
function decimals() public view returns (uint8) {
128+
return bank.decimals(denom);
129+
}
130+
131+
function totalSupply() public view returns (uint256) {
132+
return bank.totalSupply(denom);
133+
}
134+
135+
function balanceOf(address account) public view returns (uint256) {
136+
return bank.balanceOf(account, denom);
137+
}
138+
139+
function transfer(address to, uint256 value) public returns (bool) {
140+
_transfer(msg.sender, to, value);
141+
return true;
142+
}
143+
144+
function approve(address spender, uint256 value) public returns (bool) {
145+
_approve(msg.sender, spender, value);
146+
return true;
147+
}
148+
149+
function transferFrom(address from, address to, uint256 value) public returns (bool) {
150+
address spender = msg.sender;
151+
_spendAllowance(from, spender, value);
152+
_transfer(from, to, value);
153+
return true;
154+
}
155+
156+
function _transfer(address from, address to, uint256 value) internal {
157+
if (from == address(0)) {
158+
revert ERC20InvalidSender(address(0));
159+
}
160+
if (to == address(0)) {
161+
revert ERC20InvalidReceiver(address(0));
162+
}
163+
164+
bank.transferFrom(from, to, value, denom);
165+
emit Transfer(from, to, value);
166+
}
167+
168+
function _approve(address owner, address spender, uint256 value) internal {
169+
_approve(owner, spender, value, true);
170+
}
171+
172+
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
173+
if (owner == address(0)) {
174+
revert ERC20InvalidApprover(address(0));
175+
}
176+
if (spender == address(0)) {
177+
revert ERC20InvalidSpender(address(0));
178+
}
179+
allowance[owner][spender] = value;
180+
if (emitEvent) {
181+
emit Approval(owner, spender, value);
182+
}
183+
}
184+
185+
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
186+
uint256 currentAllowance = allowance[owner][spender];
187+
if (currentAllowance < type(uint256).max) {
188+
if (currentAllowance < value) {
189+
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
190+
}
191+
unchecked {
192+
_approve(owner, spender, currentAllowance - value, false);
193+
}
194+
}
195+
}
196+
}

0 commit comments

Comments
 (0)