diff --git a/.gitattributes b/.gitattributes index 0269fab9cb..573ea56ed5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,5 @@ # Auto detect text files and perform LF normalization * text=auto *.sol linguist-language=Solidity + +contracts/astria_bridgeable_erc20.go linguist-generated=true diff --git a/.gitignore b/.gitignore index 3f27cdc00f..7c8a972858 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,6 @@ profile.cov logs/ tests/spec-tests/ + +contracts/abi +contracts/bin diff --git a/.gitmodules b/.gitmodules index 241c169c47..1bb5a11958 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,3 +6,6 @@ path = tests/evm-benchmarks url = https://github.com/ipsilon/evm-benchmarks shallow = true +[submodule "contracts/astria-bridge-contracts"] + path = contracts/astria-bridge-contracts + url = https://github.com/astriaorg/astria-bridge-contracts.git diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index 03a2e2eb99..319e82c561 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -140,7 +140,7 @@ func Transaction(ctx *cli.Context) error { } // Check intrinsic gas if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, - chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int), 0)); err != nil { + chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int), 0), false); err != nil { r.Error = err results = append(results, r) continue diff --git a/contracts/README.md b/contracts/README.md new file mode 100644 index 0000000000..bfef64a4cc --- /dev/null +++ b/contracts/README.md @@ -0,0 +1,95 @@ +# astria bridgeable erc20s + +Package for the `AstriaBridgeableERC20` contract. + +## Initializing + +Requirements: + +- foundry + +Build: + +```sh +git submodule update --init --recursive +cd astria-bridge-contracts +forge build +``` + +## Go bindings + +If you change the contract and wish to update the go bindings, run: + +```sh +chmod +x +./generate-bindings.sh +``` + +## Testing + +To test the full end-to-end flow, run the sequencer, cometbft, composer, and conductor. Ensure the configured chain IDs are correct. + +Copy the example .env: + +```sh +cp local.env.example .env && source .env +``` + +Deploy `AstriaBridgeableERC20.sol`: + +```sh +forge script script/AstriaBridgeableERC20.s.sol:AstriaBridgeableERC20Script \ + --rpc-url $RPC_URL --broadcast --sig "deploy()" -vvvv +``` + +Take note of the deployed address. + +Add the following to the genesis file under `astriaBridgeAddresses`: + +```json +"astriaBridgeAddresses": [ + { + "bridgeAddress": "0x1c0c490f1b5528d8173c5de46d131160e4b2c0c3", + "startHeight": 1, + "assetDenom": "nria", + "assetPrecision": 6, + "erc20asset": { + "contractAddress":"0x9Aae647A1CB2ec6b39afd552aD149F6A26Bb2aD6", + "contractPrecision": 18 + } + } +], +``` + +Note: this mints `nria` as an erc20 instead of the native asset. + +`bridgeAddress` is the bridge address that corresponds to this asset on the sequencer chain. +`assetDenom` does not need to match the name of the token in the deployed contract, but it does need to match the denom of the token on the sequencer. +`contractAddress` in `erc20asset` is the address of the contract deployed above. + +Stop the geth node and rerun `geth init --genesis genesis.json`. Restart the node. The contract is now initialized as a bridge from the sequencer. + +Run the following with the `astria-cli`: + +```sh +# this matches the `bridgeAddress` 0x1c0c490f1b5528d8173c5de46d131160e4b2c0c3 in the genesis above +export SEQUENCER_PRIVATE_KEY=2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90 +./target/debug/astria-cli sequencer init-bridge-account --sequencer-url=http://localhost:26657 --rollup-name=astria +# the `destination-chain-address` matches the `PRIVATE_KEY` in local.example.env +./target/debug/astria-cli sequencer bridge-lock --sequencer-url=http://localhost:26657 --amount=1000000 --destination-chain-address=0x46B77EFDFB20979E1C29ec98DcE73e3eCbF64102 --sequencer.chain-id=astria -- 1c0c490f1b5528d8173c5de46d131160e4b2c0c3 +``` + +This initializes the bridge account and also transfer funds over. + +Check your ERC20 balance: + +```sh +forge script script/AstriaBridgeableERC20.s.sol:AstriaBridgeableERC20Script \ + --rpc-url $RPC_URL --sig "getBalance()" -vvvv +``` + +If everything worked, you should see a balance logged: +``` +== Logs == + 1000000000000000000 +``` diff --git a/contracts/astria-bridge-contracts b/contracts/astria-bridge-contracts new file mode 160000 index 0000000000..4580ffc074 --- /dev/null +++ b/contracts/astria-bridge-contracts @@ -0,0 +1 @@ +Subproject commit 4580ffc0747f463e304214bb29848e21e4e93e32 diff --git a/contracts/astria_bridgeable_erc20.go b/contracts/astria_bridgeable_erc20.go new file mode 100644 index 0000000000..26c63dd9c2 --- /dev/null +++ b/contracts/astria_bridgeable_erc20.go @@ -0,0 +1,1401 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package contracts + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "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/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// AstriaBridgeableERC20MetaData contains all meta data concerning the AstriaBridgeableERC20 contract. +var AstriaBridgeableERC20MetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_bridge\",\"type\":\"address\"},{\"internalType\":\"uint32\",\"name\":\"_baseChainAssetPrecision\",\"type\":\"uint32\"},{\"internalType\":\"string\",\"name\":\"_baseChainBridgeAddress\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_baseChainAssetDenomination\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_name\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_symbol\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"destinationChainAddress\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"memo\",\"type\":\"string\"}],\"name\":\"Ics20Withdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Mint\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"destinationChainAddress\",\"type\":\"string\"}],\"name\":\"SequencerWithdrawal\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"BASE_CHAIN_ASSET_DENOMINATION\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"BASE_CHAIN_ASSET_PRECISION\",\"outputs\":[{\"internalType\":\"uint32\",\"name\":\"\",\"type\":\"uint32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"BASE_CHAIN_BRIDGE_ADDRESS\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"BRIDGE\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"_destinationChainAddress\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_memo\",\"type\":\"string\"}],\"name\":\"withdrawToIbcChain\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_amount\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"_destinationChainAddress\",\"type\":\"string\"}],\"name\":\"withdrawToSequencer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60e060405234801562000010575f80fd5b506040516200138838038062001388833981016040819052620000339162000266565b81816005620000438382620003c8565b506006620000528282620003c8565b5050505f620000666200017360201b60201c565b90508060ff168663ffffffff161115620001125760405162461bcd60e51b815260206004820152605e60248201527f41737472696142726964676561626c6545524332303a2062617365206368616960448201527f6e20617373657420707265636973696f6e206d757374206265206c657373207460648201527f68616e206f7220657175616c20746f20746f6b656e20646563696d616c730000608482015260a40160405180910390fd5b63ffffffff86166080525f620001298682620003c8565b506001620001388582620003c8565b50620001488660ff8316620004a4565b6200015590600a620005c6565b60c0525050506001600160a01b0390931660a05250620005e0915050565b601290565b80516001600160a01b03811681146200018f575f80fd5b919050565b805163ffffffff811681146200018f575f80fd5b634e487b7160e01b5f52604160045260245ffd5b5f82601f830112620001cc575f80fd5b81516001600160401b0380821115620001e957620001e9620001a8565b604051601f8301601f19908116603f01168101908282118183101715620002145762000214620001a8565b8160405283815260209250868385880101111562000230575f80fd5b5f91505b8382101562000253578582018301518183018401529082019062000234565b5f93810190920192909252949350505050565b5f805f805f8060c087890312156200027c575f80fd5b620002878762000178565b9550620002976020880162000194565b60408801519095506001600160401b0380821115620002b4575f80fd5b620002c28a838b01620001bc565b95506060890151915080821115620002d8575f80fd5b620002e68a838b01620001bc565b94506080890151915080821115620002fc575f80fd5b6200030a8a838b01620001bc565b935060a089015191508082111562000320575f80fd5b506200032f89828a01620001bc565b9150509295509295509295565b600181811c908216806200035157607f821691505b6020821081036200037057634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115620003c3575f81815260208120601f850160051c810160208610156200039e5750805b601f850160051c820191505b81811015620003bf57828155600101620003aa565b5050505b505050565b81516001600160401b03811115620003e457620003e4620001a8565b620003fc81620003f584546200033c565b8462000376565b602080601f83116001811462000432575f84156200041a5750858301515b5f19600386901b1c1916600185901b178555620003bf565b5f85815260208120601f198616915b82811015620004625788860151825594840194600190910190840162000441565b50858210156200048057878501515f19600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b5f52601160045260245ffd5b63ffffffff828116828216039080821115620004c457620004c462000490565b5092915050565b600181815b808511156200050b57815f1904821115620004ef57620004ef62000490565b80851615620004fd57918102915b93841c9390800290620004d0565b509250929050565b5f826200052357506001620005c0565b816200053157505f620005c0565b81600181146200054a5760028114620005555762000575565b6001915050620005c0565b60ff84111562000569576200056962000490565b50506001821b620005c0565b5060208310610133831016604e8410600b84101617156200059a575081810a620005c0565b620005a68383620004cb565b805f1904821115620005bc57620005bc62000490565b0290505b92915050565b5f620005d963ffffffff84168362000513565b9392505050565b60805160a05160c051610d6f620006195f395f818161046a01526105b501525f818161027b015261038c01525f6101c90152610d6f5ff3fe608060405234801561000f575f80fd5b50600436106100fb575f3560e01c80637eb6dec711610093578063d38fe9a711610063578063d38fe9a714610223578063db97dc9814610236578063dd62ed3e1461023e578063ee9a31a214610276575f80fd5b80637eb6dec7146101c457806395d89b4114610200578063a9059cbb14610208578063b6476c7e1461021b575f80fd5b8063313ce567116100ce578063313ce5671461016557806340c10f19146101745780635fe56b091461018957806370a082311461019c575f80fd5b806306fdde03146100ff578063095ea7b31461011d57806318160ddd1461014057806323b872dd14610152575b5f80fd5b6101076102b5565b6040516101149190610997565b60405180910390f35b61013061012b3660046109fd565b610345565b6040519015158152602001610114565b6004545b604051908152602001610114565b610130610160366004610a25565b61035e565b60405160128152602001610114565b6101876101823660046109fd565b610381565b005b610187610197366004610aa3565b610463565b6101446101aa366004610b17565b6001600160a01b03165f9081526002602052604090205490565b6101eb7f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff9091168152602001610114565b610107610506565b6101306102163660046109fd565b610515565b610107610522565b610187610231366004610b37565b6105ae565b61010761064b565b61014461024c366004610b7f565b6001600160a01b039182165f90815260036020908152604080832093909416825291909152205490565b61029d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610114565b6060600580546102c490610bb0565b80601f01602080910402602001604051908101604052809291908181526020018280546102f090610bb0565b801561033b5780601f106103125761010080835404028352916020019161033b565b820191905f5260205f20905b81548152906001019060200180831161031e57829003601f168201915b5050505050905090565b5f33610352818585610657565b60019150505b92915050565b5f3361036b858285610669565b6103768585856106e4565b506001949350505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104125760405162461bcd60e51b815260206004820152602b60248201527f41737472696142726964676561626c6545524332303a206f6e6c79206272696460448201526a19d94818d85b881b5a5b9d60aa1b60648201526084015b60405180910390fd5b61041c8282610741565b816001600160a01b03167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968858260405161045791815260200190565b60405180910390a25050565b845f61048f7f000000000000000000000000000000000000000000000000000000000000000083610be8565b116104ac5760405162461bcd60e51b815260040161040990610c07565b6104b63387610779565b85336001600160a01b03167f0c64e29a5254a71c7f4e52b3d2d236348c80e00a00ba2e1961962bd2827c03fb878787876040516104f69493929190610cce565b60405180910390a3505050505050565b6060600680546102c490610bb0565b5f336103528185856106e4565b6001805461052f90610bb0565b80601f016020809104026020016040519081016040528092919081815260200182805461055b90610bb0565b80156105a65780601f1061057d576101008083540402835291602001916105a6565b820191905f5260205f20905b81548152906001019060200180831161058957829003601f168201915b505050505081565b825f6105da7f000000000000000000000000000000000000000000000000000000000000000083610be8565b116105f75760405162461bcd60e51b815260040161040990610c07565b6106013385610779565b83336001600160a01b03167f0f4961cab7530804898499aa89f5ec81d1a73102e2e4a1f30f88e5ae3513ba2a858560405161063d929190610cff565b60405180910390a350505050565b5f805461052f90610bb0565b61066483838360016107ad565b505050565b6001600160a01b038381165f908152600360209081526040808320938616835292905220545f1981146106de57818110156106d057604051637dc7a0d960e11b81526001600160a01b03841660048201526024810182905260448101839052606401610409565b6106de84848484035f6107ad565b50505050565b6001600160a01b03831661070d57604051634b637e8f60e11b81525f6004820152602401610409565b6001600160a01b0382166107365760405163ec442f0560e01b81525f6004820152602401610409565b610664838383610871565b6001600160a01b03821661076a5760405163ec442f0560e01b81525f6004820152602401610409565b6107755f8383610871565b5050565b6001600160a01b0382166107a257604051634b637e8f60e11b81525f6004820152602401610409565b610775825f83610871565b6001600160a01b0384166107d65760405163e602df0560e01b81525f6004820152602401610409565b6001600160a01b0383166107ff57604051634a1406b160e11b81525f6004820152602401610409565b6001600160a01b038085165f90815260036020908152604080832093871683529290522082905580156106de57826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161063d91815260200190565b6001600160a01b03831661089b578060045f8282546108909190610d1a565b9091555061090b9050565b6001600160a01b0383165f90815260026020526040902054818110156108ed5760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610409565b6001600160a01b0384165f9081526002602052604090209082900390555b6001600160a01b03821661092757600480548290039055610945565b6001600160a01b0382165f9081526002602052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161098a91815260200190565b60405180910390a3505050565b5f6020808352835180828501525f5b818110156109c2578581018301518582016040015282016109a6565b505f604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b03811681146109f8575f80fd5b919050565b5f8060408385031215610a0e575f80fd5b610a17836109e2565b946020939093013593505050565b5f805f60608486031215610a37575f80fd5b610a40846109e2565b9250610a4e602085016109e2565b9150604084013590509250925092565b5f8083601f840112610a6e575f80fd5b50813567ffffffffffffffff811115610a85575f80fd5b602083019150836020828501011115610a9c575f80fd5b9250929050565b5f805f805f60608688031215610ab7575f80fd5b85359450602086013567ffffffffffffffff80821115610ad5575f80fd5b610ae189838a01610a5e565b90965094506040880135915080821115610af9575f80fd5b50610b0688828901610a5e565b969995985093965092949392505050565b5f60208284031215610b27575f80fd5b610b30826109e2565b9392505050565b5f805f60408486031215610b49575f80fd5b83359250602084013567ffffffffffffffff811115610b66575f80fd5b610b7286828701610a5e565b9497909650939450505050565b5f8060408385031215610b90575f80fd5b610b99836109e2565b9150610ba7602084016109e2565b90509250929050565b600181811c90821680610bc457607f821691505b602082108103610be257634e487b7160e01b5f52602260045260245ffd5b50919050565b5f82610c0257634e487b7160e01b5f52601260045260245ffd5b500490565b60208082526073908201527f41737472696142726964676561626c6545524332303a20696e7375666669636960408201527f656e742076616c75652c206d7573742062652067726561746572207468616e2060608201527f3130202a2a2028544f4b454e5f444543494d414c53202d20424153455f434841608082015272494e5f41535345545f505245434953494f4e2960681b60a082015260c00190565b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b604081525f610ce1604083018688610ca6565b8281036020840152610cf4818587610ca6565b979650505050505050565b602081525f610d12602083018486610ca6565b949350505050565b8082018082111561035857634e487b7160e01b5f52601160045260245ffdfea2646970667358221220839fde846bdf8b562d3e7e8b39bc0acde2c69b1f2a7bba30ec27b96ba437f6a564736f6c63430008150033", +} + +// AstriaBridgeableERC20ABI is the input ABI used to generate the binding from. +// Deprecated: Use AstriaBridgeableERC20MetaData.ABI instead. +var AstriaBridgeableERC20ABI = AstriaBridgeableERC20MetaData.ABI + +// AstriaBridgeableERC20Bin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use AstriaBridgeableERC20MetaData.Bin instead. +var AstriaBridgeableERC20Bin = AstriaBridgeableERC20MetaData.Bin + +// DeployAstriaBridgeableERC20 deploys a new Ethereum contract, binding an instance of AstriaBridgeableERC20 to it. +func DeployAstriaBridgeableERC20(auth *bind.TransactOpts, backend bind.ContractBackend, _bridge common.Address, _baseChainAssetPrecision uint32, _baseChainBridgeAddress string, _baseChainAssetDenomination string, _name string, _symbol string) (common.Address, *types.Transaction, *AstriaBridgeableERC20, error) { + parsed, err := AstriaBridgeableERC20MetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(AstriaBridgeableERC20Bin), backend, _bridge, _baseChainAssetPrecision, _baseChainBridgeAddress, _baseChainAssetDenomination, _name, _symbol) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &AstriaBridgeableERC20{AstriaBridgeableERC20Caller: AstriaBridgeableERC20Caller{contract: contract}, AstriaBridgeableERC20Transactor: AstriaBridgeableERC20Transactor{contract: contract}, AstriaBridgeableERC20Filterer: AstriaBridgeableERC20Filterer{contract: contract}}, nil +} + +// AstriaBridgeableERC20 is an auto generated Go binding around an Ethereum contract. +type AstriaBridgeableERC20 struct { + AstriaBridgeableERC20Caller // Read-only binding to the contract + AstriaBridgeableERC20Transactor // Write-only binding to the contract + AstriaBridgeableERC20Filterer // Log filterer for contract events +} + +// AstriaBridgeableERC20Caller is an auto generated read-only Go binding around an Ethereum contract. +type AstriaBridgeableERC20Caller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// AstriaBridgeableERC20Transactor is an auto generated write-only Go binding around an Ethereum contract. +type AstriaBridgeableERC20Transactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// AstriaBridgeableERC20Filterer is an auto generated log filtering Go binding around an Ethereum contract events. +type AstriaBridgeableERC20Filterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// AstriaBridgeableERC20Session is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type AstriaBridgeableERC20Session struct { + Contract *AstriaBridgeableERC20 // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// AstriaBridgeableERC20CallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type AstriaBridgeableERC20CallerSession struct { + Contract *AstriaBridgeableERC20Caller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// AstriaBridgeableERC20TransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type AstriaBridgeableERC20TransactorSession struct { + Contract *AstriaBridgeableERC20Transactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// AstriaBridgeableERC20Raw is an auto generated low-level Go binding around an Ethereum contract. +type AstriaBridgeableERC20Raw struct { + Contract *AstriaBridgeableERC20 // Generic contract binding to access the raw methods on +} + +// AstriaBridgeableERC20CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type AstriaBridgeableERC20CallerRaw struct { + Contract *AstriaBridgeableERC20Caller // Generic read-only contract binding to access the raw methods on +} + +// AstriaBridgeableERC20TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type AstriaBridgeableERC20TransactorRaw struct { + Contract *AstriaBridgeableERC20Transactor // Generic write-only contract binding to access the raw methods on +} + +// NewAstriaBridgeableERC20 creates a new instance of AstriaBridgeableERC20, bound to a specific deployed contract. +func NewAstriaBridgeableERC20(address common.Address, backend bind.ContractBackend) (*AstriaBridgeableERC20, error) { + contract, err := bindAstriaBridgeableERC20(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &AstriaBridgeableERC20{AstriaBridgeableERC20Caller: AstriaBridgeableERC20Caller{contract: contract}, AstriaBridgeableERC20Transactor: AstriaBridgeableERC20Transactor{contract: contract}, AstriaBridgeableERC20Filterer: AstriaBridgeableERC20Filterer{contract: contract}}, nil +} + +// NewAstriaBridgeableERC20Caller creates a new read-only instance of AstriaBridgeableERC20, bound to a specific deployed contract. +func NewAstriaBridgeableERC20Caller(address common.Address, caller bind.ContractCaller) (*AstriaBridgeableERC20Caller, error) { + contract, err := bindAstriaBridgeableERC20(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &AstriaBridgeableERC20Caller{contract: contract}, nil +} + +// NewAstriaBridgeableERC20Transactor creates a new write-only instance of AstriaBridgeableERC20, bound to a specific deployed contract. +func NewAstriaBridgeableERC20Transactor(address common.Address, transactor bind.ContractTransactor) (*AstriaBridgeableERC20Transactor, error) { + contract, err := bindAstriaBridgeableERC20(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &AstriaBridgeableERC20Transactor{contract: contract}, nil +} + +// NewAstriaBridgeableERC20Filterer creates a new log filterer instance of AstriaBridgeableERC20, bound to a specific deployed contract. +func NewAstriaBridgeableERC20Filterer(address common.Address, filterer bind.ContractFilterer) (*AstriaBridgeableERC20Filterer, error) { + contract, err := bindAstriaBridgeableERC20(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &AstriaBridgeableERC20Filterer{contract: contract}, nil +} + +// bindAstriaBridgeableERC20 binds a generic wrapper to an already deployed contract. +func bindAstriaBridgeableERC20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := AstriaBridgeableERC20MetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AstriaBridgeableERC20.Contract.AstriaBridgeableERC20Caller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.AstriaBridgeableERC20Transactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.AstriaBridgeableERC20Transactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _AstriaBridgeableERC20.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.contract.Transact(opts, method, params...) +} + +// BASECHAINASSETDENOMINATION is a free data retrieval call binding the contract method 0xb6476c7e. +// +// Solidity: function BASE_CHAIN_ASSET_DENOMINATION() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Caller) BASECHAINASSETDENOMINATION(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _AstriaBridgeableERC20.contract.Call(opts, &out, "BASE_CHAIN_ASSET_DENOMINATION") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// BASECHAINASSETDENOMINATION is a free data retrieval call binding the contract method 0xb6476c7e. +// +// Solidity: function BASE_CHAIN_ASSET_DENOMINATION() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) BASECHAINASSETDENOMINATION() (string, error) { + return _AstriaBridgeableERC20.Contract.BASECHAINASSETDENOMINATION(&_AstriaBridgeableERC20.CallOpts) +} + +// BASECHAINASSETDENOMINATION is a free data retrieval call binding the contract method 0xb6476c7e. +// +// Solidity: function BASE_CHAIN_ASSET_DENOMINATION() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerSession) BASECHAINASSETDENOMINATION() (string, error) { + return _AstriaBridgeableERC20.Contract.BASECHAINASSETDENOMINATION(&_AstriaBridgeableERC20.CallOpts) +} + +// BASECHAINASSETPRECISION is a free data retrieval call binding the contract method 0x7eb6dec7. +// +// Solidity: function BASE_CHAIN_ASSET_PRECISION() view returns(uint32) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Caller) BASECHAINASSETPRECISION(opts *bind.CallOpts) (uint32, error) { + var out []interface{} + err := _AstriaBridgeableERC20.contract.Call(opts, &out, "BASE_CHAIN_ASSET_PRECISION") + + if err != nil { + return *new(uint32), err + } + + out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) + + return out0, err + +} + +// BASECHAINASSETPRECISION is a free data retrieval call binding the contract method 0x7eb6dec7. +// +// Solidity: function BASE_CHAIN_ASSET_PRECISION() view returns(uint32) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) BASECHAINASSETPRECISION() (uint32, error) { + return _AstriaBridgeableERC20.Contract.BASECHAINASSETPRECISION(&_AstriaBridgeableERC20.CallOpts) +} + +// BASECHAINASSETPRECISION is a free data retrieval call binding the contract method 0x7eb6dec7. +// +// Solidity: function BASE_CHAIN_ASSET_PRECISION() view returns(uint32) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerSession) BASECHAINASSETPRECISION() (uint32, error) { + return _AstriaBridgeableERC20.Contract.BASECHAINASSETPRECISION(&_AstriaBridgeableERC20.CallOpts) +} + +// BASECHAINBRIDGEADDRESS is a free data retrieval call binding the contract method 0xdb97dc98. +// +// Solidity: function BASE_CHAIN_BRIDGE_ADDRESS() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Caller) BASECHAINBRIDGEADDRESS(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _AstriaBridgeableERC20.contract.Call(opts, &out, "BASE_CHAIN_BRIDGE_ADDRESS") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// BASECHAINBRIDGEADDRESS is a free data retrieval call binding the contract method 0xdb97dc98. +// +// Solidity: function BASE_CHAIN_BRIDGE_ADDRESS() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) BASECHAINBRIDGEADDRESS() (string, error) { + return _AstriaBridgeableERC20.Contract.BASECHAINBRIDGEADDRESS(&_AstriaBridgeableERC20.CallOpts) +} + +// BASECHAINBRIDGEADDRESS is a free data retrieval call binding the contract method 0xdb97dc98. +// +// Solidity: function BASE_CHAIN_BRIDGE_ADDRESS() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerSession) BASECHAINBRIDGEADDRESS() (string, error) { + return _AstriaBridgeableERC20.Contract.BASECHAINBRIDGEADDRESS(&_AstriaBridgeableERC20.CallOpts) +} + +// BRIDGE is a free data retrieval call binding the contract method 0xee9a31a2. +// +// Solidity: function BRIDGE() view returns(address) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Caller) BRIDGE(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _AstriaBridgeableERC20.contract.Call(opts, &out, "BRIDGE") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// BRIDGE is a free data retrieval call binding the contract method 0xee9a31a2. +// +// Solidity: function BRIDGE() view returns(address) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) BRIDGE() (common.Address, error) { + return _AstriaBridgeableERC20.Contract.BRIDGE(&_AstriaBridgeableERC20.CallOpts) +} + +// BRIDGE is a free data retrieval call binding the contract method 0xee9a31a2. +// +// Solidity: function BRIDGE() view returns(address) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerSession) BRIDGE() (common.Address, error) { + return _AstriaBridgeableERC20.Contract.BRIDGE(&_AstriaBridgeableERC20.CallOpts) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Caller) Allowance(opts *bind.CallOpts, owner common.Address, spender common.Address) (*big.Int, error) { + var out []interface{} + err := _AstriaBridgeableERC20.contract.Call(opts, &out, "allowance", owner, spender) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _AstriaBridgeableERC20.Contract.Allowance(&_AstriaBridgeableERC20.CallOpts, owner, spender) +} + +// Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. +// +// Solidity: function allowance(address owner, address spender) view returns(uint256) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerSession) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _AstriaBridgeableERC20.Contract.Allowance(&_AstriaBridgeableERC20.CallOpts, owner, spender) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Caller) BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) { + var out []interface{} + err := _AstriaBridgeableERC20.contract.Call(opts, &out, "balanceOf", account) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) BalanceOf(account common.Address) (*big.Int, error) { + return _AstriaBridgeableERC20.Contract.BalanceOf(&_AstriaBridgeableERC20.CallOpts, account) +} + +// BalanceOf is a free data retrieval call binding the contract method 0x70a08231. +// +// Solidity: function balanceOf(address account) view returns(uint256) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerSession) BalanceOf(account common.Address) (*big.Int, error) { + return _AstriaBridgeableERC20.Contract.BalanceOf(&_AstriaBridgeableERC20.CallOpts, account) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Caller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _AstriaBridgeableERC20.contract.Call(opts, &out, "decimals") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) Decimals() (uint8, error) { + return _AstriaBridgeableERC20.Contract.Decimals(&_AstriaBridgeableERC20.CallOpts) +} + +// Decimals is a free data retrieval call binding the contract method 0x313ce567. +// +// Solidity: function decimals() view returns(uint8) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerSession) Decimals() (uint8, error) { + return _AstriaBridgeableERC20.Contract.Decimals(&_AstriaBridgeableERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Caller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _AstriaBridgeableERC20.contract.Call(opts, &out, "name") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) Name() (string, error) { + return _AstriaBridgeableERC20.Contract.Name(&_AstriaBridgeableERC20.CallOpts) +} + +// Name is a free data retrieval call binding the contract method 0x06fdde03. +// +// Solidity: function name() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerSession) Name() (string, error) { + return _AstriaBridgeableERC20.Contract.Name(&_AstriaBridgeableERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Caller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _AstriaBridgeableERC20.contract.Call(opts, &out, "symbol") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) Symbol() (string, error) { + return _AstriaBridgeableERC20.Contract.Symbol(&_AstriaBridgeableERC20.CallOpts) +} + +// Symbol is a free data retrieval call binding the contract method 0x95d89b41. +// +// Solidity: function symbol() view returns(string) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerSession) Symbol() (string, error) { + return _AstriaBridgeableERC20.Contract.Symbol(&_AstriaBridgeableERC20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Caller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _AstriaBridgeableERC20.contract.Call(opts, &out, "totalSupply") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) TotalSupply() (*big.Int, error) { + return _AstriaBridgeableERC20.Contract.TotalSupply(&_AstriaBridgeableERC20.CallOpts) +} + +// TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. +// +// Solidity: function totalSupply() view returns(uint256) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20CallerSession) TotalSupply() (*big.Int, error) { + return _AstriaBridgeableERC20.Contract.TotalSupply(&_AstriaBridgeableERC20.CallOpts) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Transactor) Approve(opts *bind.TransactOpts, spender common.Address, value *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.contract.Transact(opts, "approve", spender, value) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.Approve(&_AstriaBridgeableERC20.TransactOpts, spender, value) +} + +// Approve is a paid mutator transaction binding the contract method 0x095ea7b3. +// +// Solidity: function approve(address spender, uint256 value) returns(bool) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20TransactorSession) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.Approve(&_AstriaBridgeableERC20.TransactOpts, spender, value) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address _to, uint256 _amount) returns() +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Transactor) Mint(opts *bind.TransactOpts, _to common.Address, _amount *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.contract.Transact(opts, "mint", _to, _amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address _to, uint256 _amount) returns() +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) Mint(_to common.Address, _amount *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.Mint(&_AstriaBridgeableERC20.TransactOpts, _to, _amount) +} + +// Mint is a paid mutator transaction binding the contract method 0x40c10f19. +// +// Solidity: function mint(address _to, uint256 _amount) returns() +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20TransactorSession) Mint(_to common.Address, _amount *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.Mint(&_AstriaBridgeableERC20.TransactOpts, _to, _amount) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Transactor) Transfer(opts *bind.TransactOpts, to common.Address, value *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.contract.Transact(opts, "transfer", to, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.Transfer(&_AstriaBridgeableERC20.TransactOpts, to, value) +} + +// Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. +// +// Solidity: function transfer(address to, uint256 value) returns(bool) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20TransactorSession) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.Transfer(&_AstriaBridgeableERC20.TransactOpts, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Transactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.contract.Transact(opts, "transferFrom", from, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.TransferFrom(&_AstriaBridgeableERC20.TransactOpts, from, to, value) +} + +// TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. +// +// Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20TransactorSession) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.TransferFrom(&_AstriaBridgeableERC20.TransactOpts, from, to, value) +} + +// WithdrawToIbcChain is a paid mutator transaction binding the contract method 0x5fe56b09. +// +// Solidity: function withdrawToIbcChain(uint256 _amount, string _destinationChainAddress, string _memo) returns() +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Transactor) WithdrawToIbcChain(opts *bind.TransactOpts, _amount *big.Int, _destinationChainAddress string, _memo string) (*types.Transaction, error) { + return _AstriaBridgeableERC20.contract.Transact(opts, "withdrawToIbcChain", _amount, _destinationChainAddress, _memo) +} + +// WithdrawToIbcChain is a paid mutator transaction binding the contract method 0x5fe56b09. +// +// Solidity: function withdrawToIbcChain(uint256 _amount, string _destinationChainAddress, string _memo) returns() +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) WithdrawToIbcChain(_amount *big.Int, _destinationChainAddress string, _memo string) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.WithdrawToIbcChain(&_AstriaBridgeableERC20.TransactOpts, _amount, _destinationChainAddress, _memo) +} + +// WithdrawToIbcChain is a paid mutator transaction binding the contract method 0x5fe56b09. +// +// Solidity: function withdrawToIbcChain(uint256 _amount, string _destinationChainAddress, string _memo) returns() +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20TransactorSession) WithdrawToIbcChain(_amount *big.Int, _destinationChainAddress string, _memo string) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.WithdrawToIbcChain(&_AstriaBridgeableERC20.TransactOpts, _amount, _destinationChainAddress, _memo) +} + +// WithdrawToSequencer is a paid mutator transaction binding the contract method 0xd38fe9a7. +// +// Solidity: function withdrawToSequencer(uint256 _amount, string _destinationChainAddress) returns() +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Transactor) WithdrawToSequencer(opts *bind.TransactOpts, _amount *big.Int, _destinationChainAddress string) (*types.Transaction, error) { + return _AstriaBridgeableERC20.contract.Transact(opts, "withdrawToSequencer", _amount, _destinationChainAddress) +} + +// WithdrawToSequencer is a paid mutator transaction binding the contract method 0xd38fe9a7. +// +// Solidity: function withdrawToSequencer(uint256 _amount, string _destinationChainAddress) returns() +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Session) WithdrawToSequencer(_amount *big.Int, _destinationChainAddress string) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.WithdrawToSequencer(&_AstriaBridgeableERC20.TransactOpts, _amount, _destinationChainAddress) +} + +// WithdrawToSequencer is a paid mutator transaction binding the contract method 0xd38fe9a7. +// +// Solidity: function withdrawToSequencer(uint256 _amount, string _destinationChainAddress) returns() +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20TransactorSession) WithdrawToSequencer(_amount *big.Int, _destinationChainAddress string) (*types.Transaction, error) { + return _AstriaBridgeableERC20.Contract.WithdrawToSequencer(&_AstriaBridgeableERC20.TransactOpts, _amount, _destinationChainAddress) +} + +// AstriaBridgeableERC20ApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the AstriaBridgeableERC20 contract. +type AstriaBridgeableERC20ApprovalIterator struct { + Event *AstriaBridgeableERC20Approval // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *AstriaBridgeableERC20ApprovalIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(AstriaBridgeableERC20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(AstriaBridgeableERC20Approval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *AstriaBridgeableERC20ApprovalIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *AstriaBridgeableERC20ApprovalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// AstriaBridgeableERC20Approval represents a Approval event raised by the AstriaBridgeableERC20 contract. +type AstriaBridgeableERC20Approval struct { + Owner common.Address + Spender common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterApproval is a free log retrieval operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*AstriaBridgeableERC20ApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _AstriaBridgeableERC20.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return &AstriaBridgeableERC20ApprovalIterator{contract: _AstriaBridgeableERC20.contract, event: "Approval", logs: logs, sub: sub}, nil +} + +// WatchApproval is a free log subscription operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *AstriaBridgeableERC20Approval, owner []common.Address, spender []common.Address) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _AstriaBridgeableERC20.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(AstriaBridgeableERC20Approval) + if err := _AstriaBridgeableERC20.contract.UnpackLog(event, "Approval", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseApproval is a log parse operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. +// +// Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) ParseApproval(log types.Log) (*AstriaBridgeableERC20Approval, error) { + event := new(AstriaBridgeableERC20Approval) + if err := _AstriaBridgeableERC20.contract.UnpackLog(event, "Approval", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// AstriaBridgeableERC20Ics20WithdrawalIterator is returned from FilterIcs20Withdrawal and is used to iterate over the raw logs and unpacked data for Ics20Withdrawal events raised by the AstriaBridgeableERC20 contract. +type AstriaBridgeableERC20Ics20WithdrawalIterator struct { + Event *AstriaBridgeableERC20Ics20Withdrawal // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *AstriaBridgeableERC20Ics20WithdrawalIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(AstriaBridgeableERC20Ics20Withdrawal) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(AstriaBridgeableERC20Ics20Withdrawal) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *AstriaBridgeableERC20Ics20WithdrawalIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *AstriaBridgeableERC20Ics20WithdrawalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// AstriaBridgeableERC20Ics20Withdrawal represents a Ics20Withdrawal event raised by the AstriaBridgeableERC20 contract. +type AstriaBridgeableERC20Ics20Withdrawal struct { + Sender common.Address + Amount *big.Int + DestinationChainAddress string + Memo string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterIcs20Withdrawal is a free log retrieval operation binding the contract event 0x0c64e29a5254a71c7f4e52b3d2d236348c80e00a00ba2e1961962bd2827c03fb. +// +// Solidity: event Ics20Withdrawal(address indexed sender, uint256 indexed amount, string destinationChainAddress, string memo) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) FilterIcs20Withdrawal(opts *bind.FilterOpts, sender []common.Address, amount []*big.Int) (*AstriaBridgeableERC20Ics20WithdrawalIterator, error) { + + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + + logs, sub, err := _AstriaBridgeableERC20.contract.FilterLogs(opts, "Ics20Withdrawal", senderRule, amountRule) + if err != nil { + return nil, err + } + return &AstriaBridgeableERC20Ics20WithdrawalIterator{contract: _AstriaBridgeableERC20.contract, event: "Ics20Withdrawal", logs: logs, sub: sub}, nil +} + +// WatchIcs20Withdrawal is a free log subscription operation binding the contract event 0x0c64e29a5254a71c7f4e52b3d2d236348c80e00a00ba2e1961962bd2827c03fb. +// +// Solidity: event Ics20Withdrawal(address indexed sender, uint256 indexed amount, string destinationChainAddress, string memo) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) WatchIcs20Withdrawal(opts *bind.WatchOpts, sink chan<- *AstriaBridgeableERC20Ics20Withdrawal, sender []common.Address, amount []*big.Int) (event.Subscription, error) { + + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + + logs, sub, err := _AstriaBridgeableERC20.contract.WatchLogs(opts, "Ics20Withdrawal", senderRule, amountRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(AstriaBridgeableERC20Ics20Withdrawal) + if err := _AstriaBridgeableERC20.contract.UnpackLog(event, "Ics20Withdrawal", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseIcs20Withdrawal is a log parse operation binding the contract event 0x0c64e29a5254a71c7f4e52b3d2d236348c80e00a00ba2e1961962bd2827c03fb. +// +// Solidity: event Ics20Withdrawal(address indexed sender, uint256 indexed amount, string destinationChainAddress, string memo) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) ParseIcs20Withdrawal(log types.Log) (*AstriaBridgeableERC20Ics20Withdrawal, error) { + event := new(AstriaBridgeableERC20Ics20Withdrawal) + if err := _AstriaBridgeableERC20.contract.UnpackLog(event, "Ics20Withdrawal", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// AstriaBridgeableERC20MintIterator is returned from FilterMint and is used to iterate over the raw logs and unpacked data for Mint events raised by the AstriaBridgeableERC20 contract. +type AstriaBridgeableERC20MintIterator struct { + Event *AstriaBridgeableERC20Mint // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *AstriaBridgeableERC20MintIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(AstriaBridgeableERC20Mint) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(AstriaBridgeableERC20Mint) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *AstriaBridgeableERC20MintIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *AstriaBridgeableERC20MintIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// AstriaBridgeableERC20Mint represents a Mint event raised by the AstriaBridgeableERC20 contract. +type AstriaBridgeableERC20Mint struct { + Account common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterMint is a free log retrieval operation binding the contract event 0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885. +// +// Solidity: event Mint(address indexed account, uint256 amount) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) FilterMint(opts *bind.FilterOpts, account []common.Address) (*AstriaBridgeableERC20MintIterator, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + + logs, sub, err := _AstriaBridgeableERC20.contract.FilterLogs(opts, "Mint", accountRule) + if err != nil { + return nil, err + } + return &AstriaBridgeableERC20MintIterator{contract: _AstriaBridgeableERC20.contract, event: "Mint", logs: logs, sub: sub}, nil +} + +// WatchMint is a free log subscription operation binding the contract event 0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885. +// +// Solidity: event Mint(address indexed account, uint256 amount) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) WatchMint(opts *bind.WatchOpts, sink chan<- *AstriaBridgeableERC20Mint, account []common.Address) (event.Subscription, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + + logs, sub, err := _AstriaBridgeableERC20.contract.WatchLogs(opts, "Mint", accountRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(AstriaBridgeableERC20Mint) + if err := _AstriaBridgeableERC20.contract.UnpackLog(event, "Mint", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseMint is a log parse operation binding the contract event 0x0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885. +// +// Solidity: event Mint(address indexed account, uint256 amount) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) ParseMint(log types.Log) (*AstriaBridgeableERC20Mint, error) { + event := new(AstriaBridgeableERC20Mint) + if err := _AstriaBridgeableERC20.contract.UnpackLog(event, "Mint", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// AstriaBridgeableERC20SequencerWithdrawalIterator is returned from FilterSequencerWithdrawal and is used to iterate over the raw logs and unpacked data for SequencerWithdrawal events raised by the AstriaBridgeableERC20 contract. +type AstriaBridgeableERC20SequencerWithdrawalIterator struct { + Event *AstriaBridgeableERC20SequencerWithdrawal // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *AstriaBridgeableERC20SequencerWithdrawalIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(AstriaBridgeableERC20SequencerWithdrawal) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(AstriaBridgeableERC20SequencerWithdrawal) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *AstriaBridgeableERC20SequencerWithdrawalIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *AstriaBridgeableERC20SequencerWithdrawalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// AstriaBridgeableERC20SequencerWithdrawal represents a SequencerWithdrawal event raised by the AstriaBridgeableERC20 contract. +type AstriaBridgeableERC20SequencerWithdrawal struct { + Sender common.Address + Amount *big.Int + DestinationChainAddress string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterSequencerWithdrawal is a free log retrieval operation binding the contract event 0x0f4961cab7530804898499aa89f5ec81d1a73102e2e4a1f30f88e5ae3513ba2a. +// +// Solidity: event SequencerWithdrawal(address indexed sender, uint256 indexed amount, string destinationChainAddress) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) FilterSequencerWithdrawal(opts *bind.FilterOpts, sender []common.Address, amount []*big.Int) (*AstriaBridgeableERC20SequencerWithdrawalIterator, error) { + + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + + logs, sub, err := _AstriaBridgeableERC20.contract.FilterLogs(opts, "SequencerWithdrawal", senderRule, amountRule) + if err != nil { + return nil, err + } + return &AstriaBridgeableERC20SequencerWithdrawalIterator{contract: _AstriaBridgeableERC20.contract, event: "SequencerWithdrawal", logs: logs, sub: sub}, nil +} + +// WatchSequencerWithdrawal is a free log subscription operation binding the contract event 0x0f4961cab7530804898499aa89f5ec81d1a73102e2e4a1f30f88e5ae3513ba2a. +// +// Solidity: event SequencerWithdrawal(address indexed sender, uint256 indexed amount, string destinationChainAddress) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) WatchSequencerWithdrawal(opts *bind.WatchOpts, sink chan<- *AstriaBridgeableERC20SequencerWithdrawal, sender []common.Address, amount []*big.Int) (event.Subscription, error) { + + var senderRule []interface{} + for _, senderItem := range sender { + senderRule = append(senderRule, senderItem) + } + var amountRule []interface{} + for _, amountItem := range amount { + amountRule = append(amountRule, amountItem) + } + + logs, sub, err := _AstriaBridgeableERC20.contract.WatchLogs(opts, "SequencerWithdrawal", senderRule, amountRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(AstriaBridgeableERC20SequencerWithdrawal) + if err := _AstriaBridgeableERC20.contract.UnpackLog(event, "SequencerWithdrawal", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseSequencerWithdrawal is a log parse operation binding the contract event 0x0f4961cab7530804898499aa89f5ec81d1a73102e2e4a1f30f88e5ae3513ba2a. +// +// Solidity: event SequencerWithdrawal(address indexed sender, uint256 indexed amount, string destinationChainAddress) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) ParseSequencerWithdrawal(log types.Log) (*AstriaBridgeableERC20SequencerWithdrawal, error) { + event := new(AstriaBridgeableERC20SequencerWithdrawal) + if err := _AstriaBridgeableERC20.contract.UnpackLog(event, "SequencerWithdrawal", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// AstriaBridgeableERC20TransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the AstriaBridgeableERC20 contract. +type AstriaBridgeableERC20TransferIterator struct { + Event *AstriaBridgeableERC20Transfer // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *AstriaBridgeableERC20TransferIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(AstriaBridgeableERC20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(AstriaBridgeableERC20Transfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *AstriaBridgeableERC20TransferIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *AstriaBridgeableERC20TransferIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// AstriaBridgeableERC20Transfer represents a Transfer event raised by the AstriaBridgeableERC20 contract. +type AstriaBridgeableERC20Transfer struct { + From common.Address + To common.Address + Value *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTransfer is a free log retrieval operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*AstriaBridgeableERC20TransferIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AstriaBridgeableERC20.contract.FilterLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return &AstriaBridgeableERC20TransferIterator{contract: _AstriaBridgeableERC20.contract, event: "Transfer", logs: logs, sub: sub}, nil +} + +// WatchTransfer is a free log subscription operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *AstriaBridgeableERC20Transfer, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _AstriaBridgeableERC20.contract.WatchLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(AstriaBridgeableERC20Transfer) + if err := _AstriaBridgeableERC20.contract.UnpackLog(event, "Transfer", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTransfer is a log parse operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. +// +// Solidity: event Transfer(address indexed from, address indexed to, uint256 value) +func (_AstriaBridgeableERC20 *AstriaBridgeableERC20Filterer) ParseTransfer(log types.Log) (*AstriaBridgeableERC20Transfer, error) { + event := new(AstriaBridgeableERC20Transfer) + if err := _AstriaBridgeableERC20.contract.UnpackLog(event, "Transfer", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/contracts/generate-bindings.sh b/contracts/generate-bindings.sh new file mode 100755 index 0000000000..3e4577e519 --- /dev/null +++ b/contracts/generate-bindings.sh @@ -0,0 +1,12 @@ +solc --optimize --optimize-runs=200 \ + --metadata --metadata-literal \ + --base-path "astria-bridge-contracts" \ + --abi "astria-bridge-contracts/src/AstriaBridgeableERC20.sol" \ + -o abi/ --overwrite + +solc --optimize --optimize-runs=200 \ + --base-path "astria-bridge-contracts" \ + --bin "astria-bridge-contracts/src/AstriaBridgeableERC20.sol" \ + -o bin/ --overwrite + +abigen --abi abi/AstriaBridgeableERC20.abi --bin bin/AstriaBridgeableERC20.bin --pkg contracts --type AstriaBridgeableERC20 --out astria_bridgeable_erc20.go diff --git a/core/bench_test.go b/core/bench_test.go index c5991f10e8..7c5d2b303a 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -83,7 +83,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) - gas, _ := IntrinsicGas(data, nil, false, false, false, false) + gas, _ := IntrinsicGas(data, nil, false, false, false, false, false) signer := gen.Signer() gasPrice := big.NewInt(0) if gen.header.BaseFee != nil { diff --git a/core/state_transition.go b/core/state_transition.go index 5b5af15064..679f1fd55d 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -26,6 +26,7 @@ import ( cmath "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" ) @@ -65,7 +66,12 @@ func (result *ExecutionResult) Revert() []byte { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool, isEIP3860 bool) (uint64, error) { +func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool, isEIP3860 bool, isDepositTx bool) (uint64, error) { + if isDepositTx { + // deposit txs are gasless + return 0, nil + } + // Set the starting gas for the raw transaction var gas uint64 if isContractCreation && isHomestead { @@ -274,6 +280,12 @@ func (st *StateTransition) buyGas() error { } func (st *StateTransition) preCheck() error { + if st.msg.IsDepositTx { + // deposit txs do not require checks as they are part of rollup consensus, + // not txs that originate externally. + return nil + } + // Only check transactions that are not fake msg := st.msg if !msg.SkipAccountChecks { @@ -362,9 +374,10 @@ func (st *StateTransition) preCheck() error { // However if any consensus issue encountered, return the error directly with // nil evm execution result. func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { - // if this is a deposit tx, we only need to mint funds and no gas is used. - if st.msg.IsDepositTx { - st.state.AddBalance(st.msg.From, st.msg.Value) + // if this is a native asset deposit tx, we only need to mint funds. + if st.msg.IsDepositTx && len(st.msg.Data) == 0 { + log.Debug("deposit tx minting funds", "to", *st.msg.To, "value", st.msg.Value) + st.state.AddBalance(*st.msg.To, st.msg.Value) return &ExecutionResult{ UsedGas: 0, Err: nil, @@ -372,6 +385,12 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { }, nil } + if st.msg.IsDepositTx { + st.initialGas = st.msg.GasLimit + st.gasRemaining = st.msg.GasLimit + log.Debug("deposit tx minting erc20", "to", *st.msg.To, "value", st.msg.Value) + } + // First check this message satisfies all consensus rules before // applying the message. The rules include these clauses // @@ -402,7 +421,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { ) // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(msg.Data, msg.AccessList, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) + gas, err := IntrinsicGas(msg.Data, msg.AccessList, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai, msg.IsDepositTx) if err != nil { return nil, err } @@ -438,6 +457,17 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, msg.Value) } + // if this is a deposit tx, don't refund gas and also don't pay to the coinbase, + // as no gas was used. + if st.msg.IsDepositTx { + log.Debug("deposit tx executed", "to", *st.msg.To, "value", st.msg.Value, "from", st.msg.From, "gasUsed", st.gasUsed(), "err", vmerr) + return &ExecutionResult{ + UsedGas: st.gasUsed(), + Err: vmerr, + ReturnData: ret, + }, nil + } + if !rules.IsLondon { // Before EIP-3529: refunds were capped to gasUsed / 2 st.refundGas(params.RefundQuotient) diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 6fa14a4cdd..595275a780 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -97,7 +97,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types } // Ensure the transaction has more gas than the bare minimum needed to cover // the transaction metadata - intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time)) + intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time), false) if err != nil { return err } diff --git a/core/types/deposit_tx.go b/core/types/deposit_tx.go index 96c7efe830..85cdf00a75 100644 --- a/core/types/deposit_tx.go +++ b/core/types/deposit_tx.go @@ -11,37 +11,53 @@ import ( var _ TxData = &DepositTx{} type DepositTx struct { - // the address of the account that initiated the deposit + // the bridge sender address set in the genesis file + // ie. the minter or the caller of the ERC20 contract From common.Address - // value to be minted to `From` + // value to be minted to the recipient, if this is a native asset mint Value *big.Int // gas limit Gas uint64 + // if this is a native asset mint, this is set to the mint recipient + // if this is an ERC20 mint, this is set to the ERC20 contract address + To *common.Address + // if this is an ERC20 mint, the following field is set + // to the `mint` function calldata. + Data []byte } func (tx *DepositTx) copy() TxData { + to := new(common.Address) + if tx.To != nil { + *to = *tx.To + } + cpy := &DepositTx{ From: tx.From, Value: new(big.Int), Gas: tx.Gas, + To: to, + Data: make([]byte, len(tx.Data)), } + if tx.Value != nil { cpy.Value.Set(tx.Value) } + copy(cpy.Data, tx.Data) return cpy } func (tx *DepositTx) txType() byte { return DepositTxType } func (tx *DepositTx) chainID() *big.Int { return common.Big0 } func (tx *DepositTx) accessList() AccessList { return nil } -func (tx *DepositTx) data() []byte { return nil } +func (tx *DepositTx) data() []byte { return tx.Data } func (tx *DepositTx) gas() uint64 { return tx.Gas } func (tx *DepositTx) gasFeeCap() *big.Int { return new(big.Int) } func (tx *DepositTx) gasTipCap() *big.Int { return new(big.Int) } func (tx *DepositTx) gasPrice() *big.Int { return new(big.Int) } func (tx *DepositTx) value() *big.Int { return tx.Value } func (tx *DepositTx) nonce() uint64 { return 0 } -func (tx *DepositTx) to() *common.Address { return nil } +func (tx *DepositTx) to() *common.Address { return tx.To } func (tx *DepositTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { return dst.Set(new(big.Int)) diff --git a/genesis.json b/genesis.json index 4298c0d6d2..9199771dab 100644 --- a/genesis.json +++ b/genesis.json @@ -26,8 +26,19 @@ "startHeight": 1, "assetDenom": "nria", "assetPrecision": 9 + }, + { + "bridgeAddress": "34fec43c7fcab9aef3b3cf8aba855e41ee69ca3a", + "startHeight": 1, + "assetDenom": "transfer/channel-1/usdc", + "assetPrecision": 6, + "erc20asset": { + "contractAddress":"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "contractPrecision": 6 + } } ], + "astriaBridgeSenderAddress": "0x0000000000000000000000000000000000000000", "astriaFeeCollectors": { "1": "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30" }, diff --git a/grpc/execution/server.go b/grpc/execution/server.go index cf49270048..e09ee13194 100644 --- a/grpc/execution/server.go +++ b/grpc/execution/server.go @@ -48,7 +48,9 @@ type ExecutionServiceServerV1Alpha2 struct { bridgeAddresses map[string]*params.AstriaBridgeAddressConfig // astria bridge addess to config for that bridge account bridgeAllowedAssetIDs map[[32]byte]struct{} // a set of allowed asset IDs structs are left empty - nextFeeRecipient common.Address // Fee recipient for the next block + bridgeSenderAddress common.Address // address from which AstriaBridgeableERC20 contracts are called + + nextFeeRecipient common.Address // Fee recipient for the next block } var ( @@ -104,16 +106,25 @@ func NewExecutionServiceServerV1Alpha2(eth *eth.Ethereum) (*ExecutionServiceServ return nil, fmt.Errorf("invalid bridge address config: %w", err) } - if cfg.Erc20Asset != nil && nativeBridgeSeen { - return nil, errors.New("only one native bridge address is allowed") - } - if cfg.Erc20Asset != nil && !nativeBridgeSeen { + if cfg.Erc20Asset == nil { + if nativeBridgeSeen { + return nil, errors.New("only one native bridge address is allowed") + } nativeBridgeSeen = true } + if cfg.Erc20Asset != nil && bc.Config().AstriaBridgeSenderAddress == (common.Address{}) { + return nil, errors.New("astria bridge sender address must be set for bridged ERC20 assets") + } + bridgeAddresses[string(cfg.BridgeAddress)] = &cfg assetID := sha256.Sum256([]byte(cfg.AssetDenom)) bridgeAllowedAssetIDs[assetID] = struct{}{} + if cfg.Erc20Asset == nil { + log.Info("bridge for sequencer native asset initialized", "bridgeAddress", cfg.BridgeAddress, "assetDenom", cfg.AssetDenom) + } else { + log.Info("bridge for ERC20 asset initialized", "bridgeAddress", cfg.BridgeAddress, "assetDenom", cfg.AssetDenom, "contractAddress", cfg.Erc20Asset.ContractAddress) + } } } @@ -142,6 +153,7 @@ func NewExecutionServiceServerV1Alpha2(eth *eth.Ethereum) (*ExecutionServiceServ bc: bc, bridgeAddresses: bridgeAddresses, bridgeAllowedAssetIDs: bridgeAllowedAssetIDs, + bridgeSenderAddress: bc.Config().AstriaBridgeSenderAddress, nextFeeRecipient: nextFeeRecipient, }, nil } @@ -237,9 +249,12 @@ func (s *ExecutionServiceServerV1Alpha2) ExecuteBlock(ctx context.Context, req * return nil, status.Error(codes.FailedPrecondition, "Block can only be created on top of soft block.") } + // the height that this block will be at + height := s.bc.CurrentBlock().Number.Uint64() + 1 + txsToProcess := types.Transactions{} for _, tx := range req.Transactions { - unmarshalledTx, err := validateAndUnmarshalSequencerTx(tx, s.bridgeAddresses, s.bridgeAllowedAssetIDs) + unmarshalledTx, err := validateAndUnmarshalSequencerTx(height, tx, s.bridgeAddresses, s.bridgeAllowedAssetIDs, s.bridgeSenderAddress) if err != nil { log.Debug("failed to validate sequencer tx, ignoring", "tx", tx, "err", err) continue diff --git a/grpc/execution/validation.go b/grpc/execution/validation.go index ddfb9e2552..70bd091810 100644 --- a/grpc/execution/validation.go +++ b/grpc/execution/validation.go @@ -5,15 +5,24 @@ import ( "crypto/sha256" "fmt" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "math/big" ) // `validateAndUnmarshalSequencerTx` validates and unmarshals the given rollup sequencer transaction. // If the sequencer transaction is a deposit tx, we ensure that the asset ID is allowed and the bridge address is known. // If the sequencer transaction is not a deposit tx, we unmarshal the sequenced data into an Ethereum transaction. We ensure that the // tx is not a blob tx or a deposit tx. -func validateAndUnmarshalSequencerTx(tx *sequencerblockv1alpha1.RollupData, bridgeAddresses map[string]*params.AstriaBridgeAddressConfig, bridgeAllowedAssetIDs map[[32]byte]struct{}) (*types.Transaction, error) { +func validateAndUnmarshalSequencerTx( + height uint64, + tx *sequencerblockv1alpha1.RollupData, + bridgeAddresses map[string]*params.AstriaBridgeAddressConfig, + bridgeAllowedAssetIDs map[[32]byte]struct{}, + bridgeSenderAddress common.Address, +) (*types.Transaction, error) { if deposit := tx.GetDeposit(); deposit != nil { bridgeAddress := string(deposit.BridgeAddress.GetInner()) bac, ok := bridgeAddresses[bridgeAddress] @@ -21,25 +30,67 @@ func validateAndUnmarshalSequencerTx(tx *sequencerblockv1alpha1.RollupData, brid return nil, fmt.Errorf("unknown bridge address: %s", bridgeAddress) } + if height < uint64(bac.StartHeight) { + return nil, fmt.Errorf("bridging asset %s from bridge %s not allowed before height %d", bac.AssetDenom, bridgeAddress, bac.StartHeight) + } + if len(deposit.AssetId) != 32 { return nil, fmt.Errorf("invalid asset ID: %x", deposit.AssetId) } + assetID := [32]byte{} copy(assetID[:], deposit.AssetId[:32]) + if _, ok := bridgeAllowedAssetIDs[assetID]; !ok { - return nil, fmt.Errorf("disallowed asset ID: %x", deposit.AssetId) + return nil, fmt.Errorf("disallowed asset ID %x in deposit tx", deposit.AssetId) + } + + expectedAssetID := sha256.Sum256([]byte(bac.AssetDenom)) + if assetID != expectedAssetID { + return nil, fmt.Errorf("asset ID %x does not match bridge address %s asset", deposit.AssetId, bridgeAddress) + } + + recipient := common.HexToAddress(deposit.DestinationChainAddress) + amount := bac.ScaledDepositAmount(protoU128ToBigInt(deposit.Amount)) + + if bac.Erc20Asset != nil { + log.Debug("creating deposit tx to mint ERC20 asset", "token", bac.AssetDenom, "erc20Address", bac.Erc20Asset.ContractAddress) + abi, err := contracts.AstriaBridgeableERC20MetaData.GetAbi() + if err != nil { + // this should never happen, as the abi is hardcoded in the contract bindings + return nil, fmt.Errorf("failed to get abi for erc20 contract for asset %s: %w", bac.AssetDenom, err) + } + + // pack arguments for calling the `mint` function on the ERC20 contract + args := []interface{}{recipient, amount} + calldata, err := abi.Pack("mint", args...) + if err != nil { + return nil, err + } + + txdata := types.DepositTx{ + From: bridgeSenderAddress, + Value: new(big.Int), // don't need to set this, as we aren't minting the native asset + // mints cost ~14k gas, however this can vary based on existing storage, so we add a little extra as buffer. + // + // the fees are spent from the "bridge account" which is not actually a real account, but is instead some + // address defined by consensus, so the gas cost is not actually deducted from any account. + Gas: 16000, + To: &bac.Erc20Asset.ContractAddress, + Data: calldata, + } + + tx := types.NewTx(&txdata) + return tx, nil } - amount := protoU128ToBigInt(deposit.Amount) - address := common.HexToAddress(deposit.DestinationChainAddress) txdata := types.DepositTx{ - From: address, - Value: bac.ScaledDepositAmount(amount), + From: bridgeSenderAddress, + To: &recipient, + Value: amount, Gas: 0, } - - tx := types.NewTx(&txdata) - return tx, nil + return types.NewTx(&txdata), nil } else { ethTx := new(types.Transaction) err := ethTx.UnmarshalBinary(tx.GetSequencedData()) diff --git a/grpc/execution/validation_test.go b/grpc/execution/validation_test.go index c9e247d8be..47295419b2 100644 --- a/grpc/execution/validation_test.go +++ b/grpc/execution/validation_test.go @@ -55,6 +55,14 @@ func TestSequenceTxValidation(t *testing.T) { bridgeAssetDenom := sha256.Sum256([]byte(ethservice.BlockChain().Config().AstriaBridgeAddressConfigs[0].AssetDenom)) invalidBridgeAssetDenom := sha256.Sum256([]byte("invalid-asset-denom")) + invalidHeightBridgeAssetDenom := "invalid-height-asset-denom" + invalidHeightBridgeAssetDenomID := sha256.Sum256([]byte(invalidHeightBridgeAssetDenom)) + invalidHeightBridgeAddress := "invalid-height-bridge-address" + serviceV1Alpha1.bridgeAddresses[invalidHeightBridgeAddress] = ¶ms.AstriaBridgeAddressConfig{ + AssetDenom: invalidHeightBridgeAssetDenom, + StartHeight: 100, + } + bridgeAddress := ethservice.BlockChain().Config().AstriaBridgeAddressConfigs[0].BridgeAddress tests := []struct { @@ -129,6 +137,19 @@ func TestSequenceTxValidation(t *testing.T) { }}}, wantErr: "disallowed asset ID", }, + { + description: "deposit tx with a height and asset below the bridge start height", + sequencerTx: &sequencerblockv1alpha1.RollupData{Value: &sequencerblockv1alpha1.RollupData_Deposit{Deposit: &sequencerblockv1alpha1.Deposit{ + BridgeAddress: &primitivev1.Address{ + Inner: []byte(invalidHeightBridgeAddress), + }, + AssetId: invalidHeightBridgeAssetDenomID[:], + Amount: bigIntToProtoU128(big.NewInt(1000000000000000000)), + RollupId: &primitivev1.RollupId{Inner: make([]byte, 0)}, + DestinationChainAddress: chainDestinationAddress.String(), + }}}, + wantErr: "not allowed before height", + }, { description: "valid deposit tx", sequencerTx: &sequencerblockv1alpha1.RollupData{Value: &sequencerblockv1alpha1.RollupData_Deposit{Deposit: &sequencerblockv1alpha1.Deposit{ @@ -153,7 +174,7 @@ func TestSequenceTxValidation(t *testing.T) { for _, test := range tests { t.Run(test.description, func(t *testing.T) { - _, err := validateAndUnmarshalSequencerTx(test.sequencerTx, serviceV1Alpha1.bridgeAddresses, serviceV1Alpha1.bridgeAllowedAssetIDs) + _, err := validateAndUnmarshalSequencerTx(2, test.sequencerTx, serviceV1Alpha1.bridgeAddresses, serviceV1Alpha1.bridgeAllowedAssetIDs, common.Address{}) if test.wantErr == "" && err == nil { return } diff --git a/light/txpool.go b/light/txpool.go index b792d70b14..a8b5225565 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -386,7 +386,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error } // Should supply enough intrinsic gas - gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai) + gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai, false) if err != nil { return err } diff --git a/params/astria_config_test.go b/params/astria_config_test.go index 5d94c0c623..2a8625c47e 100644 --- a/params/astria_config_test.go +++ b/params/astria_config_test.go @@ -168,25 +168,25 @@ func TestAstriaBridgeConfigValidation(t *testing.T) { AssetDenom: "nria", AssetPrecision: 22, Erc20Asset: &AstriaErc20AssetConfig{ - Erc20Address: erc20Asset, + ContractAddress: erc20Asset, ContractPrecision: 18, }, }, wantErr: fmt.Errorf("asset precision must be less than or equal to contract precision"), }, { - description: "erc20 assets not supported", + description: "erc20 assets supported", config: AstriaBridgeAddressConfig{ BridgeAddress: bridgeAddress.Bytes(), StartHeight: 2, AssetDenom: "nria", AssetPrecision: 18, Erc20Asset: &AstriaErc20AssetConfig{ - Erc20Address: erc20Asset, + ContractAddress: erc20Asset, ContractPrecision: 18, }, }, - wantErr: fmt.Errorf("cannot currently process erc20 bridged assets"), + wantErr: nil, }, { description: "valid config", diff --git a/params/config.go b/params/config.go index 09cb045e9a..781c171d97 100644 --- a/params/config.go +++ b/params/config.go @@ -346,6 +346,7 @@ type ChainConfig struct { AstriaCelestiaInitialHeight uint64 `json:"astriaCelestiaInitialHeight"` AstriaCelestiaHeightVariance uint64 `json:"astriaCelestiaHeightVariance,omitempty"` AstriaBridgeAddressConfigs []AstriaBridgeAddressConfig `json:"astriaBridgeAddresses,omitempty"` + AstriaBridgeSenderAddress common.Address `json:"astriaBridgeSenderAddress,omitempty"` AstriaFeeCollectors map[uint32]common.Address `json:"astriaFeeCollectors"` AstriaEIP1559Params *AstriaEIP1559Params `json:"astriaEIP1559Params,omitempty"` } @@ -994,7 +995,7 @@ type AstriaBridgeAddressConfig struct { } type AstriaErc20AssetConfig struct { - Erc20Address common.Address `json:"erc20Address"` + ContractAddress common.Address `json:"contractAddress"` ContractPrecision uint16 `json:"contractPrecision"` } @@ -1014,10 +1015,6 @@ func (abc *AstriaBridgeAddressConfig) Validate() error { if abc.Erc20Asset != nil && abc.AssetPrecision > abc.Erc20Asset.ContractPrecision { return fmt.Errorf("asset precision must be less than or equal to contract precision") } - // TODO: support erc20 bridged assets - if abc.Erc20Asset != nil { - return fmt.Errorf("cannot currently process erc20 bridged assets") - } return nil } diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index 391aa57584..d9ffa37028 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -55,7 +55,7 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error { return nil, nil, err } // Intrinsic gas - requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul, false) + requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul, false, false) if err != nil { return nil, nil, err }