diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index e619cf7c..0b64973d 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -380,6 +380,7 @@ func NewAppKeeper( appKeepers.Erc20Keeper, appKeepers.AccountKeeper, appKeepers.EvmKeeper, + contract.NewBridgeFeeQuoteKeeper(appKeepers.EvmKeeper, contract.BridgeFeeAddress), authAddr, ) appKeepers.PolygonKeeper = crosschainkeeper.NewKeeper( @@ -394,6 +395,7 @@ func NewAppKeeper( appKeepers.Erc20Keeper, appKeepers.AccountKeeper, appKeepers.EvmKeeper, + contract.NewBridgeFeeQuoteKeeper(appKeepers.EvmKeeper, contract.BridgeFeeAddress), authAddr, ) appKeepers.AvalancheKeeper = crosschainkeeper.NewKeeper( @@ -408,6 +410,7 @@ func NewAppKeeper( appKeepers.Erc20Keeper, appKeepers.AccountKeeper, appKeepers.EvmKeeper, + contract.NewBridgeFeeQuoteKeeper(appKeepers.EvmKeeper, contract.BridgeFeeAddress), authAddr, ) appKeepers.EthKeeper = crosschainkeeper.NewKeeper( @@ -422,6 +425,7 @@ func NewAppKeeper( appKeepers.Erc20Keeper, appKeepers.AccountKeeper, appKeepers.EvmKeeper, + contract.NewBridgeFeeQuoteKeeper(appKeepers.EvmKeeper, contract.BridgeFeeAddress), authAddr, ) appKeepers.ArbitrumKeeper = crosschainkeeper.NewKeeper( @@ -436,6 +440,7 @@ func NewAppKeeper( appKeepers.Erc20Keeper, appKeepers.AccountKeeper, appKeepers.EvmKeeper, + contract.NewBridgeFeeQuoteKeeper(appKeepers.EvmKeeper, contract.BridgeFeeAddress), authAddr, ) appKeepers.OptimismKeeper = crosschainkeeper.NewKeeper( @@ -450,6 +455,7 @@ func NewAppKeeper( appKeepers.Erc20Keeper, appKeepers.AccountKeeper, appKeepers.EvmKeeper, + contract.NewBridgeFeeQuoteKeeper(appKeepers.EvmKeeper, contract.BridgeFeeAddress), authAddr, ) appKeepers.Layer2Keeper = crosschainkeeper.NewKeeper( @@ -464,6 +470,7 @@ func NewAppKeeper( appKeepers.Erc20Keeper, appKeepers.AccountKeeper, appKeepers.EvmKeeper, + contract.NewBridgeFeeQuoteKeeper(appKeepers.EvmKeeper, contract.BridgeFeeAddress), authAddr, ) appKeepers.TronKeeper = crosschainkeeper.NewKeeper( @@ -478,6 +485,7 @@ func NewAppKeeper( appKeepers.Erc20Keeper, appKeepers.AccountKeeper, appKeepers.EvmKeeper, + contract.NewBridgeFeeQuoteKeeper(appKeepers.EvmKeeper, contract.BridgeFeeAddress), authAddr, ) diff --git a/contract/bridge_fee_quote.go b/contract/bridge_fee_quote.go new file mode 100644 index 00000000..e6589bce --- /dev/null +++ b/contract/bridge_fee_quote.go @@ -0,0 +1,45 @@ +package contract + +import ( + "context" + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/evmos/ethermint/x/evm/types" +) + +type BrideFeeQuoteKeeper struct { + Caller + abi abi.ABI + from common.Address + contract common.Address +} + +func NewBridgeFeeQuoteKeeper(caller Caller, contract string) BrideFeeQuoteKeeper { + return BrideFeeQuoteKeeper{ + Caller: caller, + abi: bridgeFeeQuoteABI, + // evm module address + from: common.BytesToAddress(authtypes.NewModuleAddress(types.ModuleName).Bytes()), + contract: common.HexToAddress(contract), + } +} + +func (k BrideFeeQuoteKeeper) GetQuotesByToken(ctx context.Context, chainName, tokenName string) ([]IBridgeFeeQuoteQuoteInfo, error) { + var res struct{ Quotes []IBridgeFeeQuoteQuoteInfo } + if err := k.QueryContract(sdk.UnwrapSDKContext(ctx), k.from, k.contract, k.abi, "getQuotesByToken", &res, chainName, tokenName); err != nil { + return nil, err + } + return res.Quotes, nil +} + +func (k BrideFeeQuoteKeeper) GetQuoteById(ctx context.Context, id *big.Int) (IBridgeFeeQuoteQuoteInfo, error) { + var res struct{ Quote IBridgeFeeQuoteQuoteInfo } + if err := k.QueryContract(sdk.UnwrapSDKContext(ctx), k.from, k.contract, k.abi, "getQuoteById", &res, id); err != nil { + return IBridgeFeeQuoteQuoteInfo{}, err + } + return res.Quote, nil +} diff --git a/contract/contract.go b/contract/contract.go index d4c10b44..9107cc64 100644 --- a/contract/contract.go +++ b/contract/contract.go @@ -18,6 +18,7 @@ const ( StakingAddress = "0x0000000000000000000000000000000000001003" CrosschainAddress = "0x0000000000000000000000000000000000001004" + BridgeFeeAddress = "0x0000000000000000000000000000000000001005" ) const DefaultGasCap uint64 = 30000000 @@ -45,6 +46,7 @@ var ( fxBridgeABI = MustABIJson(IFxBridgeLogicMetaData.ABI) bridgeCallbackABI = MustABIJson(IBridgeCallbackMetaData.ABI) errorABI = MustABIJson(IErrorMetaData.ABI) + bridgeFeeQuoteABI = MustABIJson(IBridgeFeeQuoteMetaData.ABI) ) type Caller interface { diff --git a/contract/ibridge_fee_quote.sol.go b/contract/ibridge_fee_quote.sol.go index bd9e44f7..d37be78a 100644 --- a/contract/ibridge_fee_quote.sol.go +++ b/contract/ibridge_fee_quote.sol.go @@ -60,7 +60,7 @@ type IBridgeFeeQuoteQuoteInput struct { // IBridgeFeeQuoteMetaData contains all meta data concerning the IBridgeFeeQuote contract. var IBridgeFeeQuoteMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"_oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_index\",\"type\":\"uint256\"}],\"name\":\"getQuote\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"internalType\":\"structIBridgeFeeQuote.QuoteInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_id\",\"type\":\"uint256\"}],\"name\":\"getQuoteById\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"internalType\":\"structIBridgeFeeQuote.QuoteInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_tokenName\",\"type\":\"string\"}],\"name\":\"getQuoteByToken\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"internalType\":\"structIBridgeFeeQuote.QuoteInfo[]\",\"name\":\"quotes\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_chainName\",\"type\":\"string\"}],\"name\":\"getQuoteList\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"internalType\":\"structIBridgeFeeQuote.QuoteInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"quoteIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structIBridgeFeeQuote.QuoteInput[]\",\"name\":\"_inputs\",\"type\":\"tuple[]\"}],\"name\":\"quote\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_chainName\",\"type\":\"string\"}],\"name\":\"supportAssets\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isActive\",\"type\":\"bool\"},{\"internalType\":\"string[]\",\"name\":\"tokenNames\",\"type\":\"string[]\"}],\"internalType\":\"structIBridgeFeeQuote.Asset\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"supportChainNames\",\"outputs\":[{\"internalType\":\"string[]\",\"name\":\"\",\"type\":\"string[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + ABI: "[{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"_oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_index\",\"type\":\"uint256\"}],\"name\":\"getQuote\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"internalType\":\"structIBridgeFeeQuote.QuoteInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_id\",\"type\":\"uint256\"}],\"name\":\"getQuoteById\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"internalType\":\"structIBridgeFeeQuote.QuoteInfo\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_chainName\",\"type\":\"string\"}],\"name\":\"getQuoteList\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"internalType\":\"structIBridgeFeeQuote.QuoteInfo[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_tokenName\",\"type\":\"string\"}],\"name\":\"getQuotesByToken\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"id\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"}],\"internalType\":\"structIBridgeFeeQuote.QuoteInfo[]\",\"name\":\"quotes\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"chainName\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"tokenName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"oracle\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"quoteIndex\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"gasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expiry\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"structIBridgeFeeQuote.QuoteInput[]\",\"name\":\"_inputs\",\"type\":\"tuple[]\"}],\"name\":\"quote\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_chainName\",\"type\":\"string\"}],\"name\":\"supportAssets\",\"outputs\":[{\"components\":[{\"internalType\":\"bool\",\"name\":\"isActive\",\"type\":\"bool\"},{\"internalType\":\"string[]\",\"name\":\"tokenNames\",\"type\":\"string[]\"}],\"internalType\":\"structIBridgeFeeQuote.Asset\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"supportChainNames\",\"outputs\":[{\"internalType\":\"string[]\",\"name\":\"\",\"type\":\"string[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", } // IBridgeFeeQuoteABI is the input ABI used to generate the binding from. @@ -271,12 +271,12 @@ func (_IBridgeFeeQuote *IBridgeFeeQuoteCallerSession) GetQuoteById(_id *big.Int) return _IBridgeFeeQuote.Contract.GetQuoteById(&_IBridgeFeeQuote.CallOpts, _id) } -// GetQuoteByToken is a free data retrieval call binding the contract method 0xa3101bf2. +// GetQuoteList is a free data retrieval call binding the contract method 0x398a0e6b. // -// Solidity: function getQuoteByToken(string _chainName, string _tokenName) view returns((uint256,string,string,address,uint256,uint256,uint256)[] quotes) -func (_IBridgeFeeQuote *IBridgeFeeQuoteCaller) GetQuoteByToken(opts *bind.CallOpts, _chainName string, _tokenName string) ([]IBridgeFeeQuoteQuoteInfo, error) { +// Solidity: function getQuoteList(string _chainName) view returns((uint256,string,string,address,uint256,uint256,uint256)[]) +func (_IBridgeFeeQuote *IBridgeFeeQuoteCaller) GetQuoteList(opts *bind.CallOpts, _chainName string) ([]IBridgeFeeQuoteQuoteInfo, error) { var out []interface{} - err := _IBridgeFeeQuote.contract.Call(opts, &out, "getQuoteByToken", _chainName, _tokenName) + err := _IBridgeFeeQuote.contract.Call(opts, &out, "getQuoteList", _chainName) if err != nil { return *new([]IBridgeFeeQuoteQuoteInfo), err @@ -288,26 +288,26 @@ func (_IBridgeFeeQuote *IBridgeFeeQuoteCaller) GetQuoteByToken(opts *bind.CallOp } -// GetQuoteByToken is a free data retrieval call binding the contract method 0xa3101bf2. +// GetQuoteList is a free data retrieval call binding the contract method 0x398a0e6b. // -// Solidity: function getQuoteByToken(string _chainName, string _tokenName) view returns((uint256,string,string,address,uint256,uint256,uint256)[] quotes) -func (_IBridgeFeeQuote *IBridgeFeeQuoteSession) GetQuoteByToken(_chainName string, _tokenName string) ([]IBridgeFeeQuoteQuoteInfo, error) { - return _IBridgeFeeQuote.Contract.GetQuoteByToken(&_IBridgeFeeQuote.CallOpts, _chainName, _tokenName) +// Solidity: function getQuoteList(string _chainName) view returns((uint256,string,string,address,uint256,uint256,uint256)[]) +func (_IBridgeFeeQuote *IBridgeFeeQuoteSession) GetQuoteList(_chainName string) ([]IBridgeFeeQuoteQuoteInfo, error) { + return _IBridgeFeeQuote.Contract.GetQuoteList(&_IBridgeFeeQuote.CallOpts, _chainName) } -// GetQuoteByToken is a free data retrieval call binding the contract method 0xa3101bf2. +// GetQuoteList is a free data retrieval call binding the contract method 0x398a0e6b. // -// Solidity: function getQuoteByToken(string _chainName, string _tokenName) view returns((uint256,string,string,address,uint256,uint256,uint256)[] quotes) -func (_IBridgeFeeQuote *IBridgeFeeQuoteCallerSession) GetQuoteByToken(_chainName string, _tokenName string) ([]IBridgeFeeQuoteQuoteInfo, error) { - return _IBridgeFeeQuote.Contract.GetQuoteByToken(&_IBridgeFeeQuote.CallOpts, _chainName, _tokenName) +// Solidity: function getQuoteList(string _chainName) view returns((uint256,string,string,address,uint256,uint256,uint256)[]) +func (_IBridgeFeeQuote *IBridgeFeeQuoteCallerSession) GetQuoteList(_chainName string) ([]IBridgeFeeQuoteQuoteInfo, error) { + return _IBridgeFeeQuote.Contract.GetQuoteList(&_IBridgeFeeQuote.CallOpts, _chainName) } -// GetQuoteList is a free data retrieval call binding the contract method 0x398a0e6b. +// GetQuotesByToken is a free data retrieval call binding the contract method 0x3dd7c98c. // -// Solidity: function getQuoteList(string _chainName) view returns((uint256,string,string,address,uint256,uint256,uint256)[]) -func (_IBridgeFeeQuote *IBridgeFeeQuoteCaller) GetQuoteList(opts *bind.CallOpts, _chainName string) ([]IBridgeFeeQuoteQuoteInfo, error) { +// Solidity: function getQuotesByToken(string _chainName, string _tokenName) view returns((uint256,string,string,address,uint256,uint256,uint256)[] quotes) +func (_IBridgeFeeQuote *IBridgeFeeQuoteCaller) GetQuotesByToken(opts *bind.CallOpts, _chainName string, _tokenName string) ([]IBridgeFeeQuoteQuoteInfo, error) { var out []interface{} - err := _IBridgeFeeQuote.contract.Call(opts, &out, "getQuoteList", _chainName) + err := _IBridgeFeeQuote.contract.Call(opts, &out, "getQuotesByToken", _chainName, _tokenName) if err != nil { return *new([]IBridgeFeeQuoteQuoteInfo), err @@ -319,18 +319,18 @@ func (_IBridgeFeeQuote *IBridgeFeeQuoteCaller) GetQuoteList(opts *bind.CallOpts, } -// GetQuoteList is a free data retrieval call binding the contract method 0x398a0e6b. +// GetQuotesByToken is a free data retrieval call binding the contract method 0x3dd7c98c. // -// Solidity: function getQuoteList(string _chainName) view returns((uint256,string,string,address,uint256,uint256,uint256)[]) -func (_IBridgeFeeQuote *IBridgeFeeQuoteSession) GetQuoteList(_chainName string) ([]IBridgeFeeQuoteQuoteInfo, error) { - return _IBridgeFeeQuote.Contract.GetQuoteList(&_IBridgeFeeQuote.CallOpts, _chainName) +// Solidity: function getQuotesByToken(string _chainName, string _tokenName) view returns((uint256,string,string,address,uint256,uint256,uint256)[] quotes) +func (_IBridgeFeeQuote *IBridgeFeeQuoteSession) GetQuotesByToken(_chainName string, _tokenName string) ([]IBridgeFeeQuoteQuoteInfo, error) { + return _IBridgeFeeQuote.Contract.GetQuotesByToken(&_IBridgeFeeQuote.CallOpts, _chainName, _tokenName) } -// GetQuoteList is a free data retrieval call binding the contract method 0x398a0e6b. +// GetQuotesByToken is a free data retrieval call binding the contract method 0x3dd7c98c. // -// Solidity: function getQuoteList(string _chainName) view returns((uint256,string,string,address,uint256,uint256,uint256)[]) -func (_IBridgeFeeQuote *IBridgeFeeQuoteCallerSession) GetQuoteList(_chainName string) ([]IBridgeFeeQuoteQuoteInfo, error) { - return _IBridgeFeeQuote.Contract.GetQuoteList(&_IBridgeFeeQuote.CallOpts, _chainName) +// Solidity: function getQuotesByToken(string _chainName, string _tokenName) view returns((uint256,string,string,address,uint256,uint256,uint256)[] quotes) +func (_IBridgeFeeQuote *IBridgeFeeQuoteCallerSession) GetQuotesByToken(_chainName string, _tokenName string) ([]IBridgeFeeQuoteQuoteInfo, error) { + return _IBridgeFeeQuote.Contract.GetQuotesByToken(&_IBridgeFeeQuote.CallOpts, _chainName, _tokenName) } // SupportAssets is a free data retrieval call binding the contract method 0x1b826a1b. diff --git a/solidity/contracts/bridge/BridgeFeeQuote.sol b/solidity/contracts/bridge/BridgeFeeQuote.sol index bd888326..6969f8ab 100644 --- a/solidity/contracts/bridge/BridgeFeeQuote.sol +++ b/solidity/contracts/bridge/BridgeFeeQuote.sol @@ -201,7 +201,7 @@ contract BridgeFeeQuote is * @param _tokenName The address of the token. * @return QuoteInfo[] The quote list. */ - function getQuoteByToken( + function getQuotesByToken( string memory _chainName, string memory _tokenName ) external view returns (QuoteInfo[] memory) { diff --git a/solidity/contracts/bridge/IBridgeFee.sol b/solidity/contracts/bridge/IBridgeFee.sol index a99cabf9..11a9860a 100644 --- a/solidity/contracts/bridge/IBridgeFee.sol +++ b/solidity/contracts/bridge/IBridgeFee.sol @@ -37,7 +37,7 @@ interface IBridgeFeeQuote { function getQuoteById(uint256 _id) external view returns (QuoteInfo memory); - function getQuoteByToken( + function getQuotesByToken( string memory _chainName, string memory _tokenName ) external view returns (QuoteInfo[] memory quotes); diff --git a/solidity/test/bridge_fee_quote.ts b/solidity/test/bridge_fee_quote.ts index 2be347bd..b78c7615 100644 --- a/solidity/test/bridge_fee_quote.ts +++ b/solidity/test/bridge_fee_quote.ts @@ -224,7 +224,7 @@ describe("BridgeFeeQuoteUpgradeable", function () { }, ]); - const quotes = await bridgeFeeQuote.getQuoteByToken(chainName, token1); + const quotes = await bridgeFeeQuote.getQuotesByToken(chainName, token1); expect(quotes.length).to.equal(3); for (let i = 0; i < 3; i++) { diff --git a/x/crosschain/keeper/keeper.go b/x/crosschain/keeper/keeper.go index 9aa5278c..85ac0a44 100644 --- a/x/crosschain/keeper/keeper.go +++ b/x/crosschain/keeper/keeper.go @@ -20,14 +20,15 @@ type Keeper struct { cdc codec.BinaryCodec // The wire codec for binary encoding/decoding. storeKey storetypes.StoreKey // Unexposed key to access store from sdk.Context - stakingKeeper types.StakingKeeper - stakingMsgServer types.StakingMsgServer - distributionKeeper types.DistributionMsgServer - bankKeeper types.BankKeeper - ak types.AccountKeeper - ibcTransferKeeper types.IBCTransferKeeper - erc20Keeper types.Erc20Keeper - evmKeeper types.EVMKeeper + stakingKeeper types.StakingKeeper + stakingMsgServer types.StakingMsgServer + distributionKeeper types.DistributionMsgServer + bankKeeper types.BankKeeper + ak types.AccountKeeper + ibcTransferKeeper types.IBCTransferKeeper + erc20Keeper types.Erc20Keeper + evmKeeper types.EVMKeeper + brideFeeQuoteKeeper types.BridgeFeeQuoteKeeper authority string callbackFrom common.Address @@ -37,7 +38,7 @@ type Keeper struct { func NewKeeper(cdc codec.BinaryCodec, moduleName string, storeKey storetypes.StoreKey, stakingKeeper types.StakingKeeper, stakingMsgServer types.StakingMsgServer, distributionKeeper types.DistributionMsgServer, bankKeeper types.BankKeeper, ibcTransferKeeper types.IBCTransferKeeper, erc20Keeper types.Erc20Keeper, ak types.AccountKeeper, - evmKeeper types.EVMKeeper, authority string, + evmKeeper types.EVMKeeper, brideFeeQuoteKeeper types.BridgeFeeQuoteKeeper, authority string, ) Keeper { if addr := ak.GetModuleAddress(moduleName); addr == nil { panic(fmt.Sprintf("%s module account has not been set", moduleName)) @@ -48,16 +49,18 @@ func NewKeeper(cdc codec.BinaryCodec, moduleName string, storeKey storetypes.Sto cdc: cdc, storeKey: storeKey, - stakingKeeper: stakingKeeper, - stakingMsgServer: stakingMsgServer, - distributionKeeper: distributionKeeper, - bankKeeper: bankKeeper, - ak: ak, - ibcTransferKeeper: ibcTransferKeeper, - erc20Keeper: erc20Keeper, - evmKeeper: evmKeeper, - authority: authority, - callbackFrom: common.BytesToAddress(autytypes.NewModuleAddress(types.BridgeCallSender)), + stakingKeeper: stakingKeeper, + stakingMsgServer: stakingMsgServer, + distributionKeeper: distributionKeeper, + bankKeeper: bankKeeper, + ak: ak, + ibcTransferKeeper: ibcTransferKeeper, + erc20Keeper: erc20Keeper, + evmKeeper: evmKeeper, + brideFeeQuoteKeeper: brideFeeQuoteKeeper, + + authority: authority, + callbackFrom: common.BytesToAddress(autytypes.NewModuleAddress(types.BridgeCallSender)), } } diff --git a/x/crosschain/keeper/keeper_test.go b/x/crosschain/keeper/keeper_test.go index 8d52e1c0..13038ae9 100644 --- a/x/crosschain/keeper/keeper_test.go +++ b/x/crosschain/keeper/keeper_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" + "github.com/functionx/fx-core/v8/contract" "github.com/functionx/fx-core/v8/testutil" "github.com/functionx/fx-core/v8/testutil/helpers" arbitrumtypes "github.com/functionx/fx-core/v8/x/arbitrum/types" @@ -111,6 +112,7 @@ func (s *KeeperMockSuite) SetupTest() { s.erc20Keeper, s.accountKeeper, s.evmKeeper, + contract.NewBridgeFeeQuoteKeeper(nil, contract.BridgeFeeAddress), authtypes.NewModuleAddress(govtypes.ModuleName).String(), ) diff --git a/x/crosschain/keeper/outgoing_tx.go b/x/crosschain/keeper/outgoing_tx.go index f1a16684..bb7a8d9a 100644 --- a/x/crosschain/keeper/outgoing_tx.go +++ b/x/crosschain/keeper/outgoing_tx.go @@ -2,17 +2,36 @@ package keeper import ( "fmt" + "math/big" + sdkmath "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/hashicorp/go-metrics" + "github.com/functionx/fx-core/v8/contract" fxtelemetry "github.com/functionx/fx-core/v8/telemetry" "github.com/functionx/fx-core/v8/x/crosschain/types" ) func (k Keeper) BuildOutgoingTxBatch(ctx sdk.Context, sender sdk.AccAddress, receiver string, amount, fee sdk.Coin) (uint64, error) { + quoteInfos, err := k.brideFeeQuoteKeeper.GetQuotesByToken(ctx, k.moduleName, fee.Denom) + if err != nil { + return 0, err + } + var quoteInfo *contract.IBridgeFeeQuoteQuoteInfo + for _, quote := range quoteInfos { + if fee.Amount.GTE(sdkmath.NewIntFromBigInt(quote.Fee)) && + new(big.Int).Sub(quote.Expiry, big.NewInt(ctx.BlockTime().UnixNano())).Sign() > 0 { + quoteInfo = "e + break + } + } + if quoteInfo == nil { + return 0, types.ErrInvalid.Wrapf("bridge fee is too small or expired") + } + bridgeToken, err := k.BaseCoinToBridgeToken(ctx, sender, amount.Add(fee)) if err != nil { return 0, err @@ -21,8 +40,6 @@ func (k Keeper) BuildOutgoingTxBatch(ctx sdk.Context, sender sdk.AccAddress, rec return 0, err } - feeReceive := "" // todo: query feeReceive from quote contract - batchTimeout := k.CalExternalTimeoutHeight(ctx, GetExternalBatchTimeout) if batchTimeout <= 0 { return 0, types.ErrInvalid.Wrapf("batch timeout height") @@ -40,7 +57,7 @@ func (k Keeper) BuildOutgoingTxBatch(ctx sdk.Context, sender sdk.AccAddress, rec }, }, TokenContract: bridgeToken.Contract, - FeeReceive: feeReceive, + FeeReceive: quoteInfo.Oracle.String(), Block: uint64(ctx.BlockHeight()), // set the current block height when storing the batch } if err = k.StoreBatch(ctx, batch); err != nil { diff --git a/x/crosschain/mock/expected_keepers_mocks.go b/x/crosschain/mock/expected_keepers_mocks.go index 0dc2d573..9cc6c1a5 100644 --- a/x/crosschain/mock/expected_keepers_mocks.go +++ b/x/crosschain/mock/expected_keepers_mocks.go @@ -22,6 +22,7 @@ import ( types2 "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" common "github.com/ethereum/go-ethereum/common" types3 "github.com/evmos/ethermint/x/evm/types" + contract "github.com/functionx/fx-core/v8/contract" types4 "github.com/functionx/fx-core/v8/x/erc20/types" gomock "go.uber.org/mock/gomock" ) @@ -752,3 +753,41 @@ func (mr *MockAccountKeeperMockRecorder) SetAccount(ctx, acc any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetAccount", reflect.TypeOf((*MockAccountKeeper)(nil).SetAccount), ctx, acc) } + +// MockBridgeFeeQuoteKeeper is a mock of BridgeFeeQuoteKeeper interface. +type MockBridgeFeeQuoteKeeper struct { + ctrl *gomock.Controller + recorder *MockBridgeFeeQuoteKeeperMockRecorder +} + +// MockBridgeFeeQuoteKeeperMockRecorder is the mock recorder for MockBridgeFeeQuoteKeeper. +type MockBridgeFeeQuoteKeeperMockRecorder struct { + mock *MockBridgeFeeQuoteKeeper +} + +// NewMockBridgeFeeQuoteKeeper creates a new mock instance. +func NewMockBridgeFeeQuoteKeeper(ctrl *gomock.Controller) *MockBridgeFeeQuoteKeeper { + mock := &MockBridgeFeeQuoteKeeper{ctrl: ctrl} + mock.recorder = &MockBridgeFeeQuoteKeeperMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockBridgeFeeQuoteKeeper) EXPECT() *MockBridgeFeeQuoteKeeperMockRecorder { + return m.recorder +} + +// GetQuotesByToken mocks base method. +func (m *MockBridgeFeeQuoteKeeper) GetQuotesByToken(ctx context.Context, chainName, denom string) ([]contract.IBridgeFeeQuoteQuoteInfo, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetQuotesByToken", ctx, chainName, denom) + ret0, _ := ret[0].([]contract.IBridgeFeeQuoteQuoteInfo) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetQuotesByToken indicates an expected call of GetQuotesByToken. +func (mr *MockBridgeFeeQuoteKeeperMockRecorder) GetQuotesByToken(ctx, chainName, denom any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQuotesByToken", reflect.TypeOf((*MockBridgeFeeQuoteKeeper)(nil).GetQuotesByToken), ctx, chainName, denom) +} diff --git a/x/crosschain/types/expected_keepers.go b/x/crosschain/types/expected_keepers.go index b295fbc9..749d2938 100644 --- a/x/crosschain/types/expected_keepers.go +++ b/x/crosschain/types/expected_keepers.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/evmos/ethermint/x/evm/types" + "github.com/functionx/fx-core/v8/contract" erc20types "github.com/functionx/fx-core/v8/x/erc20/types" ) @@ -85,3 +86,7 @@ type AccountKeeper interface { NewAccountWithAddress(ctx context.Context, addr sdk.AccAddress) sdk.AccountI GetModuleAccount(ctx context.Context, moduleName string) sdk.ModuleAccountI } + +type BridgeFeeQuoteKeeper interface { + GetQuotesByToken(ctx context.Context, chainName, denom string) ([]contract.IBridgeFeeQuoteQuoteInfo, error) +}