Commit a91214c9 authored by vicotor's avatar vicotor

add swap router support

parent c3edc3cd
......@@ -54,7 +54,7 @@ type BridgesubmitParams struct {
// BridgeContractMetaData contains all meta data concerning the BridgeContract contract.
var BridgeContractMetaData = &bind.MetaData{
ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"}],\"name\":\"BeaconUpgraded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldReceiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newReceiver\",\"type\":\"address\"}],\"name\":\"FeeReceiverChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"prior\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"present\",\"type\":\"uint256\"}],\"name\":\"RequirementChange\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"fromToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"toToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"swap\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"path\",\"type\":\"address[]\"}],\"name\":\"SwapConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"TokenOutConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fromChainID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TransferIn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"TransferInConfirmation\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TransferInExecution\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"TransferInRejection\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fromChainID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiveToken\",\"type\":\"address\"}],\"name\":\"TransferOut\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldTreasury\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newTreasury\",\"type\":\"address\"}],\"name\":\"TreasuryChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"minReserve\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reserveRatio\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"TreasuryConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"contractBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"treasuryBalance\",\"type\":\"uint256\"}],\"name\":\"TreasuryTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"ValidatorAddition\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"ValidatorRemoval\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"_inID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"_outID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"addValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"autoTransferToTreasury\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"calculateTreasuryTransfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"canTransfer\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"transferAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"willReserve\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"currentBalance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"receiveToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isBurn\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"internalType\":\"structBridge.OutConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"changeOutConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"required\",\"type\":\"uint256\"}],\"name\":\"changeValidRequired\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"}],\"name\":\"confirmInTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"emergencyWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feeReceiver\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"getInId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"}],\"name\":\"getSupportedTokensOut\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"inTotal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"inTransfers\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fromChainID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"outId\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"executed\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"confirmCounter\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rejectCounter\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"initialOwner\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"}],\"name\":\"isChecked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"isConfirmed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"isRejected\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isTokenSupportedOut\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"}],\"name\":\"isTokenTransferEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isValidator\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"outConfiguration\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"receiveToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isBurn\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"outTotal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"outTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"outTransfers\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"outId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fromChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiveToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"receiveAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proxiableUUID\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"}],\"name\":\"rejectInTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"removeValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"newValidator\",\"type\":\"address\"}],\"name\":\"replaceValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"retryTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newReceiver\",\"type\":\"address\"}],\"name\":\"setFeeReceiver\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newTreasury\",\"type\":\"address\"}],\"name\":\"setTreasury\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"minReserve\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reserveRatio\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setTreasuryConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"outId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fromChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"sendToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"sendAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"}],\"internalType\":\"structBridge.submitParams\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"submitInTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"supportedTokensOut\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"treasury\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"treasuryConfigs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"minReserve\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reserveRatio\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"treasuryTotal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"validRequired\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"previousAdmin\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"newAdmin\",\"type\":\"address\"}],\"name\":\"AdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"beacon\",\"type\":\"address\"}],\"name\":\"BeaconUpgraded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldReceiver\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newReceiver\",\"type\":\"address\"}],\"name\":\"FeeReceiverChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"prior\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"present\",\"type\":\"uint256\"}],\"name\":\"RequirementChange\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"fromToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"toToken\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"swap\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address[]\",\"name\":\"path\",\"type\":\"address[]\"}],\"name\":\"SwapConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"TokenOutConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fromChainID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TransferIn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"TransferInConfirmation\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"TransferInExecution\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"TransferInRejection\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"outId\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fromChainID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"receiveToken\",\"type\":\"address\"}],\"name\":\"TransferOut\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"oldTreasury\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newTreasury\",\"type\":\"address\"}],\"name\":\"TreasuryChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"minReserve\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"reserveRatio\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"TreasuryConfigChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"contractBalance\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"treasuryBalance\",\"type\":\"uint256\"}],\"name\":\"TreasuryTransfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"implementation\",\"type\":\"address\"}],\"name\":\"Upgraded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"ValidatorAddition\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"ValidatorRemoval\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"_inID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"_outID\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"addValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"autoTransferToTreasury\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"calculateTreasuryTransfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"canTransfer\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"transferAmount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"willReserve\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"currentBalance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"receiveToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isBurn\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"internalType\":\"structBridge.OutConfig\",\"name\":\"config\",\"type\":\"tuple\"}],\"name\":\"changeOutConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"required\",\"type\":\"uint256\"}],\"name\":\"changeValidRequired\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"}],\"name\":\"confirmInTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"emergencyWithdraw\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feeReceiver\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"getInId\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"}],\"name\":\"getSupportedTokensOut\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"inTotal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"inTransfers\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fromChainID\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"outId\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"executed\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"confirmCounter\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rejectCounter\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"initialOwner\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"}],\"name\":\"isChecked\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"isConfirmed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"isRejected\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isTokenSupportedOut\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"}],\"name\":\"isTokenTransferEnabled\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"isValidator\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"outConfiguration\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"receiveToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"limit\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isBurn\",\"type\":\"bool\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"outTotal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"outTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"outTransfers\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"outId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fromChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"receiveToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"receiveAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"proxiableUUID\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"}],\"name\":\"rejectInTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"}],\"name\":\"removeValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"validator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"newValidator\",\"type\":\"address\"}],\"name\":\"replaceValidator\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inId\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"retryTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newReceiver\",\"type\":\"address\"}],\"name\":\"setFeeReceiver\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newTreasury\",\"type\":\"address\"}],\"name\":\"setTreasury\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"minReserve\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reserveRatio\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"name\":\"setTreasuryConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"outId\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"fromChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"sendToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"sendAmount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"signature\",\"type\":\"bytes32\"}],\"internalType\":\"structBridge.submitParams\",\"name\":\"params\",\"type\":\"tuple\"}],\"name\":\"submitInTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"supportedTokensOut\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"treasury\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"treasuryConfigs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"minReserve\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reserveRatio\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"enabled\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"treasuryTotal\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"}],\"name\":\"upgradeTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newImplementation\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"upgradeToAndCall\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"validRequired\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"toChainID\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"fromToken\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"toToken\",\"type\":\"address\"}],\"name\":\"outTransferSwap\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]",
}
// BridgeContractABI is the input ABI used to generate the binding from.
......@@ -1300,6 +1300,27 @@ func (_BridgeContract *BridgeContractTransactorSession) OutTransfer(token common
return _BridgeContract.Contract.OutTransfer(&_BridgeContract.TransactOpts, token, amount, toChainID, receiver)
}
// OutTransferSwap is a paid mutator transaction binding the contract method 0x433ba133.
//
// Solidity: function outTransferSwap(address token, uint256 amount, uint256 toChainID, address receiver, address fromToken, address toToken) returns()
func (_BridgeContract *BridgeContractTransactor) OutTransferSwap(opts *bind.TransactOpts, token common.Address, amount *big.Int, toChainID *big.Int, receiver common.Address, fromToken common.Address, toToken common.Address) (*types.Transaction, error) {
return _BridgeContract.contract.Transact(opts, "outTransferSwap", token, amount, toChainID, receiver, fromToken, toToken)
}
// OutTransferSwap is a paid mutator transaction binding the contract method 0x433ba133.
//
// Solidity: function outTransferSwap(address token, uint256 amount, uint256 toChainID, address receiver, address fromToken, address toToken) returns()
func (_BridgeContract *BridgeContractSession) OutTransferSwap(token common.Address, amount *big.Int, toChainID *big.Int, receiver common.Address, fromToken common.Address, toToken common.Address) (*types.Transaction, error) {
return _BridgeContract.Contract.OutTransferSwap(&_BridgeContract.TransactOpts, token, amount, toChainID, receiver, fromToken, toToken)
}
// OutTransferSwap is a paid mutator transaction binding the contract method 0x433ba133.
//
// Solidity: function outTransferSwap(address token, uint256 amount, uint256 toChainID, address receiver, address fromToken, address toToken) returns()
func (_BridgeContract *BridgeContractTransactorSession) OutTransferSwap(token common.Address, amount *big.Int, toChainID *big.Int, receiver common.Address, fromToken common.Address, toToken common.Address) (*types.Transaction, error) {
return _BridgeContract.Contract.OutTransferSwap(&_BridgeContract.TransactOpts, token, amount, toChainID, receiver, fromToken, toToken)
}
// RejectInTransfer is a paid mutator transaction binding the contract method 0x9004e82d.
//
// Solidity: function rejectInTransfer(uint256 inId) returns()
......
......@@ -1433,5 +1433,43 @@
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "toChainID",
"type": "uint256"
},
{
"internalType": "address",
"name": "receiver",
"type": "address"
},
{
"internalType": "address",
"name": "fromToken",
"type": "address"
},
{
"internalType": "address",
"name": "toToken",
"type": "address"
}
],
"name": "outTransferSwap",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]
\ No newline at end of file
package router
const UniswapV2RouterABI = `[
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "amountIn",
"type": "uint256"
},
{
"internalType": "address[]",
"name": "path",
"type": "address[]"
}
],
"name": "getAmountsOut",
"outputs": [
{
"internalType": "uint256[]",
"name": "amounts",
"type": "uint256[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
}
]`
package router
import (
"context"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"math/big"
"strings"
)
// Pre-parse the router ABI once at init to avoid repeated JSON parsing cost.
var (
parsedRouterABI abi.ABI
parseRouterErr error
)
func init() {
parsedRouterABI, parseRouterErr = abi.JSON(strings.NewReader(UniswapV2RouterABI))
}
// GetAmountsOut queries the router for the output amount given an input amount and swap path.
// Returns the final output amount (last element of amounts). It validates inputs and avoids panics
// on unexpected empty results.
func GetAmountsOut(
client *ethclient.Client,
user common.Address,
router common.Address,
amountIn *big.Int,
path []string,
) (*big.Int, error) {
if parseRouterErr != nil {
return nil, fmt.Errorf("router ABI init: %w", parseRouterErr)
}
if client == nil {
return nil, errors.New("nil eth client")
}
if amountIn == nil {
return nil, errors.New("nil amountIn")
}
if len(path) < 2 {
return nil, fmt.Errorf("path length must be >= 2, got %d", len(path))
}
addrPath := make([]common.Address, 0, len(path))
for _, addrStr := range path {
if !common.IsHexAddress(addrStr) || common.HexToAddress(addrStr) == (common.Address{}) {
return nil, fmt.Errorf("invalid address in path: %s", addrStr)
}
addrPath = append(addrPath, common.HexToAddress(addrStr))
}
contract := bind.NewBoundContract(router, parsedRouterABI, client, client, client)
callOpts := &bind.CallOpts{
Context: context.Background(),
From: user, // optional; included for completeness (some routers ignore).
}
var amounts = make([]interface{}, len(path))
if err := contract.Call(callOpts, &amounts, "getAmountsOut", amountIn, addrPath); err != nil {
return nil, fmt.Errorf("call getAmountsOut (amountIn=%s pathLen=%d): %w", amountIn.String(), len(path), err)
}
amountOut := *abi.ConvertType(amounts[len(path)-1], new(*big.Int)).(**big.Int)
return amountOut, nil
}
......@@ -124,3 +124,11 @@ func (d *Dao) GetOutConfig(chainId int64, token common.Address, toChainId int64)
}
return syncer.GetOutConfig(token, toChainId)
}
func (d *Dao) GetChainConfig(chainId int64) (ChainInfo, error) {
chainInfo, ok := d.chainGroup[chainId]
if !ok {
return ChainInfo{}, fmt.Errorf("chain %d config not found", chainId)
}
return chainInfo, nil
}
......@@ -2,11 +2,16 @@ package dao
import (
"code.wuban.net.cn/movabridge/bridge-backend/constant"
"code.wuban.net.cn/movabridge/bridge-backend/contract/bridge"
"code.wuban.net.cn/movabridge/bridge-backend/contract/router"
apiModel "code.wuban.net.cn/movabridge/bridge-backend/model/api"
"context"
"fmt"
"github.com/ethereum/go-ethereum/common"
log "github.com/sirupsen/logrus"
"math/big"
"sort"
"strings"
"time"
dbModel "code.wuban.net.cn/movabridge/bridge-backend/model/db"
......@@ -103,7 +108,7 @@ func (d *Dao) GetBridgeConfig() (config apiModel.BridgeConfig, err error) {
RpcUrl: chainInfo.Rpc,
ExplorerUrl: chainInfo.Explorer,
BridgeContract: info.Contract,
SupportTokens: make(map[string]apiModel.ToToken),
SupportTokens: make(map[string]apiModel.SupportBridgeTokenInfo),
}
} else {
chainConfig = _chainConfig
......@@ -117,7 +122,7 @@ func (d *Dao) GetBridgeConfig() (config apiModel.BridgeConfig, err error) {
}).Error("not found token info with tokenrepo, skip symbol info")
}
tokenInfo := apiModel.ToToken{
tokenInfo := apiModel.ToBridgeToken{
TokenContract: info.Token,
ToChainId: info.ToChainId,
ToToken: info.ToToken,
......@@ -128,7 +133,16 @@ func (d *Dao) GetBridgeConfig() (config apiModel.BridgeConfig, err error) {
tokenInfo.Fee = outConfig.Fee.String()
tokenInfo.MaxLimit = outConfig.Limit.String()
}
chainConfig.SupportTokens[info.TokenName] = tokenInfo
if m, exist := chainConfig.SupportTokens[info.TokenName]; exist {
m.BridgeTokens = append(m.BridgeTokens, tokenInfo)
chainConfig.SupportTokens[info.TokenName] = m
} else {
chainConfig.SupportTokens[info.TokenName] = apiModel.SupportBridgeTokenInfo{
TokenContract: info.Token,
TokenSymbol: info.TokenName,
BridgeTokens: []apiModel.ToBridgeToken{tokenInfo},
}
}
config.Chains[chainInfo.Name] = chainConfig
}
......@@ -200,72 +214,569 @@ func (d *Dao) GetHistoryInfo(user string) (history apiModel.History, err error)
}
func (d *Dao) GetSwapConfig(chainId int64) (config apiModel.SwapConfigs, err error) {
collection := d.db.Collection(new(dbModel.SwapTokenInfo).TableName())
type tokenBalance struct {
Name string
Decimals int
Balance *big.Int
Contract string
}
func (d *Dao) GetBridgeTokenBalance(chainId int64, user string) (balances apiModel.TokenBalances, err error) {
balances = apiModel.TokenBalances{
Balances: make([]apiModel.TokenBalance, 0),
}
collection := d.db.Collection(new(dbModel.BridgeTokenInfo).TableName())
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Find all swap token info fillter by chain_id.
cursor, err := collection.Find(ctx, bson.M{})
if err != nil {
return config, err
return balances, err
}
defer cursor.Close(ctx)
var swaps []dbModel.SwapTokenInfo
err = cursor.All(ctx, &swaps)
var tokens []dbModel.BridgeTokenInfo
err = cursor.All(ctx, &tokens)
if err != nil {
return config, err
return balances, err
}
config.SwapPairs = make(map[string]apiModel.SwapPair)
// Convert database model to API model
for _, info := range swaps {
if info.ChainId != chainId {
chainClient := d.ChainClient(chainId)
if chainClient == nil {
log.Error("not found chain client for chain id:", chainId)
return balances, nil
}
var innerBalance = make([]tokenBalance, 0)
for _, token := range tokens {
if token.ChainId != chainId {
continue
}
swap := apiModel.SwapPair{
From: apiModel.TokenInfo{
Contract: info.Token,
Name: info.TokenName,
},
To: apiModel.TokenInfo{
Contract: info.ToToken,
Name: "",
},
SwapContract: info.SwapContract,
SwapPath: make([]string, 0),
tokenInfo, balance, err := d.tokenRepo.RetriveTokenInfoAndBalance(chainClient, token.Token, user)
if err != nil {
log.WithFields(log.Fields{
"chain_id": token.ChainId,
"token": token.Token,
"error": err,
}).Error("get token balance failed, skip this token")
continue
}
for _, pathToken := range info.SwapPath {
swap.SwapPath = append(swap.SwapPath, pathToken)
innerBalance = append(innerBalance, tokenBalance{
Name: tokenInfo.Symbol,
Decimals: int(tokenInfo.Decimals),
Balance: balance,
Contract: token.Token,
})
}
if swap.From.Name == "" {
tokenInfo, err := d.tokenRepo.RetriveTokenInfo(info.ChainId, info.Token)
// sort balances by Balance desc
sort.Slice(innerBalance, func(i, j int) bool {
return innerBalance[i].Balance.Cmp(innerBalance[j].Balance) > 0
})
for _, b := range innerBalance {
balances.Balances = append(balances.Balances, apiModel.TokenBalance{
Name: b.Name,
Decimals: b.Decimals,
Balance: b.Balance.String(),
Contract: b.Contract,
})
}
return balances, nil
}
func (d *Dao) buildBridgePayload(token common.Address, amount *big.Int, toChainID *big.Int, receiver common.Address) ([]byte, error) {
contractAbi, _ := bridge.BridgeContractMetaData.GetAbi()
return contractAbi.Pack("outTransfer", token, amount, toChainID, receiver)
}
func (d *Dao) QuoteBridge(param apiModel.QuoteBridgeParam) (quote apiModel.QuoteResult, err error) {
chainInfo, err := d.GetChainConfig(param.FromChainId)
if err != nil {
log.Error("not found chain config for chain id:", param.FromChainId)
return quote, nil
}
outConfig, err := d.GetOutConfig(param.FromChainId, common.HexToAddress(param.FromToken), param.ToChainId)
if err != nil {
log.WithFields(log.Fields{
"chain_id": info.ChainId,
"token": info.Token,
"chain_id": param.FromChainId,
"token": param.FromToken,
"to_chain": param.ToChainId,
"error": err,
}).Error("not found token info with tokenrepo, skip symbol info")
} else {
swap.From.Name = tokenInfo.Symbol
}).Error("get out config failed")
return quote, err
}
_, balance, err := d.tokenRepo.RetriveTokenInfoAndBalance(chainInfo.cli, param.FromToken, param.User)
if err != nil {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"token": param.FromToken,
"user": param.User,
"error": err,
}).Error("get user balance failed")
return quote, err
}
// check balance and param.InputAmount
// check param.InputAmount and outConfig.Limit
// calculate out amount = input amount - fee
inputAmount, ok := new(big.Int).SetString(param.InputAmount, 10)
if !ok {
log.WithFields(log.Fields{
"input_amount": param.InputAmount,
}).Error("invalid input amount")
return quote, nil
}
if balance.Cmp(inputAmount) < 0 {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"token": param.FromToken,
"user": param.User,
"balance": balance.String(),
"inputAmount": inputAmount.String(),
}).Error("user balance insufficient")
return quote, fmt.Errorf("user balance insufficient")
}
if inputAmount.Cmp(outConfig.Limit) > 0 {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"token": param.FromToken,
"user": param.User,
"inputAmount": inputAmount.String(),
"limit": outConfig.Limit.String(),
}).Error("input amount exceed limit")
return quote, fmt.Errorf("input amount exceed limit")
}
fee := outConfig.Fee
if inputAmount.Cmp(fee) <= 0 {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"token": param.FromToken,
"user": param.User,
"inputAmount": inputAmount.String(),
"fee": fee.String(),
}).Error("input amount less than fee")
return quote, fmt.Errorf("input amount less than fee")
}
outAmount := new(big.Int).Sub(inputAmount, fee)
quote = apiModel.QuoteResult{
ToContract: chainInfo.conf.BridgeContract,
OutAmount: outAmount.String(),
}
if swap.To.Name == "" {
toTokenInfo, err := d.tokenRepo.RetriveTokenInfo(info.ChainId, info.ToToken)
// build payload
payload, err := d.buildBridgePayload(common.HexToAddress(param.ToToken), inputAmount, big.NewInt(param.ToChainId), common.HexToAddress(param.Receiver))
if err != nil {
log.WithFields(log.Fields{
"chain_id": info.ChainId,
"token": info.ToToken,
"chain_id": param.FromChainId,
"token": param.FromToken,
"user": param.User,
"inputAmount": inputAmount.String(),
"error": err,
}).Error("not found token info with tokenrepo, skip symbol info")
}).Error("build bridge payload failed")
return quote, err
}
quote.Payload = common.Bytes2Hex(payload)
return quote, nil
}
func (d *Dao) GetAllChainSwapConfig() (map[int64]*apiModel.ChainSwapConfig, error) {
var res = make(map[int64]*apiModel.ChainSwapConfig)
collection := d.db.Collection(new(dbModel.BridgeTokenInfo).TableName())
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cursor, err := collection.Find(ctx, bson.M{})
if err != nil {
return res, err
}
defer cursor.Close(ctx)
var tokens []dbModel.BridgeTokenInfo
err = cursor.All(ctx, &tokens)
if err != nil {
return res, err
}
for _, token := range tokens {
if _, exist := res[token.ChainId]; !exist {
chainConfig, err := d.GetSwapConfig(token.ChainId)
if err != nil {
continue
} else {
swap.To.Name = toTokenInfo.Symbol
res[token.ChainId] = chainConfig
}
}
}
return res, nil
}
func (d *Dao) GetSwapConfig(chainId int64) (*apiModel.ChainSwapConfig, error) {
chainInfo, err := d.GetChainConfig(chainId)
if err != nil {
log.Error("not found chain config for chain id:", chainId)
return nil, fmt.Errorf("not found chain config for chain id: %s", chainId)
}
chainRepo, exist := d.chainList.Get(chainId)
if !exist {
log.Error("not found chain info in chainlist for chain id:", chainId)
return nil, fmt.Errorf("not found chain info in chainlist for chain id: %s", chainId)
}
config := &apiModel.ChainSwapConfig{
Chain: chainRepo.Name,
ChainId: chainId,
RpcUrl: chainRepo.Rpc,
ExplorerUrl: chainRepo.Explorer,
BridgeContract: chainInfo.conf.BridgeContract,
SupportTokens: make(map[string]apiModel.SupportSwapTokenInfo),
}
bridgeTokens, err := d.getSupportedBridgeChains(chainId, "")
if err != nil {
return config, err
}
for _, bridgeToken := range bridgeTokens {
swapChains := []dbModel.BridgeTokenInfo{bridgeToken}
toChains := d.getTargetChainSwapTokens(swapChains, apiModel.SwapPath{
SwapFromToken: bridgeToken.Token,
BridgeFromToken: bridgeToken.Token,
})
if len(toChains) > 0 {
config.SupportTokens[bridgeToken.Token] = apiModel.SupportSwapTokenInfo{
TokenContract: bridgeToken.Token,
TokenSymbol: bridgeToken.TokenName,
SwapTokens: toChains,
}
}
}
swapTokens, err := d.getSupportedSwapTokens(chainId)
if err != nil {
return config, err
}
for _, swap := range swapTokens {
// if swap.ToToken is support, then it can swap to all bridged tokens and target chain token swaps.
swapChains, err := d.getSupportedBridgeChains(chainId, swap.ToToken)
if err != nil {
return config, err
}
config.SwapPairs[swap.From.Name] = swap
toChains := d.getTargetChainSwapTokens(swapChains, apiModel.SwapPath{
SwapFromToken: swap.Token,
BridgeFromToken: swap.ToToken,
})
if len(toChains) > 0 {
config.SupportTokens[swap.Token] = apiModel.SupportSwapTokenInfo{
TokenContract: swap.Token,
TokenSymbol: swap.TokenName,
SwapTokens: toChains,
}
}
}
return config, nil
}
func (d *Dao) GetSwapTokenBalance(chainId int64, user string) (balances apiModel.TokenBalances, err error) {
balances = apiModel.TokenBalances{
Balances: make([]apiModel.TokenBalance, 0),
}
chainSwapConfig, err := d.GetSwapConfig(chainId)
if err != nil {
return balances, err
}
chainInfo, err := d.GetChainConfig(chainId)
if err != nil {
log.Error("not found chain config for chain id:", chainId)
return balances, nil
}
innerBalance := make([]tokenBalance, 0)
for _, token := range chainSwapConfig.SupportTokens {
tokenInfo, balance, err := d.tokenRepo.RetriveTokenInfoAndBalance(chainInfo.cli, token.TokenContract, user)
if err != nil {
log.WithFields(log.Fields{
"chain_id": chainId,
"token": token.TokenContract,
"error": err,
}).Error("get token balance failed, skip this token")
continue
}
innerBalance = append(innerBalance, tokenBalance{
Name: tokenInfo.Symbol,
Decimals: int(tokenInfo.Decimals),
Balance: balance,
Contract: token.TokenContract,
})
}
// sort balances by Balance desc
sort.Slice(innerBalance, func(i, j int) bool {
return innerBalance[i].Balance.Cmp(innerBalance[j].Balance) > 0
})
for _, b := range innerBalance {
balances.Balances = append(balances.Balances, apiModel.TokenBalance{
Name: b.Name,
Decimals: b.Decimals,
Balance: b.Balance.String(),
Contract: b.Contract,
})
}
return balances, nil
}
func (d *Dao) QuoteSwap(param apiModel.QuoteSwapParam) (quote apiModel.QuoteResult, err error) {
chainInfo, err := d.GetChainConfig(param.FromChainId)
if err != nil {
log.Error("not found chain config for chain id:", param.FromChainId)
return quote, nil
}
_, balance, err := d.tokenRepo.RetriveTokenInfoAndBalance(chainInfo.cli, param.Path.SwapFromToken, param.User)
if err != nil {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"token": param.Path.SwapFromToken,
"user": param.User,
"error": err,
}).Error("get user balance failed")
return quote, err
}
// check balance and param.InputAmount
inputAmount, ok := new(big.Int).SetString(param.InputAmount, 10)
if !ok {
log.WithFields(log.Fields{
"input_amount": param.InputAmount,
}).Error("invalid input amount")
return quote, nil
}
if balance.Cmp(inputAmount) < 0 {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"token": param.Path.SwapFromToken,
"user": param.User,
"balance": balance.String(),
"inputAmount": inputAmount.String(),
}).Error("user balance insufficient")
return quote, fmt.Errorf("user balance insufficient")
}
quote = apiModel.QuoteResult{
ToContract: chainInfo.conf.BridgeContract,
OutAmount: inputAmount.String(),
}
// if need swap, then calculate out amount after swap.
if param.Path.SwapFromToken != param.Path.BridgeFromToken {
// swap from token -> bridge from token
swapConfig, err := d.findSwapConfig(param.FromChainId, param.Path.SwapFromToken, param.Path.BridgeFromToken)
if err != nil {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"swap_from_token": param.Path.SwapFromToken,
"bridge_from_token": param.Path.BridgeFromToken,
"error": err,
}).Error("not found swap config for swap from token to bridge from token")
return quote, err
}
swapFromAmount, err := router.GetAmountsOut(chainInfo.cli, common.HexToAddress(param.User), common.HexToAddress(swapConfig.SwapContract), inputAmount, swapConfig.SwapPath)
if err != nil {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"swap_from_token": param.Path.SwapFromToken,
"bridge_from_token": param.Path.BridgeFromToken,
"input_amount": inputAmount.String(),
"error": err,
}).Error("get swap amounts out failed")
return quote, err
}
inputAmount = swapFromAmount
}
// then sub bridge fee.
outConfig, err := d.GetOutConfig(param.FromChainId, common.HexToAddress(param.Path.BridgeFromToken), param.ToChainId)
if err != nil {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"bridge_from_token": param.Path.BridgeFromToken,
"to_chain": param.ToChainId,
"error": err,
}).Error("get out config failed")
return quote, err
}
if outConfig.ReceiveToken != common.HexToAddress(param.Path.BridgeToToken) {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"bridge_from_token": param.Path.BridgeFromToken,
"to_chain": param.ToChainId,
"bridge_to_token": param.Path.BridgeToToken,
"receive_token": outConfig.ReceiveToken.Hex(),
}).Error("bridge to token not match receive token")
return quote, fmt.Errorf("bridge to token not match receive token")
}
fee := outConfig.Fee
if inputAmount.Cmp(fee) <= 0 {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"bridge_from_token": param.Path.BridgeFromToken,
"to_chain": param.ToChainId,
"inputAmount": inputAmount.String(),
"fee": fee.String(),
}).Error("input amount less than fee")
return quote, fmt.Errorf("input amount less than fee")
}
outAmount := new(big.Int).Sub(inputAmount, fee)
quote.OutAmount = outAmount.String()
// if target need swap, then calculate out amount after target swap.
if param.Path.BridgeToToken != param.Path.SwapToToken {
// swap from bridge to token -> swap to token
swapConfig, err := d.findSwapConfig(param.ToChainId, param.Path.BridgeToToken, param.Path.SwapToToken)
if err != nil {
log.WithFields(log.Fields{
"chain_id": param.ToChainId,
"bridge_to_token": param.Path.BridgeToToken,
"swap_to_token": param.Path.SwapToToken,
"error": err,
}).Error("not found swap config for bridge to token to swap to token")
return quote, err
}
swapToAmount, err := router.GetAmountsOut(chainInfo.cli, common.HexToAddress(param.User), common.HexToAddress(swapConfig.SwapContract), outAmount, swapConfig.SwapPath)
if err != nil {
log.WithFields(log.Fields{
"chain_id": param.ToChainId,
"bridge_to_token": param.Path.BridgeToToken,
"swap_to_token": param.Path.SwapToToken,
"input_amount": outAmount.String(),
"error": err,
}).Error("get swap amounts out failed")
return quote, err
}
quote.OutAmount = swapToAmount.String()
}
// build payload
payload, err := d.buildSwapPayload(common.HexToAddress(param.Path.BridgeFromToken), inputAmount, big.NewInt(param.ToChainId), common.HexToAddress(param.Receiver), common.HexToAddress(param.Path.SwapFromToken), common.HexToAddress(param.Path.SwapToToken))
if err != nil {
log.WithFields(log.Fields{
"chain_id": param.FromChainId,
"token": param.Path.SwapFromToken,
"user": param.User,
"inputAmount": inputAmount.String(),
"error": err,
}).Error("build swap payload failed")
return quote, err
}
quote.Payload = common.Bytes2Hex(payload)
return quote, nil
}
func (d *Dao) getSupportedBridgeChains(fromChain int64, token string) (configs []dbModel.BridgeTokenInfo, err error) {
collection := d.db.Collection(new(dbModel.BridgeTokenInfo).TableName())
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
filter := bson.M{"chain_id": fromChain}
if token != "" {
filter["token"] = strings.ToLower(token)
}
cursor, err := collection.Find(ctx, filter)
if err != nil {
return configs, err
}
defer cursor.Close(ctx)
err = cursor.All(ctx, &configs)
return configs, err
}
func (d *Dao) getTargetChainSwapTokens(targetChains []dbModel.BridgeTokenInfo, fromPath apiModel.SwapPath) map[int64]map[string]apiModel.ToSwapToken {
toChains := make(map[int64]map[string]apiModel.ToSwapToken)
for _, sc := range targetChains {
if !sc.Enabled {
continue
}
tokens, exist := toChains[sc.ToChainId]
if !exist {
tokens = make(map[string]apiModel.ToSwapToken)
toChains[sc.ToChainId] = tokens
}
toTokenInfo, err := d.tokenRepo.RetriveTokenInfo(sc.ToChainId, sc.ToToken)
if err != nil {
log.WithFields(log.Fields{
"chain_id": sc.ToChainId,
"token": sc.ToToken,
}).Error("get token info failed")
continue
}
tokens[sc.ToToken] = apiModel.ToSwapToken{
ToChainId: sc.ToChainId,
ToToken: sc.ToToken,
ToTokenSymbol: toTokenInfo.Symbol,
Path: apiModel.SwapPath{
SwapFromToken: fromPath.SwapFromToken,
BridgeFromToken: fromPath.BridgeFromToken,
BridgeToToken: sc.ToToken,
SwapToToken: sc.ToToken,
},
}
// get all supported swap tokens for toChainId
targetChainSwapTokens, err := d.getSupportedSwapTokens(sc.ToChainId)
if err != nil {
continue
}
for _, targetSwap := range targetChainSwapTokens {
if common.HexToAddress(targetSwap.Token) == common.HexToAddress(sc.ToToken) {
tokenInfo, err := d.tokenRepo.RetriveTokenInfo(targetSwap.ChainId, targetSwap.ToToken)
if err != nil {
continue
}
// add targetSwap.ToToken to toChains
tokens[targetSwap.ToToken] = apiModel.ToSwapToken{
ToChainId: targetSwap.ChainId,
ToToken: targetSwap.ToToken,
ToTokenSymbol: tokenInfo.Symbol,
Path: apiModel.SwapPath{
SwapFromToken: fromPath.SwapFromToken,
BridgeFromToken: fromPath.BridgeFromToken,
BridgeToToken: sc.ToToken,
SwapToToken: targetSwap.ToToken,
},
}
}
}
toChains[sc.ToChainId] = tokens
}
return toChains
}
func (d *Dao) getSupportedSwapTokens(chainId int64) (tokens []dbModel.SwapTokenInfo, err error) {
collection := d.db.Collection(new(dbModel.SwapTokenInfo).TableName())
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
cursor, err := collection.Find(ctx, bson.M{"chain_id": chainId})
if err != nil {
return tokens, err
}
defer cursor.Close(ctx)
err = cursor.All(ctx, &tokens)
return tokens, err
}
func (d *Dao) buildSwapPayload(token common.Address, amount *big.Int, toChainID *big.Int, receiver common.Address, fromToken common.Address, toToken common.Address) ([]byte, error) {
contractAbi, _ := bridge.BridgeContractMetaData.GetAbi()
return contractAbi.Pack("outTransferSwap", token, amount, toChainID, receiver, fromToken, toToken)
}
func (d *Dao) findSwapConfig(chainId int64, from string, to string) (*dbModel.SwapTokenInfo, error) {
collection := d.db.Collection(new(dbModel.SwapTokenInfo).TableName())
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var swapConfig dbModel.SwapTokenInfo
err := collection.FindOne(ctx, bson.M{"chain_id": chainId, "token": strings.ToLower(from), "to_token": strings.ToLower(to)}).Decode(&swapConfig)
if err == mongo.ErrNoDocuments {
return nil, ErrRecordNotFound
}
return &swapConfig, err
}
package api
type ToToken struct {
type ToBridgeToken struct {
TokenContract string `json:"token_contract" bson:"token_contract"`
ToChainId int64 `json:"to_chain_id" bson:"to_chain_id"`
ToToken string `json:"to_token" bson:"to_token"`
......@@ -8,29 +8,53 @@ type ToToken struct {
Fee string `json:"fee" bson:"fee"`
MaxLimit string `json:"max_limit" bson:"max_limit"`
}
type SwapPath struct {
SwapFromToken string `json:"swap_from_token" bson:"swap_from_token"`
BridgeFromToken string `json:"bridge_from_token" bson:"bridge_from_token"`
BridgeToToken string `json:"bridge_to_token" bson:"bridge_to_token"`
SwapToToken string `json:"swap_to_token" bson:"swap_to_token"`
}
type ToSwapToken struct {
ToChainId int64 `json:"to_chain_id" bson:"to_chain_id"`
ToToken string `json:"to_token" bson:"to_token"`
ToTokenSymbol string `json:"to_token_symbol" bson:"to_token_symbol"`
Path SwapPath `json:"path" bson:"path"`
}
type SupportBridgeTokenInfo struct {
TokenContract string `json:"token_contract" bson:"token_contract"`
TokenSymbol string `json:"token_symbol" bson:"token_symbol"`
BridgeTokens []ToBridgeToken `json:"bridge_tokens" bson:"bridge_tokens"`
}
type SupportSwapTokenInfo struct {
TokenContract string `json:"token_contract" bson:"token_contract"`
TokenSymbol string `json:"token_symbol" bson:"token_symbol"`
SwapTokens map[int64]map[string]ToSwapToken `json:"swap_tokens" bson:"swap_tokens"`
}
type ChainConfig struct {
Chain string `json:"chain" bson:"chain"`
ChainId int64 `json:"chain_id" bson:"chain_id"`
BridgeContract string `json:"contract" bson:"contract"`
SupportTokens map[string]ToToken `json:"support_tokens" bson:"support_tokens"`
SupportTokens map[string]SupportBridgeTokenInfo `json:"support_tokens" bson:"support_tokens"`
ExplorerUrl string `json:"explorer_url" bson:"explorer_url"`
RpcUrl string `json:"rpc" bson:"rpc"`
}
type TokenInfo struct {
Name string `json:"name" bson:"name"`
Contract string `json:"contract" bson:"contract"`
}
type SwapPair struct {
From TokenInfo `json:"from" bson:"from"`
To TokenInfo `json:"to" bson:"to"`
SwapContract string `json:"swap_contract" bson:"swap_contract"`
SwapPath []string `json:"swap_path" bson:"swap_path"`
type ChainSwapConfig struct {
Chain string `json:"chain" bson:"chain"`
ChainId int64 `json:"chain_id" bson:"chain_id"`
BridgeContract string `json:"contract" bson:"contract"`
SupportTokens map[string]SupportSwapTokenInfo `json:"support_tokens" bson:"support_tokens"`
ExplorerUrl string `json:"explorer_url" bson:"explorer_url"`
RpcUrl string `json:"rpc" bson:"rpc"`
}
type SwapConfigs struct {
SwapPairs map[string]SwapPair `json:"swap_pairs" bson:"swap_pairs"`
Chains map[string]ChainSwapConfig `json:"chains" bson:"chains"`
}
type BridgeConfig struct {
......@@ -58,8 +82,54 @@ type History struct {
Finish []*HistoryInfo `json:"finish" bson:"finish"`
}
type RouterQuery struct {
ChainId int64 `json:"chain_id"`
User string `json:"user"`
}
type QuoteBridgeParam struct {
FromChainId int64 `json:"from_chain_id"`
ToChainId int64 `json:"to_chain_id"`
FromToken string `json:"from_token"`
ToToken string `json:"to_token"`
InputAmount string `json:"amount"`
User string `json:"user"`
Receiver string `json:"receiver"`
}
type QuoteSwapParam struct {
FromChainId int64 `json:"from_chain_id"`
ToChainId int64 `json:"to_chain_id"`
Path SwapPath `json:"path"`
InputAmount string `json:"amount"`
User string `json:"user"`
Receiver string `json:"receiver"`
}
type TokenBalance struct {
Name string `json:"name"`
Decimals int `json:"decimals"`
Balance string `json:"balance"`
Contract string `json:"contract"`
}
type TokenBalances struct {
Balances []TokenBalance `json:"balances"`
}
type QuoteResult struct {
ToContract string `json:"to_contract"`
OutAmount string `json:"out_amount"`
Payload string `json:"payload"`
}
type Querier interface {
GetBridgeConfig() (config BridgeConfig, err error)
GetSwapConfig(chainId int64) (config SwapConfigs, err error)
GetAllChainSwapConfig() (map[int64]*ChainSwapConfig, error)
GetSwapConfig(int64) (*ChainSwapConfig, error)
GetHistoryInfo(user string) (history History, err error)
GetBridgeTokenBalance(chainId int64, user string) (balances TokenBalances, err error)
QuoteBridge(param QuoteBridgeParam) (quote QuoteResult, err error)
GetSwapTokenBalance(chainId int64, user string) (balances TokenBalances, err error)
QuoteSwap(param QuoteSwapParam) (quote QuoteResult, err error)
}
......@@ -2,6 +2,7 @@ package server
import (
"code.wuban.net.cn/movabridge/bridge-backend/constant"
apiModel "code.wuban.net.cn/movabridge/bridge-backend/model/api"
"github.com/ethereum/go-ethereum/common"
log "github.com/sirupsen/logrus"
"strconv"
......@@ -47,21 +48,113 @@ func getHistory(c *gin.Context) {
c.JSON(200, withSuccess(history))
}
func getSwapConfig(c *gin.Context) {
chainIdStr := c.DefaultQuery("chain", "")
chainId, err := strconv.ParseInt(chainIdStr, 10, 64)
func bridgeRouters(c *gin.Context) {
if _querier == nil {
log.Error("querier is nil")
c.JSON(500, withError(constant.InternalError))
return
}
var queryParam apiModel.RouterQuery
if err := c.ShouldBindQuery(&queryParam); err != nil {
log.Errorf("bind query param error: %v", err)
c.JSON(200, withError(constant.InvalidParam))
return
}
config, err := _querier.GetBridgeConfig()
if err != nil {
log.Errorf("convert chainId(%s) to int error: %v", chainIdStr, err)
log.Errorf("get bridge config error: %v", err)
c.JSON(500, withError(constant.InternalError))
return
}
c.JSON(200, withSuccess(config))
}
func bridgeBalance(c *gin.Context) {
if _querier == nil {
log.Error("querier is nil")
c.JSON(500, withError(constant.InternalError))
return
}
var queryParam apiModel.RouterQuery
if err := c.ShouldBindQuery(&queryParam); err != nil {
log.Errorf("bind query param error: %v", err)
c.JSON(200, withError(constant.InvalidParam))
return
}
if !common.IsHexAddress(queryParam.User) {
log.Errorf("invalid user address: %v", queryParam.User)
c.JSON(200, withError(constant.InvalidParam))
}
// get all tokens
balances, err := _querier.GetBridgeTokenBalance(queryParam.ChainId, queryParam.User)
if err != nil {
log.Errorf("get bridge balance error: %v", err)
c.JSON(500, withError(constant.InternalError))
return
}
c.JSON(200, withSuccess(balances))
}
func bridgeQuote(c *gin.Context) {
if _querier == nil {
log.Error("querier is nil")
c.JSON(500, withError(constant.InternalError))
return
}
var queryParam apiModel.QuoteBridgeParam
if err := c.ShouldBindQuery(&queryParam); err != nil {
log.Errorf("bind query param error: %v", err)
c.JSON(200, withError(constant.InvalidParam))
return
}
if !common.IsHexAddress(queryParam.User) {
log.Errorf("invalid user address: %v", queryParam.User)
c.JSON(200, withError(constant.InvalidParam))
return
}
if !common.IsHexAddress(queryParam.Receiver) {
log.Errorf("invalid receiver address: %v", queryParam.Receiver)
c.JSON(200, withError(constant.InvalidParam))
return
}
quote, err := _querier.QuoteBridge(queryParam)
if err != nil {
log.Errorf("get bridge quote error: %v", err)
c.JSON(500, withError(constant.InternalError))
return
}
c.JSON(200, withSuccess(quote))
}
func getSwapRouters(c *gin.Context) {
if _querier == nil {
log.Error("querier is nil")
c.JSON(500, withError(constant.InternalError))
return
}
var queryParam apiModel.RouterQuery
if err := c.ShouldBindQuery(&queryParam); err != nil {
log.Errorf("bind query param error: %v", err)
c.JSON(200, withError(constant.InvalidParam))
return
}
configs, err := _querier.GetSwapConfig(queryParam.ChainId)
if err != nil {
log.Errorf("get swap config error: %v", err)
c.JSON(500, withError(constant.InternalError))
return
}
c.JSON(200, withSuccess(configs))
}
configs, err := _querier.GetSwapConfig(chainId)
func getAllSwapRouters(c *gin.Context) {
if _querier == nil {
log.Error("querier is nil")
c.JSON(500, withError(constant.InternalError))
return
}
configs, err := _querier.GetAllChainSwapConfig()
if err != nil {
log.Errorf("get swap config error: %v", err)
c.JSON(500, withError(constant.InternalError))
......@@ -69,3 +162,86 @@ func getSwapConfig(c *gin.Context) {
}
c.JSON(200, withSuccess(configs))
}
func swapBalance(c *gin.Context) {
if _querier == nil {
log.Error("querier is nil")
c.JSON(500, withError(constant.InternalError))
return
}
var queryParam apiModel.RouterQuery
if err := c.ShouldBindQuery(&queryParam); err != nil {
log.Errorf("bind query param error: %v", err)
c.JSON(200, withError(constant.InvalidParam))
return
}
if !common.IsHexAddress(queryParam.User) {
log.Errorf("invalid user address: %v", queryParam.User)
c.JSON(200, withError(constant.InvalidParam))
}
// get all tokens
balances, err := _querier.GetSwapTokenBalance(queryParam.ChainId, queryParam.User)
if err != nil {
log.Errorf("get swap balance error: %v", err)
c.JSON(500, withError(constant.InternalError))
return
}
c.JSON(200, withSuccess(balances))
}
func swapQuote(c *gin.Context) {
if _querier == nil {
log.Error("querier is nil")
c.JSON(500, withError(constant.InternalError))
return
}
var queryParam apiModel.QuoteSwapParam
if err := c.ShouldBindQuery(&queryParam); err != nil {
log.Errorf("bind query param error: %v", err)
c.JSON(200, withError(constant.InvalidParam))
return
}
if !common.IsHexAddress(queryParam.User) {
log.Errorf("invalid user address: %v", queryParam.User)
c.JSON(200, withError(constant.InvalidParam))
return
}
if !common.IsHexAddress(queryParam.Receiver) {
log.Errorf("invalid receiver address: %v", queryParam.Receiver)
c.JSON(200, withError(constant.InvalidParam))
return
}
quote, err := _querier.QuoteSwap(queryParam)
if err != nil {
log.Errorf("get swap quote error: %v", err)
c.JSON(500, withError(constant.InternalError))
return
}
c.JSON(200, withSuccess(quote))
}
func getSwapRoutersByChainId(c *gin.Context) {
if _querier == nil {
log.Error("querier is nil")
c.JSON(500, withError(constant.InternalError))
return
}
chainIdStr := c.Param("chainid")
if chainIdStr == "" {
c.JSON(200, withError(constant.InvalidParam))
return
}
chainId, err := strconv.ParseInt(chainIdStr, 10, 64)
if err != nil {
log.Errorf("convert chainid(%s) error: %v", chainIdStr, err)
c.JSON(200, withError(constant.InvalidParam))
return
}
config, err := _querier.GetSwapConfig(chainId)
if err != nil {
log.Errorf("get swap config for chain %d error: %v", chainId, err)
c.JSON(500, withError(constant.InternalError))
return
}
c.JSON(200, withSuccess(config))
}
......@@ -11,10 +11,24 @@ func initRouter(conf *config.Config, e *gin.Engine) {
e.Use(middleware.CheckHeaderMiddleware(conf.Server.InvalidHeaders))
v1 := e.Group("/api/v1")
v1.GET("/params", getParam)
{
user := v1.Group("/user")
user.GET("/history", getHistory)
}
v1.GET("/params", getParam)
v1.GET("/swapconfig", getSwapConfig)
{
bridge := v1.Group("/bridge")
bridge.POST("/routers", bridgeRouters)
bridge.POST("/balance", bridgeBalance)
bridge.POST("/quote", bridgeQuote)
}
{
swap := v1.Group("/swap")
swap.GET("/routers", getSwapRouters)
swap.GET("/routers/:chain", getSwapRoutersByChainId)
swap.POST("/balance", swapBalance)
swap.POST("/quote", swapQuote)
}
}
......@@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"math/big"
"strings"
"sync"
)
......@@ -88,3 +89,51 @@ func (tr *TokenRepo) RetriveTokenInfo(chainId int64, address string) (TokenInfo,
tr.SetTokenInfo(address, info)
return info, nil
}
func (tr *TokenRepo) RetriveTokenInfoAndBalance(client *ethclient.Client, address string, user string) (TokenInfo, *big.Int, error) {
info := TokenInfo{}
balance := big.NewInt(0)
contract, err := token.NewTokenCaller(common.HexToAddress(address), client)
if err != nil {
return info, balance, fmt.Errorf("fail to connect contract err: %v", err)
}
callOpt := &bind.CallOpts{
BlockNumber: nil,
From: common.HexToAddress(user),
Context: context.Background(),
}
userBalance, err := contract.BalanceOf(callOpt, common.HexToAddress(user))
if err != nil {
return info, balance, err
} else {
balance = userBalance
}
if value, ok := tr.GetTokenInfo(address); ok {
info = value
} else {
name, err := contract.Name(callOpt)
if err != nil {
return info, balance, err
}
symbol, err := contract.Symbol(callOpt)
if err != nil {
return info, balance, err
}
decimals, err := contract.Decimals(callOpt)
if err != nil {
return info, balance, err
}
info := TokenInfo{
Name: name,
Symbol: symbol,
Decimals: decimals.Int64(),
Address: address,
}
tr.SetTokenInfo(address, info)
}
return info, balance, nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment