diff --git a/Cargo.lock b/Cargo.lock index 46a2ffe3f..f9daeaf27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5626,7 +5626,9 @@ dependencies = [ "ibc-eureka-relayer-eth-to-cosmos 0.1.0", "ibc-eureka-relayer-eth-to-cosmos-compat", "ibc-eureka-relayer-eth-to-eth", + "ibc-eureka-relayer-eth-to-solana", "ibc-eureka-relayer-solana-to-cosmos", + "ibc-eureka-relayer-solana-to-eth", "opentelemetry", "opentelemetry-appender-tracing", "opentelemetry-otlp", @@ -5844,6 +5846,36 @@ dependencies = [ "tracing", ] +[[package]] +name = "ibc-eureka-relayer-eth-to-solana" +version = "0.1.0" +dependencies = [ + "alloy", + "anchor-lang", + "anyhow", + "bincode", + "borsh 1.6.0", + "ibc-eureka-relayer-core 0.1.0", + "ibc-eureka-relayer-lib 0.1.0", + "ibc-eureka-solidity-types 0.1.0", + "ibc-eureka-utils 0.1.0", + "ibc-proto 0.52.0", + "prost", + "serde", + "serde_json", + "sha2 0.10.9", + "solana-client", + "solana-ibc-constants", + "solana-ibc-proto", + "solana-ibc-types", + "solana-sdk", + "solana-transaction-status", + "spl-associated-token-account", + "spl-token", + "tonic 0.13.1", + "tracing", +] + [[package]] name = "ibc-eureka-relayer-lib" version = "0.1.0" @@ -5875,6 +5907,7 @@ dependencies = [ "sha2 0.10.9", "solana-client", "solana-ibc-constants", + "solana-ibc-proto", "solana-ibc-types", "solana-sdk", "solana-transaction-status", @@ -5950,6 +5983,21 @@ dependencies = [ "tracing", ] +[[package]] +name = "ibc-eureka-relayer-solana-to-eth" +version = "0.1.0" +dependencies = [ + "alloy", + "anyhow", + "ibc-eureka-relayer-core 0.1.0", + "ibc-eureka-relayer-lib 0.1.0", + "serde", + "serde_json", + "solana-sdk", + "tonic 0.13.1", + "tracing", +] + [[package]] name = "ibc-eureka-solidity-types" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 34cdb98a2..15ff2a23f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,8 @@ members = [ "packages/relayer/modules/eth-to-cosmos-compat", "packages/relayer/modules/solana-to-cosmos", "packages/relayer/modules/cosmos-to-solana", + "packages/relayer/modules/eth-to-solana", + "packages/relayer/modules/solana-to-eth", "packages/sp1-ics07-tendermint-prover", # Programs (binaries) @@ -55,6 +57,8 @@ ibc-eureka-relayer-eth-to-eth = { path = "packages/relayer/modules/eth ibc-eureka-relayer-cosmos-to-cosmos = { path = "packages/relayer/modules/cosmos-to-cosmos", default-features = false } ibc-eureka-relayer-solana-to-cosmos = { path = "packages/relayer/modules/solana-to-cosmos", default-features = false } ibc-eureka-relayer-cosmos-to-solana = { path = "packages/relayer/modules/cosmos-to-solana", default-features = false } +ibc-eureka-relayer-eth-to-solana = { path = "packages/relayer/modules/eth-to-solana", default-features = false } +ibc-eureka-relayer-solana-to-eth = { path = "packages/relayer/modules/solana-to-eth", default-features = false } ibc-eureka-utils = { path = "packages/utils", default-features = false } ibc-eureka-constrained-types = { path = "packages/constrained_types", default-features = false } sp1-ics07-tendermint-prover = { path = "packages/sp1-ics07-tendermint-prover", default-features = false } diff --git a/abi/bytecode/AttestationLightClient.json b/abi/bytecode/AttestationLightClient.json index d389f9d70..a0367f1de 100644 --- a/abi/bytecode/AttestationLightClient.json +++ b/abi/bytecode/AttestationLightClient.json @@ -1 +1 @@ -{"abi":[{"type":"constructor","inputs":[{"name":"attestorAddresses","type":"address[]","internalType":"address[]"},{"name":"minRequiredSigs","type":"uint8","internalType":"uint8"},{"name":"initialHeight","type":"uint64","internalType":"uint64"},{"name":"initialTimestampSeconds","type":"uint64","internalType":"uint64"},{"name":"roleManager","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"DEFAULT_ADMIN_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"PROOF_SUBMITTER_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"getAttestationSet","inputs":[],"outputs":[{"name":"attestorAddresses","type":"address[]","internalType":"address[]"},{"name":"minRequiredSigs","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"type":"function","name":"getClientState","inputs":[],"outputs":[{"name":"","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"getConsensusTimestamp","inputs":[{"name":"revisionHeight","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"getRoleAdmin","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"grantRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"hasRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"misbehaviour","inputs":[{"name":"","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"renounceRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"callerConfirmation","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"revokeRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"updateClient","inputs":[{"name":"updateMsg","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"uint8","internalType":"enum ILightClientMsgs.UpdateResult"}],"stateMutability":"nonpayable"},{"type":"function","name":"verifyMembership","inputs":[{"name":"msg_","type":"tuple","internalType":"struct ILightClientMsgs.MsgVerifyMembership","components":[{"name":"proof","type":"bytes","internalType":"bytes"},{"name":"proofHeight","type":"tuple","internalType":"struct IICS02ClientMsgs.Height","components":[{"name":"revisionNumber","type":"uint64","internalType":"uint64"},{"name":"revisionHeight","type":"uint64","internalType":"uint64"}]},{"name":"path","type":"bytes[]","internalType":"bytes[]"},{"name":"value","type":"bytes","internalType":"bytes"}]}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"verifyNonMembership","inputs":[{"name":"msg_","type":"tuple","internalType":"struct ILightClientMsgs.MsgVerifyNonMembership","components":[{"name":"proof","type":"bytes","internalType":"bytes"},{"name":"proofHeight","type":"tuple","internalType":"struct IICS02ClientMsgs.Height","components":[{"name":"revisionNumber","type":"uint64","internalType":"uint64"},{"name":"revisionHeight","type":"uint64","internalType":"uint64"}]},{"name":"path","type":"bytes[]","internalType":"bytes[]"}]}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"event","name":"RoleAdminChanged","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"previousAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"newAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"AccessControlBadConfirmation","inputs":[]},{"type":"error","name":"AccessControlUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"neededRole","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"BadQuorum","inputs":[{"name":"minRequired","type":"uint8","internalType":"uint8"},{"name":"attestationCount","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"ConsensusTimestampNotFound","inputs":[{"name":"height","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"DuplicateSigner","inputs":[{"name":"signer","type":"address","internalType":"address"}]},{"type":"error","name":"ECDSAInvalidSignature","inputs":[]},{"type":"error","name":"ECDSAInvalidSignatureLength","inputs":[{"name":"length","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"ECDSAInvalidSignatureS","inputs":[{"name":"s","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"EmptyPackets","inputs":[]},{"type":"error","name":"EmptySignatures","inputs":[]},{"type":"error","name":"EmptyValue","inputs":[]},{"type":"error","name":"FeatureNotSupported","inputs":[]},{"type":"error","name":"FrozenClientState","inputs":[]},{"type":"error","name":"HeightMismatch","inputs":[{"name":"expected","type":"uint64","internalType":"uint64"},{"name":"provided","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"InvalidPathLength","inputs":[{"name":"expectedLength","type":"uint256","internalType":"uint256"},{"name":"providedLength","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"InvalidSignatureLength","inputs":[{"name":"signature","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"InvalidState","inputs":[{"name":"height","type":"uint64","internalType":"uint64"},{"name":"timestamp","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"NoAttestors","inputs":[]},{"type":"error","name":"NotMember","inputs":[]},{"type":"error","name":"NotNonMember","inputs":[]},{"type":"error","name":"SignatureInvalid","inputs":[{"name":"signature","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"ThresholdNotMet","inputs":[{"name":"validSigners","type":"uint256","internalType":"uint256"},{"name":"minRequired","type":"uint8","internalType":"uint8"}]},{"type":"error","name":"UnknownSigner","inputs":[{"name":"signer","type":"address","internalType":"address"}]}],"bytecode":{"object":"0x60806040523461032d57611f568038038061001981610349565b928339810160a08282031261032d5781516001600160401b03811161032d57820181601f8201121561032d578051916001600160401b0383116102c8578260051b916020610068818501610349565b8095815201906020829482010192831161032d57602001905b8282106103315750505060208301519260ff841680940361032d576100a860408201610382565b6100c060806100b960608501610382565b930161036e565b9284511561031e57851515806102f2575b855190156102dc575060405195608087016001600160401b038111888210176102c857604052858752602087019081526060604088019360018060401b03169788855201915f835286519060018060401b0382116102c8576801000000000000000082116102c85760015482600155808310610284575b5060015f5260205f205f5b838110610267575050505060ff90511669ff00000000000000000060025493610100600160481b03905160081b169251151560481b169260018060501b0319161717176002555f5b83518110156101fb5760018060a01b0360208260051b8601015116805f52600360205260ff60405f2054166101e957906001915f52600360205260405f208260ff198254161790550161019b565b637010e27960e11b5f5260045260245ffd5b505f84815260046020526040902080546001600160401b0319166001600160401b039092169190911790556001600160a01b03811661024e575061023d61048c565b505b6040516119a7908161050f8239f35b8061025b61026192610396565b5061040c565b5061023f565b82516001600160a01b031681830155602090920191600101610153565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf69081019083015b8181106102bd5750610148565b5f81556001016102b0565b634e487b7160e01b5f52604160045260245ffd5b866365846b7f60e01b5f5260045260245260445ffd5b5084515f19870160ff811161030a5760ff16106100d1565b634e487b7160e01b5f52601160045260245ffd5b6343d0a89360e11b5f5260045ffd5b5f80fd5b6020809161033e8461036e565b815201910190610081565b6040519190601f01601f191682016001600160401b038111838210176102c857604052565b51906001600160a01b038216820361032d57565b51906001600160401b038216820361032d57565b6001600160a01b0381165f9081525f516020611f365f395f51905f52602052604090205460ff16610407576001600160a01b03165f8181525f516020611f365f395f51905f5260205260408120805460ff191660011790553391905f516020611eb65f395f51905f528180a4600190565b505f90565b6001600160a01b0381165f9081525f516020611ed65f395f51905f52602052604090205460ff16610407576001600160a01b03165f8181525f516020611ed65f395f51905f5260205260408120805460ff191660011790553391905f516020611f165f395f51905f52905f516020611eb65f395f51905f529080a4600190565b5f80525f516020611ed65f395f51905f526020525f516020611ef65f395f51905f525460ff1661050a575f8080525f516020611ed65f395f51905f526020525f516020611ef65f395f51905f52805460ff1916600117905533905f516020611f165f395f51905f525f516020611eb65f395f51905f528280a4600190565b5f9056fe6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146106ab575080630bece356146105e8578063248a9ca3146105be5780632f2ff15d1461058f57806336568abe146105335780634d6d9ffb146104865780635832611b146104405780635972185a14610406578063682ed5f01461035157806391d1485414610308578063a217fddf146102ee578063a845a7f314610261578063d547741f1461022b578063ddba65371461015f5763ef913a4b146100c1575f80fd5b3461015b575f60031936011261015b576101576040516020808201526080604082015261014b816100f460c08201611228565b60ff600254818116606085015267ffffffffffffffff8160081c16608085015260481c16151560a0830152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810183528261085e565b604051918291826107cd565b0390f35b5f80fd5b3461015b5761016d36610749565b505060ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475460ff16156101f6575b7fda81d7c2000000000000000000000000000000000000000000000000000000005f5260045ffd5b6101fe611277565b6101ce565b7f928b1233000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461015b5761025f61023c3661079a565b9061025a610255825f525f602052600160405f20015490565b6112ff565b61175e565b005b3461015b575f60031936011261015b5760ff6002541660405161028e8161028781611228565b038261085e565b60405190604082019260408352815180945260206060840192015f945b8086106102c057505082935060208301520390f35b909260208060019273ffffffffffffffffffffffffffffffffffffffff8751168152019401950194906102ab565b3461015b575f60031936011261015b5760206040515f8152f35b3461015b576103163661079a565b905f525f60205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060ff60405f2054166040519015158152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811161015b5760a0600319823603011261015b5760ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475490916103f19160ff16156103f9575b6004016110cb565b604051908152f35b610401611277565b6103e9565b3461015b575f60031936011261015b5760206040517fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b8152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811680910361015b575f526004602052602067ffffffffffffffff60405f205416604051908152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811161015b576080600319823603011261015b5760ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475490916103f19160ff1615610526575b600401610ef5565b61052e611277565b61051e565b3461015b576105413661079a565b3373ffffffffffffffffffffffffffffffffffffffff8216036105675761025f9161175e565b7f6697b232000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461015b5761025f6105a03661079a565b906105b9610255825f525f602052600160405f20015490565b61168c565b3461015b57602060031936011261015b5760206103f16004355f525f602052600160405f20015490565b3461015b576105f636610749565b60ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475461065e929060ff161561069e57610a3c565b6040516003821015610671576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6106a6611277565b610a3c565b3461015b57602060031936011261015b57600435907fffffffff00000000000000000000000000000000000000000000000000000000821680920361015b57817f7965db0b000000000000000000000000000000000000000000000000000000006020931490811561071f575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483610718565b90602060031983011261015b5760043567ffffffffffffffff811161015b578260238201121561015b5780600401359267ffffffffffffffff841161015b576024848301011161015b576024019190565b600319604091011261015b576004359060243573ffffffffffffffffffffffffffffffffffffffff8116810361015b5790565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602060409481855280519182918282880152018686015e5f8582860101520116010190565b6040810190811067ffffffffffffffff82111761083157604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761083157604052565b92919267ffffffffffffffff821161083157604051916108e760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461085e565b82948184528183011161015b578281602093845f960137010152565b9080601f8301121561015b5781602061091e9335910161089f565b90565b67ffffffffffffffff81116108315760051b60200190565b60208183031261015b5780359067ffffffffffffffff821161015b57019060408282031261015b576040519161096e83610815565b803567ffffffffffffffff811161015b578261098b918301610903565b835260208101359067ffffffffffffffff821161015b57019080601f8301121561015b5781356109ba81610921565b926109c8604051948561085e565b81845260208085019260051b8201019183831161015b5760208201905b8382106109f9575050505050602082015290565b813567ffffffffffffffff811161015b57602091610a1c87848094880101610903565b8152019101906109e5565b519067ffffffffffffffff8216820361015b57565b610a4891810190610939565b60205f818351604051918183925191829101835e8101838152039060025afa15610c915760205f8051604051838101917f01000000000000000000000000000000000000000000000000000000000000008352602182015260218152610aaf60418261085e565b604051918291518091835e8101838152039060025afa15610c9157610ada5f516020830151906113eb565b5160408180518101031261015b57604051610af481610815565b610b0f6040610b0560208501610a27565b9384845201610a27565b67ffffffffffffffff60208301938285521680151580610c7e575b15610c4457505067ffffffffffffffff8151165f52600460205267ffffffffffffffff60405f20541680610bf3575067ffffffffffffffff9051918183169260025490838260081c168511610bb6575b50505116905f52600460205260405f20907fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008254161790555f90565b68ffffffffffffffff007fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000ff9160081b169116176002555f80610b7a565b9167ffffffffffffffff9150511603610c0b57600290565b69010000000000000000007fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff6002541617600255600190565b67ffffffffffffffff92507fdcbc5460000000000000000000000000000000000000000000000000000000005f526004521660245260445ffd5b5067ffffffffffffffff82161515610b2a565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561015b570180359067ffffffffffffffff821161015b57602001918160051b3603831361015b57565b3567ffffffffffffffff8116810361015b5790565b15610d0d5750565b67ffffffffffffffff907ff7caaa5c000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561015b570180359067ffffffffffffffff821161015b5760200191813603831361015b57565b60208183031261015b5780519067ffffffffffffffff821161015b57019060408282031261015b5760405191610dc983610815565b610dd281610a27565b835260208101519067ffffffffffffffff821161015b570181601f8201121561015b57805190610e0182610921565b92610e0f604051948561085e565b82845260208085019360061b8301019181831161015b57602001925b828410610e3e5750505050602082015290565b60408483031261015b5760206040918251610e5881610815565b865181528287015183820152815201930192610e2b565b15610e78575050565b9067ffffffffffffffff80927fc7441689000000000000000000000000000000000000000000000000000000005f52166004521660245260445ffd5b8051821015610ec85760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b6060810190610f048282610c9c565b90506001610f128484610c9c565b9290500361109b5750610fb8610f2a60408301610cf0565b9267ffffffffffffffff841692835f526004602052610fb367ffffffffffffffff60405f20541694610f5e87871515610d05565b610f9e610f76610f6e8580610d43565b810190610939565b610f8e610f838251611365565b6020830151906113eb565b5160208082518301019101610d94565b9667ffffffffffffffff885116918214610e6f565b610c9c565b15610ec857610fd3610fcc82602093610d43565b369161089f565b818151910120920180515115611073575f5b8151805182101561104b57610ffb828692610eb4565b51511461100a57600101610fe5565b60209293506110199151610eb4565b5101516110235790565b7f9a960b4f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f291fc442000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f1e6c84da000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f88b3170e000000000000000000000000000000000000000000000000000000005f52600160045260245260445ffd5b608081016110d98183610d43565b9050156112005760608201906110ef8284610c9c565b905060016110fd8486610c9c565b9290500361109b575061111260408401610cf0565b9061117467ffffffffffffffff831693845f52600460205261116e67ffffffffffffffff60405f2054169561114986881515610d05565b611159610f76610f6e8a80610d43565b9567ffffffffffffffff875116918214610e6f565b85610c9c565b15610ec85760209161118c610fcc8361119894610d43565b83815191012095610d43565b908092918101031261015b576020903591019081515115611073575f5b8251805182101561104b576111cb828792610eb4565b515114806111e8575b6111e0576001016111b5565b505050905090565b508160206111f7838651610eb4565b510151146111d4565b7f1208b21b000000000000000000000000000000000000000000000000000000005f5260045ffd5b602060015491828152019060015f5260205f20905f5b81811061124b5750505090565b825473ffffffffffffffffffffffffffffffffffffffff1684526020909301926001928301920161123e565b335f9081527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e0602052604090205460ff16156112af57565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004527fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b60245260445ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260ff60405f205416156113365750565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b5f60208092604051918183925191829101835e8101838152039060025afa15610c915760205f8051604051838101917f020000000000000000000000000000000000000000000000000000000000000083526021820152602181526113cb60418261085e565b604051918291518091835e8101838152039060025afa15610c91575f5190565b91909182511561166457825160ff60025416907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019060ff82116116375760ff8651921610156116095750508251927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061147e61146886610921565b95611476604051978861085e565b808752610921565b013660208601375f5b81518110156116025761149a8183610eb4565b5160418151036115cc5773ffffffffffffffffffffffffffffffffffffffff6114cf6114c68387611826565b90929192611860565b169081156115925750805f52600360205260ff60405f20541615611567575f5b82811061150c5750906001916115058288610eb4565b5201611487565b8173ffffffffffffffffffffffffffffffffffffffff61152c838a610eb4565b51161461153b576001016114ef565b507fe021c4f2000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7fd7e8a2c2000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6115c8906040519182917f9e3c1b3d000000000000000000000000000000000000000000000000000000008352600483016107cd565b0390fd5b6115c8906040519182917f2ee17a3d000000000000000000000000000000000000000000000000000000008352600483016107cd565b5050509050565b7f378b0805000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f301b38b6000000000000000000000000000000000000000000000000000000005f5260045ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f205416155f1461175857805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f2054165f1461175857805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b81519190604183036118565761184f9250602082015190606060408401519301515f1a9061190b565b9192909190565b50505f9160029190565b60048110156106715780611872575050565b600181036118a2577ff645eedf000000000000000000000000000000000000000000000000000000005f5260045ffd5b600281036118d657507ffce698f7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6003146118e05750565b7fd78bce0c000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161198f579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa15610c91575f5173ffffffffffffffffffffffffffffffffffffffff81161561198557905f905f90565b505f906001905f90565b5050505f916003919056fea164736f6c634300081c000a2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac47bd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10bad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5","sourceMap":"815:11749:29:-:0;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1;;;;;815:11749:29;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;815:11749:29;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;2648:28;815:11749;;2723:19;;;:69;;;815:11749;;;;;;;-1:-1:-1;815:11749:29;;;;;;-1:-1:-1;;;;;815:11749:29;;;;;;;;;;;;;;2893:217;;815:11749;;;;;2893:217;;815:11749;;;;;;;;;;;2893:217;815:11749;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;-1:-1:-1;815:11749:29;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;3171:3:29;815:11749;;3141:28;;;;;815:11749;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;3251:11;815:11749;;;;-1:-1:-1;815:11749:29;;;;;;;;-1:-1:-1;815:11749:29;3251:11;815:11749;;;-1:-1:-1;815:11749:29;;;;;;;;;;;3126:13;;815:11749;;;;-1:-1:-1;815:11749:29;;;;-1:-1:-1;815:11749:29;3141:28;-1:-1:-1;;815:11749:29;;;3372:27;815:11749;;;;;;;-1:-1:-1;;;;;;815:11749:29;-1:-1:-1;;;;;815:11749:29;;;;;;;;;-1:-1:-1;;;;;815:11749:29;;;;3496:44;;;:::i;:::-;;3451:249;815:11749;;;;;;;;;3451:249;3587:43;;3644:45;3587:43;;:::i;:::-;;3644:45;:::i;:::-;;3451:249;;815:11749;;;-1:-1:-1;;;;;815:11749:29;;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;;;;-1:-1:-1;815:11749:29;;;;;;-1:-1:-1;815:11749:29;2723:69;-1:-1:-1;815:11749:29;;-1:-1:-1;;815:11749:29;;;;;;;;;-1:-1:-1;2723:69:29;;815:11749;;;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;-1:-1:-1;815:11749:29;;-1:-1:-1;815:11749:29;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;-1:-1:-1;;;;;815:11749:29;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;815:11749:29;;;;;;:::o;:::-;;;-1:-1:-1;;;;;815:11749:29;;;;;;:::o;6155:316:84:-;-1:-1:-1;;;;;815:11749:29;;2675:1;815:11749;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;;;;;;;;;-1:-1:-1;;;;;815:11749:29;2675:1;815:11749;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;;;;;;-1:-1:-1;;815:11749:29;;;;;735:10:112;;815:11749:29;-1:-1:-1;;;;;;;;;;;2675:1:29;;6346:40:84;6323:4;6400:11;:::o;6248:217::-;6442:12;2675:1:29;6442:12:84;:::o;6155:316::-;-1:-1:-1;;;;;815:11749:29;;;;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;;;;;;;;;-1:-1:-1;;;;;815:11749:29;;;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;;;;;;-1:-1:-1;;815:11749:29;;;;;735:10:112;;815:11749:29;-1:-1:-1;;;;;;;;;;;1507:33:29;-1:-1:-1;;;;;;;;;;;6346:40:84;815:11749:29;6346:40:84;6323:4;6400:11;:::o;6155:316::-;815:11749:29;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;-1:-1:-1;;;;;;;;;;;815:11749:29;;;;;;;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;-1:-1:-1;;;;;;;;;;;815:11749:29;;-1:-1:-1;;815:11749:29;6323:4:84;815:11749:29;;;735:10:112;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;815:11749:29;;6346:40:84;6323:4;6400:11;:::o;6248:217::-;815:11749:29;6442:12:84;:::o","linkReferences":{}},"deployedBytecode":{"object":"0x6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146106ab575080630bece356146105e8578063248a9ca3146105be5780632f2ff15d1461058f57806336568abe146105335780634d6d9ffb146104865780635832611b146104405780635972185a14610406578063682ed5f01461035157806391d1485414610308578063a217fddf146102ee578063a845a7f314610261578063d547741f1461022b578063ddba65371461015f5763ef913a4b146100c1575f80fd5b3461015b575f60031936011261015b576101576040516020808201526080604082015261014b816100f460c08201611228565b60ff600254818116606085015267ffffffffffffffff8160081c16608085015260481c16151560a0830152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810183528261085e565b604051918291826107cd565b0390f35b5f80fd5b3461015b5761016d36610749565b505060ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475460ff16156101f6575b7fda81d7c2000000000000000000000000000000000000000000000000000000005f5260045ffd5b6101fe611277565b6101ce565b7f928b1233000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461015b5761025f61023c3661079a565b9061025a610255825f525f602052600160405f20015490565b6112ff565b61175e565b005b3461015b575f60031936011261015b5760ff6002541660405161028e8161028781611228565b038261085e565b60405190604082019260408352815180945260206060840192015f945b8086106102c057505082935060208301520390f35b909260208060019273ffffffffffffffffffffffffffffffffffffffff8751168152019401950194906102ab565b3461015b575f60031936011261015b5760206040515f8152f35b3461015b576103163661079a565b905f525f60205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060ff60405f2054166040519015158152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811161015b5760a0600319823603011261015b5760ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475490916103f19160ff16156103f9575b6004016110cb565b604051908152f35b610401611277565b6103e9565b3461015b575f60031936011261015b5760206040517fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b8152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811680910361015b575f526004602052602067ffffffffffffffff60405f205416604051908152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811161015b576080600319823603011261015b5760ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475490916103f19160ff1615610526575b600401610ef5565b61052e611277565b61051e565b3461015b576105413661079a565b3373ffffffffffffffffffffffffffffffffffffffff8216036105675761025f9161175e565b7f6697b232000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461015b5761025f6105a03661079a565b906105b9610255825f525f602052600160405f20015490565b61168c565b3461015b57602060031936011261015b5760206103f16004355f525f602052600160405f20015490565b3461015b576105f636610749565b60ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475461065e929060ff161561069e57610a3c565b6040516003821015610671576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6106a6611277565b610a3c565b3461015b57602060031936011261015b57600435907fffffffff00000000000000000000000000000000000000000000000000000000821680920361015b57817f7965db0b000000000000000000000000000000000000000000000000000000006020931490811561071f575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483610718565b90602060031983011261015b5760043567ffffffffffffffff811161015b578260238201121561015b5780600401359267ffffffffffffffff841161015b576024848301011161015b576024019190565b600319604091011261015b576004359060243573ffffffffffffffffffffffffffffffffffffffff8116810361015b5790565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602060409481855280519182918282880152018686015e5f8582860101520116010190565b6040810190811067ffffffffffffffff82111761083157604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761083157604052565b92919267ffffffffffffffff821161083157604051916108e760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461085e565b82948184528183011161015b578281602093845f960137010152565b9080601f8301121561015b5781602061091e9335910161089f565b90565b67ffffffffffffffff81116108315760051b60200190565b60208183031261015b5780359067ffffffffffffffff821161015b57019060408282031261015b576040519161096e83610815565b803567ffffffffffffffff811161015b578261098b918301610903565b835260208101359067ffffffffffffffff821161015b57019080601f8301121561015b5781356109ba81610921565b926109c8604051948561085e565b81845260208085019260051b8201019183831161015b5760208201905b8382106109f9575050505050602082015290565b813567ffffffffffffffff811161015b57602091610a1c87848094880101610903565b8152019101906109e5565b519067ffffffffffffffff8216820361015b57565b610a4891810190610939565b60205f818351604051918183925191829101835e8101838152039060025afa15610c915760205f8051604051838101917f01000000000000000000000000000000000000000000000000000000000000008352602182015260218152610aaf60418261085e565b604051918291518091835e8101838152039060025afa15610c9157610ada5f516020830151906113eb565b5160408180518101031261015b57604051610af481610815565b610b0f6040610b0560208501610a27565b9384845201610a27565b67ffffffffffffffff60208301938285521680151580610c7e575b15610c4457505067ffffffffffffffff8151165f52600460205267ffffffffffffffff60405f20541680610bf3575067ffffffffffffffff9051918183169260025490838260081c168511610bb6575b50505116905f52600460205260405f20907fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008254161790555f90565b68ffffffffffffffff007fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000ff9160081b169116176002555f80610b7a565b9167ffffffffffffffff9150511603610c0b57600290565b69010000000000000000007fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff6002541617600255600190565b67ffffffffffffffff92507fdcbc5460000000000000000000000000000000000000000000000000000000005f526004521660245260445ffd5b5067ffffffffffffffff82161515610b2a565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561015b570180359067ffffffffffffffff821161015b57602001918160051b3603831361015b57565b3567ffffffffffffffff8116810361015b5790565b15610d0d5750565b67ffffffffffffffff907ff7caaa5c000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561015b570180359067ffffffffffffffff821161015b5760200191813603831361015b57565b60208183031261015b5780519067ffffffffffffffff821161015b57019060408282031261015b5760405191610dc983610815565b610dd281610a27565b835260208101519067ffffffffffffffff821161015b570181601f8201121561015b57805190610e0182610921565b92610e0f604051948561085e565b82845260208085019360061b8301019181831161015b57602001925b828410610e3e5750505050602082015290565b60408483031261015b5760206040918251610e5881610815565b865181528287015183820152815201930192610e2b565b15610e78575050565b9067ffffffffffffffff80927fc7441689000000000000000000000000000000000000000000000000000000005f52166004521660245260445ffd5b8051821015610ec85760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b6060810190610f048282610c9c565b90506001610f128484610c9c565b9290500361109b5750610fb8610f2a60408301610cf0565b9267ffffffffffffffff841692835f526004602052610fb367ffffffffffffffff60405f20541694610f5e87871515610d05565b610f9e610f76610f6e8580610d43565b810190610939565b610f8e610f838251611365565b6020830151906113eb565b5160208082518301019101610d94565b9667ffffffffffffffff885116918214610e6f565b610c9c565b15610ec857610fd3610fcc82602093610d43565b369161089f565b818151910120920180515115611073575f5b8151805182101561104b57610ffb828692610eb4565b51511461100a57600101610fe5565b60209293506110199151610eb4565b5101516110235790565b7f9a960b4f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f291fc442000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f1e6c84da000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f88b3170e000000000000000000000000000000000000000000000000000000005f52600160045260245260445ffd5b608081016110d98183610d43565b9050156112005760608201906110ef8284610c9c565b905060016110fd8486610c9c565b9290500361109b575061111260408401610cf0565b9061117467ffffffffffffffff831693845f52600460205261116e67ffffffffffffffff60405f2054169561114986881515610d05565b611159610f76610f6e8a80610d43565b9567ffffffffffffffff875116918214610e6f565b85610c9c565b15610ec85760209161118c610fcc8361119894610d43565b83815191012095610d43565b908092918101031261015b576020903591019081515115611073575f5b8251805182101561104b576111cb828792610eb4565b515114806111e8575b6111e0576001016111b5565b505050905090565b508160206111f7838651610eb4565b510151146111d4565b7f1208b21b000000000000000000000000000000000000000000000000000000005f5260045ffd5b602060015491828152019060015f5260205f20905f5b81811061124b5750505090565b825473ffffffffffffffffffffffffffffffffffffffff1684526020909301926001928301920161123e565b335f9081527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e0602052604090205460ff16156112af57565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004527fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b60245260445ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260ff60405f205416156113365750565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b5f60208092604051918183925191829101835e8101838152039060025afa15610c915760205f8051604051838101917f020000000000000000000000000000000000000000000000000000000000000083526021820152602181526113cb60418261085e565b604051918291518091835e8101838152039060025afa15610c91575f5190565b91909182511561166457825160ff60025416907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019060ff82116116375760ff8651921610156116095750508251927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061147e61146886610921565b95611476604051978861085e565b808752610921565b013660208601375f5b81518110156116025761149a8183610eb4565b5160418151036115cc5773ffffffffffffffffffffffffffffffffffffffff6114cf6114c68387611826565b90929192611860565b169081156115925750805f52600360205260ff60405f20541615611567575f5b82811061150c5750906001916115058288610eb4565b5201611487565b8173ffffffffffffffffffffffffffffffffffffffff61152c838a610eb4565b51161461153b576001016114ef565b507fe021c4f2000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7fd7e8a2c2000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6115c8906040519182917f9e3c1b3d000000000000000000000000000000000000000000000000000000008352600483016107cd565b0390fd5b6115c8906040519182917f2ee17a3d000000000000000000000000000000000000000000000000000000008352600483016107cd565b5050509050565b7f378b0805000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f301b38b6000000000000000000000000000000000000000000000000000000005f5260045ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f205416155f1461175857805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f2054165f1461175857805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b81519190604183036118565761184f9250602082015190606060408401519301515f1a9061190b565b9192909190565b50505f9160029190565b60048110156106715780611872575050565b600181036118a2577ff645eedf000000000000000000000000000000000000000000000000000000005f5260045ffd5b600281036118d657507ffce698f7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6003146118e05750565b7fd78bce0c000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161198f579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa15610c91575f5173ffffffffffffffffffffffffffffffffffffffff81161561198557905f905f90565b505f906001905f90565b5050505f916003919056fea164736f6c634300081c000a","sourceMap":"815:11749:29:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;3825:23;;;;815:11749;;;;;;3825:23;815:11749;;;;;;:::i;:::-;;;;;;;;;;;;1787:4;;;815:11749;;;;;;;;;;;;;;3825:23;;;;;;;;:::i;:::-;815:11749;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;12225:20;815:11749;;;;;;;;;;;;;;;;12444:42;12440:105;;815:11749;9710:21;815:11749;9710:21;815:11749;;9710:21;12440:105;12513:20;;:::i;:::-;12440:105;;815:11749;;;;;;;;;;;4723:26:84;815:11749:29;;;:::i;:::-;4693:18:84;2484:4;4693:18;;3877:6;815:11749:29;3877:6:84;815:11749:29;;3877:22:84;815:11749:29;3877:6:84;815:11749:29;3877:22:84;815:11749:29;3786:120:84;;4693:18;2484:4;:::i;:::-;4723:26;:::i;:::-;815:11749:29;;;;;;-1:-1:-1;;815:11749:29;;;;;;4065:27;815:11749;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4034:11;815:11749;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;2930:29:84;815:11749:29;-1:-1:-1;815:11749:29;;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;12225:20;815:11749;;;;;;;;;;;;;;;;;;5841:1853;;815:11749;;12444:42;12440:105;;815:11749;;;5841:1853;:::i;:::-;815:11749;;;;;;12440:105;12513:20;;:::i;:::-;12440:105;;815:11749;;;;;-1:-1:-1;;815:11749:29;;;;;;;;1507:33;815:11749;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;;;;1787:4;815:11749;;1787:4;815:11749;;;;1787:4;;815:11749;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;12225:20;815:11749;;;;;;;;;;;;;;;;;;7733:1799;;815:11749;;12444:42;12440:105;;815:11749;;;7733:1799;:::i;12440:105::-;12513:20;;:::i;:::-;12440:105;;815:11749;;;;;;;:::i;:::-;735:10:112;815:11749:29;;;5397:34:84;5393:102;;5505:37;;;:::i;5393:102::-;5454:30;815:11749:29;5454:30:84;815:11749:29;;5454:30:84;815:11749:29;;;;4306:25:84;815:11749:29;;;:::i;:::-;4276:18:84;2484:4;4276:18;;3877:6;815:11749:29;3877:6:84;815:11749:29;;3877:22:84;815:11749:29;3877:6:84;815:11749:29;3877:22:84;815:11749:29;3786:120:84;;2484:4;4306:25;:::i;815:11749:29:-;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;3877:6:84;815:11749:29;3877:6:84;815:11749:29;;3877:22:84;815:11749:29;3877:6:84;815:11749:29;3877:22:84;815:11749:29;3786:120:84;;815:11749:29;;;;;;;:::i;:::-;;12225:20;815:11749;;;;;;;;;;;;;;4341:1461;;815:11749;;;12444:42;12440:105;;4341:1461;:::i;:::-;815:11749;;;;;;;;;;;;;;;;;;;;;;;12440:105;12513:20;;:::i;:::-;4341:1461;:::i;815:11749::-;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;;;;2649:47:84;2664:32;815:11749:29;2649:47:84;;:87;;;;;815:11749:29;;;;;;;2649:87:84;844:25:121;829:40;;;2649:87:84;;;815:11749:29;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;1787:4;;815:11749;;;;;;;;1787:4::o;4341:1461::-;4562:58;4341:1461;4562:58;;;;:::i;:::-;815:11749;-1:-1:-1;4668:21:29;;;815:11749;;;;;;;;;;;;;;;;;;10125:12;;;;;;;;815:11749;-1:-1:-1;10125:12:29;;815:11749;;10099:39;;;815:11749;;;;;;;;;10099:39;;;;;;:::i;:::-;815:11749;;;;;;;;;;;;;;;10092:47;;10125:12;10092:47;;;;;4759:16;-1:-1:-1;10092:47:29;815:11749;4759:16;;;;;:::i;:::-;4859:21;815:11749;;;;4848:70;;1787:4;;;;815:11749;;;;;:::i;:::-;1787:4;815:11749;1787:4;815:11749;4848:70;;1787:4;:::i;:::-;;;;;;;:::i;:::-;815:11749;;1787:4;;;;;;815:11749;4937:16;;;:39;;;4341:1461;1787:4;;;;;815:11749;1787:4;;815:11749;-1:-1:-1;1787:4:29;5227:27;815:11749;1787:4;815:11749;;-1:-1:-1;1787:4:29;;815:11749;5282:22;5278:276;;1787:4;815:11749;1787:4;;815:11749;;;;1787:4;10125:12;1787:4;;;;;;815:11749;5568:39;;5564:109;;4341:1461;1787:4;;;815:11749;1787:4;-1:-1:-1;1787:4:29;5227:27;815:11749;1787:4;815:11749;-1:-1:-1;1787:4:29;;;;;;;;;-1:-1:-1;4341:1461:29;:::o;5564:109::-;1787:4;;;;;;;;;10125:12;1787:4;5564:109;;;;5278:276;1787:4;815:11749;1787:4;;;815:11749;5324:36;5320:169;;10125:12;5502:41;:::o;5320:169::-;1787:4;;10125:12;1787:4;;;10125:12;1787:4;;5425:49;:::o;1787:4::-;815:11749;1787:4;;;-1:-1:-1;1787:4:29;;815:11749;;1787:4;815:11749;1787:4;-1:-1:-1;1787:4:29;4937:39;815:11749;;;;4957:19;;4937:39;;10092:47;815:11749;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1910:4::-;;;;;;;;;;;;;;;;;;;;;;;;;;815:11749;;;;;:::i;:::-;1787:4;;;:::i;:::-;1910;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;815:11749;;1910:4;815:11749;;;;:::i;:::-;1910:4;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;815:11749;;;;;;:::i;:::-;1910:4;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;815:11749;1910:4;;;;;815:11749;1910:4;815:11749;;1787:4;815:11749;1787:4;1910;;;815:11749;;1910:4;;;;;;;;;;;;:::o;:::-;;;;;;;;;;7733:1799;7940:9;;;;;;;;:::i;:::-;7984;;7960:1;7984:9;;;;:::i;:::-;7940:21;;;;815:11749;;8104:31;9118:9;8104:31;;;;;:::i;:::-;815:11749;;;;1787:4;;-1:-1:-1;1787:4:29;8157:27;8104:16;1787:4;8893:103;815:11749;8104:31;-1:-1:-1;1787:4:29;;815:11749;8215:7;8207:57;8215:7;;;;8207:57;:::i;:::-;8739:71;8324:59;8335:10;;;;:::i;:::-;8324:59;;;;:::i;:::-;8522:16;8410:67;8430:21;;8410:67;:::i;:::-;8104:16;8522;;;;;:::i;:::-;8750:21;8104:16;815:11749;;;8739:71;;;;;;:::i;:::-;1787:4;815:11749;1787:4;;815:11749;8901:39;;;8893:103;:::i;:::-;9118:9;:::i;:::-;1910:4;;;;;;8104:16;1910:4;;:::i;:::-;;;;:::i;:::-;815:11749;;;;;9108:23;9149:25;;;;815:11749;9149:36;1910:4;;-1:-1:-1;9270:3:29;9236:25;;815:11749;;9232:36;;;;;9293:28;;;;;:::i;:::-;;1910:4;9293:45;9289:198;;7960:1;1910:4;9217:13;;9289:198;8104:16;9366:25;;;:28;:25;;:28;:::i;:::-;;:39;1910:4;;;9454:18;:::o;1910:4::-;;-1:-1:-1;1910:4:29;8157:27;-1:-1:-1;1910:4:29;9232:36;9514:11;-1:-1:-1;9514:11:29;8157:27;-1:-1:-1;9514:11:29;1910:4;;-1:-1:-1;1910:4:29;8157:27;-1:-1:-1;1910:4:29;815:11749;;;;7960:1;815:11749;;;;;;;5841:1853;6042:10;;;;;;;:::i;:::-;:22;;;815:11749;;6097:9;;;;;;;;:::i;:::-;6141;;6117:1;6141:9;;;;:::i;:::-;6097:21;;;;815:11749;;6261:31;;;;;;:::i;:::-;815:11749;7265:9;815:11749;;;1787:4;;6063:1;1787:4;6314:27;6261:16;1787:4;7050:103;815:11749;6261:31;6063:1;1787:4;;815:11749;6372:7;6364:57;6372:7;;;;6364:57;:::i;:::-;6896:71;6481:59;6492:10;;;;:::i;6896:71::-;1787:4;815:11749;1787:4;;815:11749;7058:39;;;7050:103;:::i;:::-;7265:9;;:::i;:::-;1910:4;;;6261:16;1910:4;;;;7315:10;1910:4;;:::i;:::-;815:11749;;;;;7255:23;7315:10;;:::i;:::-;7304:33;;;;;;815:11749;;;;6261:16;815:11749;;7355:25;;;;;815:11749;7355:36;1910:4;;6063:1;7476:3;7442:25;;815:11749;;7438:36;;;;;7499:28;;;;;:::i;:::-;;1910:4;7499:45;:97;;;7476:3;7495:154;;6117:1;1910:4;7423:13;;7495:154;7616:18;;;;;;:::o;7499:97::-;7548:25;;6261:16;7548:28;:25;;;:28;:::i;:::-;;:39;1910:4;7548:48;7499:97;;815:11749;;6063:1;815:11749;;6063:1;815:11749;;;4034:11;815:11749;;;;;;;4034:11;-1:-1:-1;815:11749:29;;-1:-1:-1;815:11749:29;;-1:-1:-1;815:11749:29;;;;;;;;;;:::o;:::-;;;;;;;;;;;;4034:11;815:11749;;;;;;;3175:103:84;735:10:112;2930:6:84;815:11749:29;;;;;;;;;;;;3495:23:84;3491:108;;3175:103::o;3491:108::-;3541:47;2930:6;3541:47;735:10:112;3541:47:84;815:11749:29;1507:33;815:11749;;;2930:6:84;3541:47;3175:103;815:11749:29;2930:6:84;815:11749:29;2930:6:84;815:11749:29;;;2930:6:84;815:11749:29;;735:10:112;815:11749:29;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;3495:23:84;3491:108;;3175:103;:::o;3491:108::-;3541:47;2930:6;3541:47;735:10:112;3541:47:84;815:11749:29;;;;2930:6:84;3541:47;9980:166:29;-1:-1:-1;815:11749:29;9980:166;;815:11749;;;;;;;;;;;;;;;;;;10125:12;;;;;;;;815:11749;-1:-1:-1;10125:12:29;;815:11749;;10099:39;;;815:11749;;;;;;;;;10099:39;;;;;;:::i;:::-;815:11749;;;;;;;;;;;;;;;10092:47;;10125:12;10092:47;;;;;-1:-1:-1;10092:47:29;9980:166;:::o;10562:773::-;;;;815:11749;;10672:21;815:11749;;;;;10765:27;815:11749;;;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;10692:1;11003:3;815:11749;;10980:21;;;;;11041:13;;;;:::i;:::-;;1660:2;815:11749;;11811:42;1660:2;;815:11749;3927:8:120;3871:27;;;;:::i;:::-;3927:8;;;;;:::i;:::-;815:11749:29;11970:23;;;1660:2;;815:11749;;10692:1;815:11749;12041:11;815:11749;;;;10692:1;815:11749;;;1660:2;;;10692:1;11184:5;;;;;;11299:19;;10765:11;11299:19;;;;;:::i;:::-;815:11749;1910:4;10965:13;;11191:3;11222:7;815:11749;11222:7;;;;:::i;:::-;815:11749;;11222:20;815:11749;;10765:11;1910:4;11169:13;;815:11749;;;10692:1;815:11749;;;;10692:1;815:11749;1660:2;;10692:1;1660:2;;815:11749;;10692:1;1660:2;;;815:11749;;;1660:2;;;;;;;;;;:::i;:::-;;;;;;815:11749;;;1660:2;;;;;;;;;;:::i;10980:21::-;;;;;;10562:773::o;815:11749::-;;10692:1;815:11749;;;;;;10692:1;815:11749;;;10692:1;815:11749;;;;;10692:1;815:11749;;;10692:1;815:11749;;10692:1;815:11749;6155:316:84;815:11749:29;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;6252:23:84;6248:217;815:11749:29;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;-1:-1:-1;815:11749:29;6323:4:84;815:11749:29;;;;;;;;735:10:112;815:11749:29;;6346:40:84;;815:11749:29;6346:40:84;;6323:4;6400:11;:::o;6248:217::-;6442:12;;815:11749:29;6442:12:84;:::o;6708:317::-;815:11749:29;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;6802:217:84;815:11749:29;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;-1:-1:-1;815:11749:29;;;;;;;;735:10:112;815:11749:29;;6900:40:84;;815:11749:29;6900:40:84;;815:11749:29;6954:11:84;:::o;2129:778:120:-;815:11749:29;;;2129:778:120;2319:2;2299:22;;2319:2;;2751:25;2535:196;;;;;;;;;;;;;;;-1:-1:-1;2535:196:120;2751:25;;:::i;:::-;2744:32;;;;;:::o;2295:606::-;2807:83;;2823:1;2807:83;2827:35;2807:83;;:::o;7280:532::-;815:11749:29;;;;;;7366:29:120;;;7411:7;;:::o;7362:444::-;815:11749:29;7462:38:120;;815:11749:29;;7523:23:120;7375:20;7523:23;815:11749:29;7375:20:120;7523:23;7458:348;7576:35;7567:44;;7576:35;;7634:46;;7375:20;7634:46;815:11749:29;;;7375:20:120;7634:46;7563:243;7710:30;7701:39;7697:109;;7563:243;7280:532::o;7697:109::-;7763:32;7375:20;7763:32;815:11749:29;;;7375:20:120;7763:32;5203:1551;;;6283:66;6270:79;;6266:164;;815:11749:29;;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;;6541:24:120;;;;;;;;;-1:-1:-1;6541:24:120;815:11749:29;;;6579:20:120;6575:113;;6698:49;-1:-1:-1;6698:49:120;-1:-1:-1;5203:1551:120;:::o;6575:113::-;6615:62;-1:-1:-1;6615:62:120;6541:24;6615:62;-1:-1:-1;6615:62:120;:::o;6266:164::-;6365:54;;;6381:1;6365:54;6385:30;6365:54;;:::o","linkReferences":{}},"methodIdentifiers":{"DEFAULT_ADMIN_ROLE()":"a217fddf","PROOF_SUBMITTER_ROLE()":"5972185a","getAttestationSet()":"a845a7f3","getClientState()":"ef913a4b","getConsensusTimestamp(uint64)":"5832611b","getRoleAdmin(bytes32)":"248a9ca3","grantRole(bytes32,address)":"2f2ff15d","hasRole(bytes32,address)":"91d14854","misbehaviour(bytes)":"ddba6537","renounceRole(bytes32,address)":"36568abe","revokeRole(bytes32,address)":"d547741f","supportsInterface(bytes4)":"01ffc9a7","updateClient(bytes)":"0bece356","verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":"682ed5f0","verifyNonMembership((bytes,(uint64,uint64),bytes[]))":"4d6d9ffb"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"attestorAddresses\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"minRequiredSigs\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"initialHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initialTimestampSeconds\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"roleManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"minRequired\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"attestationCount\",\"type\":\"uint256\"}],\"name\":\"BadQuorum\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"height\",\"type\":\"uint64\"}],\"name\":\"ConsensusTimestampNotFound\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ECDSAInvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"ECDSAInvalidSignatureLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"ECDSAInvalidSignatureS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyPackets\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptySignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyValue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FeatureNotSupported\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FrozenClientState\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"expected\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"provided\",\"type\":\"uint64\"}],\"name\":\"HeightMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expectedLength\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"providedLength\",\"type\":\"uint256\"}],\"name\":\"InvalidPathLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignatureLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"height\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"InvalidState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoAttestors\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotMember\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotNonMember\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"SignatureInvalid\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"validSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"minRequired\",\"type\":\"uint8\"}],\"name\":\"ThresholdNotMet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"UnknownSigner\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PROOF_SUBMITTER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAttestationSet\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"attestorAddresses\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"minRequiredSigs\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getClientState\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"name\":\"getConsensusTimestamp\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"misbehaviour\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callerConfirmation\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"updateMsg\",\"type\":\"bytes\"}],\"name\":\"updateClient\",\"outputs\":[{\"internalType\":\"enum ILightClientMsgs.UpdateResult\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"revisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"internalType\":\"struct IICS02ClientMsgs.Height\",\"name\":\"proofHeight\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"value\",\"type\":\"bytes\"}],\"internalType\":\"struct ILightClientMsgs.MsgVerifyMembership\",\"name\":\"msg_\",\"type\":\"tuple\"}],\"name\":\"verifyMembership\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"revisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"internalType\":\"struct IICS02ClientMsgs.Height\",\"name\":\"proofHeight\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ILightClientMsgs.MsgVerifyNonMembership\",\"name\":\"msg_\",\"type\":\"tuple\"}],\"name\":\"verifyNonMembership\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"errors\":{\"AccessControlBadConfirmation()\":[{\"details\":\"The caller of a function is not the expected one. NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\"}],\"AccessControlUnauthorizedAccount(address,bytes32)\":[{\"details\":\"The `account` is missing a role.\"}],\"BadQuorum(uint8,uint256)\":[{\"params\":{\"attestationCount\":\"The size of the configured attestation set.\",\"minRequired\":\"The provided minimum required signatures.\"}}],\"ConsensusTimestampNotFound(uint64)\":[{\"params\":{\"height\":\"The height that has no associated timestamp.\"}}],\"DuplicateSigner(address)\":[{\"params\":{\"signer\":\"Address of the duplicate signer.\"}}],\"ECDSAInvalidSignature()\":[{\"details\":\"The signature derives the `address(0)`.\"}],\"ECDSAInvalidSignatureLength(uint256)\":[{\"details\":\"The signature has an invalid length.\"}],\"ECDSAInvalidSignatureS(bytes32)\":[{\"details\":\"The signature has an S value that is in the upper half order.\"}],\"HeightMismatch(uint64,uint64)\":[{\"params\":{\"expected\":\"The expected height from the proofHeight field.\",\"provided\":\"The height that was present in the attested payload.\"}}],\"InvalidPathLength(uint256,uint256)\":[{\"params\":{\"expectedLength\":\"The expected length of the path.\",\"providedLength\":\"The length of the provided path.\"}}],\"InvalidSignatureLength(bytes)\":[{\"params\":{\"signature\":\"The invalid signature.\"}}],\"InvalidState(uint64,uint64)\":[{\"params\":{\"height\":\"The height of the attested state.\",\"timestamp\":\"The timestamp of the attested state.\"}}],\"SignatureInvalid(bytes)\":[{\"params\":{\"signature\":\"The invalid signature.\"}}],\"ThresholdNotMet(uint256,uint8)\":[{\"params\":{\"minRequired\":\"The minimum required signatures.\",\"validSigners\":\"Number of valid, unique attestation signatures.\"}}],\"UnknownSigner(address)\":[{\"params\":{\"signer\":\"Address of the unknown signer.\"}}]},\"events\":{\"RoleAdminChanged(bytes32,bytes32,bytes32)\":{\"details\":\"Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite {RoleAdminChanged} not being emitted to signal this.\"},\"RoleGranted(bytes32,address,address)\":{\"details\":\"Emitted when `account` is granted `role`. `sender` is the account that originated the contract call. This account bears the admin role (for the granted role). Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.\"},\"RoleRevoked(bytes32,address,address)\":{\"details\":\"Emitted when `account` is revoked `role`. `sender` is the account that originated the contract call: - if using `revokeRole`, it is the admin role bearer - if using `renounceRole`, it is the role bearer (i.e. `account`)\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"params\":{\"attestorAddresses\":\"The configured attestor addresses (EOAs)\",\"initialHeight\":\"The initial known height\",\"initialTimestampSeconds\":\"The initial timestamp in seconds for the initial height\",\"minRequiredSigs\":\"The quorum threshold\",\"roleManager\":\"Address that will administer roles and be allowed to submit proofs; if zero, anyone can submit\"}},\"getAttestationSet()\":{\"details\":\"The attestation set is fixed for the initial scope; rotation is out of scope.\",\"returns\":{\"attestorAddresses\":\"The configured attestor EOA addresses\",\"minRequiredSigs\":\"The minimum number of unique valid signatures required\"}},\"getClientState()\":{\"returns\":{\"_0\":\"The client state.\"}},\"getConsensusTimestamp(uint64)\":{\"params\":{\"revisionHeight\":\"The height for which to query the timestamp\"},\"returns\":{\"_0\":\"The trusted timestamp in unix seconds\"}},\"getRoleAdmin(bytes32)\":{\"details\":\"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}.\"},\"grantRole(bytes32,address)\":{\"details\":\"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event.\"},\"hasRole(bytes32,address)\":{\"details\":\"Returns `true` if `account` has been granted `role`.\"},\"misbehaviour(bytes)\":{\"params\":{\"misbehaviourMsg\":\"The misbehaviour message\"}},\"renounceRole(bytes32,address)\":{\"details\":\"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event.\"},\"revokeRole(bytes32,address)\":{\"details\":\"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event.\"},\"supportsInterface(bytes4)\":{\"details\":\"Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] to learn more about how these ids are created. This function call must use less than 30 000 gas.\"},\"updateClient(bytes)\":{\"params\":{\"updateMsg\":\"The encoded update message e.g., an SP1 proof.\"},\"returns\":{\"_0\":\"The result of the update operation\"}},\"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))\":{\"details\":\"Notice that this message is not view, as it may update the client state for caching purposes.\",\"params\":{\"msg_\":\"The membership message\"},\"returns\":{\"_0\":\"The unix timestamp of the verification height in the counterparty chain in seconds.\"}},\"verifyNonMembership((bytes,(uint64,uint64),bytes[]))\":{\"details\":\"Notice that this message is not view, as it may update the client state for caching purposes.\",\"params\":{\"msg_\":\"The membership message\"},\"returns\":{\"_0\":\"The unix timestamp of the verification height in the counterparty chain in seconds.\"}}},\"stateVariables\":{\"PROOF_SUBMITTER_ROLE\":{\"details\":\"If `address(0)` has this role, then anyone can submit proofs. If this client is used with `ICS26Router`, the router must be granted this role.\",\"return\":\"The role identifier\",\"returns\":{\"_0\":\"The role identifier\"}}},\"title\":\"Attestation-based IBC Light Client\",\"version\":1},\"userdoc\":{\"errors\":{\"BadQuorum(uint8,uint256)\":[{\"notice\":\"Reverts when the quorum threshold is zero or exceeds the attestation count.\"}],\"ConsensusTimestampNotFound(uint64)\":[{\"notice\":\"Missing trusted timestamp for the given height.\"}],\"DuplicateSigner(address)\":[{\"notice\":\"The recovered signer appears more than once among the signatures.\"}],\"EmptyPackets()\":[{\"notice\":\"The provided packet attestations were empty.\"}],\"EmptySignatures()\":[{\"notice\":\"The provided signatures were empty.\"}],\"EmptyValue()\":[{\"notice\":\"The provided membership value was empty.\"}],\"FeatureNotSupported()\":[{\"notice\":\"Reverts for functions that are out of scope for this implementation.\"}],\"FrozenClientState()\":[{\"notice\":\"Reverts when an action is attempted on a frozen client state.\"}],\"HeightMismatch(uint64,uint64)\":[{\"notice\":\"The attested height did not match the provided proof height.\"}],\"InvalidPathLength(uint256,uint256)\":[{\"notice\":\"The provided proof path length is invalid.\"}],\"InvalidSignatureLength(bytes)\":[{\"notice\":\"ECDSA signature has an invalid length.\"}],\"InvalidState(uint64,uint64)\":[{\"notice\":\"The attested state is invalid.\"}],\"NoAttestors()\":[{\"notice\":\"Reverts when the attestation set is empty during initialization.\"}],\"NotMember()\":[{\"notice\":\"The provided value is not a member of the attested set.\"}],\"NotNonMember()\":[{\"notice\":\"The attested commitment is not zero (receipt exists, so packet was received).\"}],\"SignatureInvalid(bytes)\":[{\"notice\":\"ECDSA signature failed to recover a valid signer.\"}],\"ThresholdNotMet(uint256,uint8)\":[{\"notice\":\"The number of valid unique signatures is below the required threshold.\"}],\"UnknownSigner(address)\":[{\"notice\":\"The recovered signer is not part of the attestation set.\"}]},\"kind\":\"user\",\"methods\":{\"PROOF_SUBMITTER_ROLE()\":{\"notice\":\"The role identifier for the proof submitter role\"},\"constructor\":{\"notice\":\"Initializes the attestor light client with its fixed attestor set and initial height/timestamp.\"},\"getAttestationSet()\":{\"notice\":\"Returns the attestation set configuration.\"},\"getClientState()\":{\"notice\":\"Returns the client state.\"},\"getConsensusTimestamp(uint64)\":{\"notice\":\"Returns the trusted consensus timestamp (in seconds) at the given revision height.\"},\"misbehaviour(bytes)\":{\"notice\":\"Misbehaviour handling, moves the light client to the frozen state if misbehaviour is detected\"},\"updateClient(bytes)\":{\"notice\":\"Updating the client and consensus state\"},\"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))\":{\"notice\":\"Querying the membership of a key-value pair\"},\"verifyNonMembership((bytes,(uint64,uint64),bytes[]))\":{\"notice\":\"Querying the non-membership of a key\"}},\"notice\":\"Implements an IBC light client that trusts an off-chain m-of-n attestor set.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/light-clients/attestation/AttestationLightClient.sol\":\"AttestationLightClient\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[\":@openzeppelin-contracts/=node_modules/@openzeppelin/contracts/\",\":@openzeppelin-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/\",\":@openzeppelin/=node_modules/@openzeppelin/\",\":@sp1-contracts/=node_modules/sp1-contracts/contracts/src/\",\":@uniswap/permit2/=node_modules/@uniswap/permit2/\",\":forge-std/=node_modules/forge-std/src/\",\":sp1-contracts/=node_modules/sp1-contracts/\"],\"viaIR\":true},\"sources\":{\"contracts/interfaces/ILightClient.sol\":{\"keccak256\":\"0x1caae9e4f741a86c0056d5f7713a35f3bd96db7a9fd52fc9f1cf79aad76a442f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4a536db3b926560f69547c17e4f451a2631ddea0f42bd224c7fd8382d74429f6\",\"dweb:/ipfs/QmcQ33kxcmmowXgMbQGTBgiKBjF7v8UiP4y73bB9hhDizC\"]},\"contracts/light-clients/attestation/AttestationLightClient.sol\":{\"keccak256\":\"0xb287ccfd4e7b3baa99215a62484a99117b3be10d9765899c221f3fb0d54876fd\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://752979b4fe813e9cee697e2c29c7b119f8f0f84e2a9b6b1d0db97685a4435660\",\"dweb:/ipfs/QmTxfSdTKcrfaKKKtsX5Wy8RVMYcyYfkKCaDm2brNYTLFz\"]},\"contracts/light-clients/attestation/errors/IAttestationLightClientErrors.sol\":{\"keccak256\":\"0x0d3592b3c36cfcde83e2510bb9179b41befb486cbb65f529ff68c09fad751564\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8d9bd1170d3954f7af6bd9fe79efbeff8f943073d27eb7bbecc8b9c66a0bdc62\",\"dweb:/ipfs/QmRuorHcJVhDJdYf7y43tyZuDowoBGkeRjH2z9C939XuDa\"]},\"contracts/light-clients/attestation/interfaces/IAttestationLightClient.sol\":{\"keccak256\":\"0x115ffce5474e6127fdb1f2b9284ea6d51ec978efd47f4425ba693c6c69d8b0af\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://bebb9e7d156b1a3ec682ff9cb8c9680b718bf0ae8e217ed153811dd399e456e8\",\"dweb:/ipfs/QmcjbxSBU4jvZMxn7PYYM6fxLCovWj1rKAtZazXY2RSA8U\"]},\"contracts/light-clients/attestation/msgs/IAttestationLightClientMsgs.sol\":{\"keccak256\":\"0x08148f1409f7e3c8968916950d920b8a4352796191875a4672ad2404e3541ef5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://94c4b12d851c5e7b581d2794c88481df658eaf55ad4e54ddef14423c90986a67\",\"dweb:/ipfs/QmZysvpbY1Xe7kNVw1iHZsPaVMUMqLauJssyrceQybBgXw\"]},\"contracts/light-clients/attestation/msgs/IAttestationMsgs.sol\":{\"keccak256\":\"0xba7901ec5fb4a4845272501a1a0b183309cd0ae37d9ec5f9e3c6ce699bfd281f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://72d0768a801a3c70910c10a1fc48c29c62d902dc602e20d72a1388ba498e8a4b\",\"dweb:/ipfs/QmUKLdbX2NwSyHFrh2WUHLm4zdFivu3Ryw2XyjT3R9Szse\"]},\"contracts/msgs/IICS02ClientMsgs.sol\":{\"keccak256\":\"0xb5fdb25319d5c32b3f527982d45ec1e9bec5215515581aee034d853006ea01a0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6b8939bf552c62c952a491008bcd9b59ab7dda20a9482bf93438e05793881542\",\"dweb:/ipfs/QmfWmzNCp8bd4pP26Lfw5HHp4dSZnCDqZDjVN7SD8P7eDh\"]},\"contracts/msgs/ILightClientMsgs.sol\":{\"keccak256\":\"0xa0565e7748b8b3284d72e1aa6ef4cc8264fcaa5f4e0761d22d64686953db6378\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c4476010d7090bfaa74127526f2051c6bc82ff42a09b936e848880a1f15a6065\",\"dweb:/ipfs/QmY87JgRsFQXSZdGXNjNHEJCkdTHzFXH4vZEJ3q7MnHuP8\"]},\"node_modules/@openzeppelin/contracts/access/AccessControl.sol\":{\"keccak256\":\"0x1a6b4f6b7798ab80929d491b89d5427a9b3338c0fd1acd0ba325f69c6f1646af\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7bb7f346c12a14dc622bc105ce3c47202fbc89f4b153a28a63bb68193297330c\",\"dweb:/ipfs/QmagwF8P3bUBXwdo159ueEnY9dLSvEWwK24kk2op58egwG\"]},\"node_modules/@openzeppelin/contracts/access/IAccessControl.sol\":{\"keccak256\":\"0xbff9f59c84e5337689161ce7641c0ef8e872d6a7536fbc1f5133f128887aba3c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b308f882e796f7b79c9502deacb0a62983035c6f6f4e962b319ba6a1f4a77d3d\",\"dweb:/ipfs/QmaWCW7ahEQqFjwhSUhV7Ae7WhfNvzSpE7DQ58hvEooqPL\"]},\"node_modules/@openzeppelin/contracts/utils/Context.sol\":{\"keccak256\":\"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12\",\"dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF\"]},\"node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol\":{\"keccak256\":\"0x69f54c02b7d81d505910ec198c11ed4c6a728418a868b906b4a0cf29946fda84\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8e25e4bdb7ae1f21d23bfee996e22736fc0ab44cfabedac82a757b1edc5623b9\",\"dweb:/ipfs/QmQdWQvB6JCP9ZMbzi8EvQ1PTETqkcTWrbcVurS7DKpa5n\"]},\"node_modules/@openzeppelin/contracts/utils/introspection/ERC165.sol\":{\"keccak256\":\"0x2d9dc2fe26180f74c11c13663647d38e259e45f95eb88f57b61d2160b0109d3e\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://81233d1f98060113d9922180bb0f14f8335856fe9f339134b09335e9f678c377\",\"dweb:/ipfs/QmWh6R35SarhAn4z2wH8SU456jJSYL2FgucfTFgbHJJN4E\"]},\"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617\",\"dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address[]","name":"attestorAddresses","type":"address[]"},{"internalType":"uint8","name":"minRequiredSigs","type":"uint8"},{"internalType":"uint64","name":"initialHeight","type":"uint64"},{"internalType":"uint64","name":"initialTimestampSeconds","type":"uint64"},{"internalType":"address","name":"roleManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"type":"error","name":"AccessControlBadConfirmation"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"type":"error","name":"AccessControlUnauthorizedAccount"},{"inputs":[{"internalType":"uint8","name":"minRequired","type":"uint8"},{"internalType":"uint256","name":"attestationCount","type":"uint256"}],"type":"error","name":"BadQuorum"},{"inputs":[{"internalType":"uint64","name":"height","type":"uint64"}],"type":"error","name":"ConsensusTimestampNotFound"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"type":"error","name":"DuplicateSigner"},{"inputs":[],"type":"error","name":"ECDSAInvalidSignature"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"type":"error","name":"ECDSAInvalidSignatureLength"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"type":"error","name":"ECDSAInvalidSignatureS"},{"inputs":[],"type":"error","name":"EmptyPackets"},{"inputs":[],"type":"error","name":"EmptySignatures"},{"inputs":[],"type":"error","name":"EmptyValue"},{"inputs":[],"type":"error","name":"FeatureNotSupported"},{"inputs":[],"type":"error","name":"FrozenClientState"},{"inputs":[{"internalType":"uint64","name":"expected","type":"uint64"},{"internalType":"uint64","name":"provided","type":"uint64"}],"type":"error","name":"HeightMismatch"},{"inputs":[{"internalType":"uint256","name":"expectedLength","type":"uint256"},{"internalType":"uint256","name":"providedLength","type":"uint256"}],"type":"error","name":"InvalidPathLength"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"type":"error","name":"InvalidSignatureLength"},{"inputs":[{"internalType":"uint64","name":"height","type":"uint64"},{"internalType":"uint64","name":"timestamp","type":"uint64"}],"type":"error","name":"InvalidState"},{"inputs":[],"type":"error","name":"NoAttestors"},{"inputs":[],"type":"error","name":"NotMember"},{"inputs":[],"type":"error","name":"NotNonMember"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"type":"error","name":"SignatureInvalid"},{"inputs":[{"internalType":"uint256","name":"validSigners","type":"uint256"},{"internalType":"uint8","name":"minRequired","type":"uint8"}],"type":"error","name":"ThresholdNotMet"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"type":"error","name":"UnknownSigner"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"previousAdminRole","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"newAdminRole","type":"bytes32","indexed":true}],"type":"event","name":"RoleAdminChanged","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleGranted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleRevoked","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"PROOF_SUBMITTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getAttestationSet","outputs":[{"internalType":"address[]","name":"attestorAddresses","type":"address[]"},{"internalType":"uint8","name":"minRequiredSigs","type":"uint8"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getClientState","outputs":[{"internalType":"bytes","name":"","type":"bytes"}]},{"inputs":[{"internalType":"uint64","name":"revisionHeight","type":"uint64"}],"stateMutability":"view","type":"function","name":"getConsensusTimestamp","outputs":[{"internalType":"uint64","name":"","type":"uint64"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"grantRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"view","type":"function","name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function","name":"misbehaviour"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"renounceRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"revokeRole"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"stateMutability":"view","type":"function","name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes","name":"updateMsg","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"updateClient","outputs":[{"internalType":"enum ILightClientMsgs.UpdateResult","name":"","type":"uint8"}]},{"inputs":[{"internalType":"struct ILightClientMsgs.MsgVerifyMembership","name":"msg_","type":"tuple","components":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"struct IICS02ClientMsgs.Height","name":"proofHeight","type":"tuple","components":[{"internalType":"uint64","name":"revisionNumber","type":"uint64"},{"internalType":"uint64","name":"revisionHeight","type":"uint64"}]},{"internalType":"bytes[]","name":"path","type":"bytes[]"},{"internalType":"bytes","name":"value","type":"bytes"}]}],"stateMutability":"view","type":"function","name":"verifyMembership","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"struct ILightClientMsgs.MsgVerifyNonMembership","name":"msg_","type":"tuple","components":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"struct IICS02ClientMsgs.Height","name":"proofHeight","type":"tuple","components":[{"internalType":"uint64","name":"revisionNumber","type":"uint64"},{"internalType":"uint64","name":"revisionHeight","type":"uint64"}]},{"internalType":"bytes[]","name":"path","type":"bytes[]"}]}],"stateMutability":"view","type":"function","name":"verifyNonMembership","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{"constructor":{"params":{"attestorAddresses":"The configured attestor addresses (EOAs)","initialHeight":"The initial known height","initialTimestampSeconds":"The initial timestamp in seconds for the initial height","minRequiredSigs":"The quorum threshold","roleManager":"Address that will administer roles and be allowed to submit proofs; if zero, anyone can submit"}},"getAttestationSet()":{"details":"The attestation set is fixed for the initial scope; rotation is out of scope.","returns":{"attestorAddresses":"The configured attestor EOA addresses","minRequiredSigs":"The minimum number of unique valid signatures required"}},"getClientState()":{"returns":{"_0":"The client state."}},"getConsensusTimestamp(uint64)":{"params":{"revisionHeight":"The height for which to query the timestamp"},"returns":{"_0":"The trusted timestamp in unix seconds"}},"getRoleAdmin(bytes32)":{"details":"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}."},"grantRole(bytes32,address)":{"details":"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event."},"hasRole(bytes32,address)":{"details":"Returns `true` if `account` has been granted `role`."},"misbehaviour(bytes)":{"params":{"misbehaviourMsg":"The misbehaviour message"}},"renounceRole(bytes32,address)":{"details":"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event."},"revokeRole(bytes32,address)":{"details":"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event."},"supportsInterface(bytes4)":{"details":"Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] to learn more about how these ids are created. This function call must use less than 30 000 gas."},"updateClient(bytes)":{"params":{"updateMsg":"The encoded update message e.g., an SP1 proof."},"returns":{"_0":"The result of the update operation"}},"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":{"details":"Notice that this message is not view, as it may update the client state for caching purposes.","params":{"msg_":"The membership message"},"returns":{"_0":"The unix timestamp of the verification height in the counterparty chain in seconds."}},"verifyNonMembership((bytes,(uint64,uint64),bytes[]))":{"details":"Notice that this message is not view, as it may update the client state for caching purposes.","params":{"msg_":"The membership message"},"returns":{"_0":"The unix timestamp of the verification height in the counterparty chain in seconds."}}},"version":1},"userdoc":{"kind":"user","methods":{"PROOF_SUBMITTER_ROLE()":{"notice":"The role identifier for the proof submitter role"},"constructor":{"notice":"Initializes the attestor light client with its fixed attestor set and initial height/timestamp."},"getAttestationSet()":{"notice":"Returns the attestation set configuration."},"getClientState()":{"notice":"Returns the client state."},"getConsensusTimestamp(uint64)":{"notice":"Returns the trusted consensus timestamp (in seconds) at the given revision height."},"misbehaviour(bytes)":{"notice":"Misbehaviour handling, moves the light client to the frozen state if misbehaviour is detected"},"updateClient(bytes)":{"notice":"Updating the client and consensus state"},"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":{"notice":"Querying the membership of a key-value pair"},"verifyNonMembership((bytes,(uint64,uint64),bytes[]))":{"notice":"Querying the non-membership of a key"}},"version":1}},"settings":{"remappings":["@openzeppelin-contracts/=node_modules/@openzeppelin/contracts/","@openzeppelin-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/","@openzeppelin/=node_modules/@openzeppelin/","@sp1-contracts/=node_modules/sp1-contracts/contracts/src/","@uniswap/permit2/=node_modules/@uniswap/permit2/","forge-std/=node_modules/forge-std/src/","sp1-contracts/=node_modules/sp1-contracts/"],"optimizer":{"enabled":true,"runs":10000},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"contracts/light-clients/attestation/AttestationLightClient.sol":"AttestationLightClient"},"evmVersion":"cancun","libraries":{},"viaIR":true},"sources":{"contracts/interfaces/ILightClient.sol":{"keccak256":"0x1caae9e4f741a86c0056d5f7713a35f3bd96db7a9fd52fc9f1cf79aad76a442f","urls":["bzz-raw://4a536db3b926560f69547c17e4f451a2631ddea0f42bd224c7fd8382d74429f6","dweb:/ipfs/QmcQ33kxcmmowXgMbQGTBgiKBjF7v8UiP4y73bB9hhDizC"],"license":"MIT"},"contracts/light-clients/attestation/AttestationLightClient.sol":{"keccak256":"0xb287ccfd4e7b3baa99215a62484a99117b3be10d9765899c221f3fb0d54876fd","urls":["bzz-raw://752979b4fe813e9cee697e2c29c7b119f8f0f84e2a9b6b1d0db97685a4435660","dweb:/ipfs/QmTxfSdTKcrfaKKKtsX5Wy8RVMYcyYfkKCaDm2brNYTLFz"],"license":"MIT"},"contracts/light-clients/attestation/errors/IAttestationLightClientErrors.sol":{"keccak256":"0x0d3592b3c36cfcde83e2510bb9179b41befb486cbb65f529ff68c09fad751564","urls":["bzz-raw://8d9bd1170d3954f7af6bd9fe79efbeff8f943073d27eb7bbecc8b9c66a0bdc62","dweb:/ipfs/QmRuorHcJVhDJdYf7y43tyZuDowoBGkeRjH2z9C939XuDa"],"license":"MIT"},"contracts/light-clients/attestation/interfaces/IAttestationLightClient.sol":{"keccak256":"0x115ffce5474e6127fdb1f2b9284ea6d51ec978efd47f4425ba693c6c69d8b0af","urls":["bzz-raw://bebb9e7d156b1a3ec682ff9cb8c9680b718bf0ae8e217ed153811dd399e456e8","dweb:/ipfs/QmcjbxSBU4jvZMxn7PYYM6fxLCovWj1rKAtZazXY2RSA8U"],"license":"MIT"},"contracts/light-clients/attestation/msgs/IAttestationLightClientMsgs.sol":{"keccak256":"0x08148f1409f7e3c8968916950d920b8a4352796191875a4672ad2404e3541ef5","urls":["bzz-raw://94c4b12d851c5e7b581d2794c88481df658eaf55ad4e54ddef14423c90986a67","dweb:/ipfs/QmZysvpbY1Xe7kNVw1iHZsPaVMUMqLauJssyrceQybBgXw"],"license":"MIT"},"contracts/light-clients/attestation/msgs/IAttestationMsgs.sol":{"keccak256":"0xba7901ec5fb4a4845272501a1a0b183309cd0ae37d9ec5f9e3c6ce699bfd281f","urls":["bzz-raw://72d0768a801a3c70910c10a1fc48c29c62d902dc602e20d72a1388ba498e8a4b","dweb:/ipfs/QmUKLdbX2NwSyHFrh2WUHLm4zdFivu3Ryw2XyjT3R9Szse"],"license":"MIT"},"contracts/msgs/IICS02ClientMsgs.sol":{"keccak256":"0xb5fdb25319d5c32b3f527982d45ec1e9bec5215515581aee034d853006ea01a0","urls":["bzz-raw://6b8939bf552c62c952a491008bcd9b59ab7dda20a9482bf93438e05793881542","dweb:/ipfs/QmfWmzNCp8bd4pP26Lfw5HHp4dSZnCDqZDjVN7SD8P7eDh"],"license":"MIT"},"contracts/msgs/ILightClientMsgs.sol":{"keccak256":"0xa0565e7748b8b3284d72e1aa6ef4cc8264fcaa5f4e0761d22d64686953db6378","urls":["bzz-raw://c4476010d7090bfaa74127526f2051c6bc82ff42a09b936e848880a1f15a6065","dweb:/ipfs/QmY87JgRsFQXSZdGXNjNHEJCkdTHzFXH4vZEJ3q7MnHuP8"],"license":"MIT"},"node_modules/@openzeppelin/contracts/access/AccessControl.sol":{"keccak256":"0x1a6b4f6b7798ab80929d491b89d5427a9b3338c0fd1acd0ba325f69c6f1646af","urls":["bzz-raw://7bb7f346c12a14dc622bc105ce3c47202fbc89f4b153a28a63bb68193297330c","dweb:/ipfs/QmagwF8P3bUBXwdo159ueEnY9dLSvEWwK24kk2op58egwG"],"license":"MIT"},"node_modules/@openzeppelin/contracts/access/IAccessControl.sol":{"keccak256":"0xbff9f59c84e5337689161ce7641c0ef8e872d6a7536fbc1f5133f128887aba3c","urls":["bzz-raw://b308f882e796f7b79c9502deacb0a62983035c6f6f4e962b319ba6a1f4a77d3d","dweb:/ipfs/QmaWCW7ahEQqFjwhSUhV7Ae7WhfNvzSpE7DQ58hvEooqPL"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/Context.sol":{"keccak256":"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2","urls":["bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12","dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol":{"keccak256":"0x69f54c02b7d81d505910ec198c11ed4c6a728418a868b906b4a0cf29946fda84","urls":["bzz-raw://8e25e4bdb7ae1f21d23bfee996e22736fc0ab44cfabedac82a757b1edc5623b9","dweb:/ipfs/QmQdWQvB6JCP9ZMbzi8EvQ1PTETqkcTWrbcVurS7DKpa5n"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/introspection/ERC165.sol":{"keccak256":"0x2d9dc2fe26180f74c11c13663647d38e259e45f95eb88f57b61d2160b0109d3e","urls":["bzz-raw://81233d1f98060113d9922180bb0f14f8335856fe9f339134b09335e9f678c377","dweb:/ipfs/QmWh6R35SarhAn4z2wH8SU456jJSYL2FgucfTFgbHJJN4E"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol":{"keccak256":"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c","urls":["bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617","dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u"],"license":"MIT"}},"version":1},"id":29} \ No newline at end of file +{"abi":[{"type":"constructor","inputs":[{"name":"attestorAddresses","type":"address[]","internalType":"address[]"},{"name":"minRequiredSigs","type":"uint8","internalType":"uint8"},{"name":"initialHeight","type":"uint64","internalType":"uint64"},{"name":"initialTimestampSeconds","type":"uint64","internalType":"uint64"},{"name":"roleManager","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"DEFAULT_ADMIN_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"PROOF_SUBMITTER_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"getAttestationSet","inputs":[],"outputs":[{"name":"attestorAddresses","type":"address[]","internalType":"address[]"},{"name":"minRequiredSigs","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"type":"function","name":"getClientState","inputs":[],"outputs":[{"name":"","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"getConsensusTimestamp","inputs":[{"name":"revisionHeight","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"getRoleAdmin","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"grantRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"hasRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"misbehaviour","inputs":[{"name":"","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"view"},{"type":"function","name":"renounceRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"callerConfirmation","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"revokeRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"updateClient","inputs":[{"name":"updateMsg","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"uint8","internalType":"enum ILightClientMsgs.UpdateResult"}],"stateMutability":"nonpayable"},{"type":"function","name":"verifyMembership","inputs":[{"name":"msg_","type":"tuple","internalType":"struct ILightClientMsgs.MsgVerifyMembership","components":[{"name":"proof","type":"bytes","internalType":"bytes"},{"name":"proofHeight","type":"tuple","internalType":"struct IICS02ClientMsgs.Height","components":[{"name":"revisionNumber","type":"uint64","internalType":"uint64"},{"name":"revisionHeight","type":"uint64","internalType":"uint64"}]},{"name":"path","type":"bytes[]","internalType":"bytes[]"},{"name":"value","type":"bytes","internalType":"bytes"}]}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"verifyNonMembership","inputs":[{"name":"msg_","type":"tuple","internalType":"struct ILightClientMsgs.MsgVerifyNonMembership","components":[{"name":"proof","type":"bytes","internalType":"bytes"},{"name":"proofHeight","type":"tuple","internalType":"struct IICS02ClientMsgs.Height","components":[{"name":"revisionNumber","type":"uint64","internalType":"uint64"},{"name":"revisionHeight","type":"uint64","internalType":"uint64"}]},{"name":"path","type":"bytes[]","internalType":"bytes[]"}]}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"event","name":"RoleAdminChanged","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"previousAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"newAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"AccessControlBadConfirmation","inputs":[]},{"type":"error","name":"AccessControlUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"neededRole","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"BadQuorum","inputs":[{"name":"minRequired","type":"uint8","internalType":"uint8"},{"name":"attestationCount","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"ConsensusTimestampNotFound","inputs":[{"name":"height","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"DuplicateSigner","inputs":[{"name":"signer","type":"address","internalType":"address"}]},{"type":"error","name":"ECDSAInvalidSignature","inputs":[]},{"type":"error","name":"ECDSAInvalidSignatureLength","inputs":[{"name":"length","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"ECDSAInvalidSignatureS","inputs":[{"name":"s","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"EmptyPackets","inputs":[]},{"type":"error","name":"EmptySignatures","inputs":[]},{"type":"error","name":"EmptyValue","inputs":[]},{"type":"error","name":"FeatureNotSupported","inputs":[]},{"type":"error","name":"FrozenClientState","inputs":[]},{"type":"error","name":"HeightMismatch","inputs":[{"name":"expected","type":"uint64","internalType":"uint64"},{"name":"provided","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"InvalidPathLength","inputs":[{"name":"expectedLength","type":"uint256","internalType":"uint256"},{"name":"providedLength","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"InvalidSignatureLength","inputs":[{"name":"signature","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"InvalidState","inputs":[{"name":"height","type":"uint64","internalType":"uint64"},{"name":"timestamp","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"NoAttestors","inputs":[]},{"type":"error","name":"NotMember","inputs":[]},{"type":"error","name":"NotNonMember","inputs":[]},{"type":"error","name":"SignatureInvalid","inputs":[{"name":"signature","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"ThresholdNotMet","inputs":[{"name":"validSigners","type":"uint256","internalType":"uint256"},{"name":"minRequired","type":"uint8","internalType":"uint8"}]},{"type":"error","name":"UnknownSigner","inputs":[{"name":"signer","type":"address","internalType":"address"}]}],"bytecode":{"object":"0x60806040523461032d57611f568038038061001981610349565b928339810160a08282031261032d5781516001600160401b03811161032d57820181601f8201121561032d578051916001600160401b0383116102c8578260051b916020610068818501610349565b8095815201906020829482010192831161032d57602001905b8282106103315750505060208301519260ff841680940361032d576100a860408201610382565b6100c060806100b960608501610382565b930161036e565b9284511561031e57851515806102f2575b855190156102dc575060405195608087016001600160401b038111888210176102c857604052858752602087019081526060604088019360018060401b03169788855201915f835286519060018060401b0382116102c8576801000000000000000082116102c85760015482600155808310610284575b5060015f5260205f205f5b838110610267575050505060ff90511669ff00000000000000000060025493610100600160481b03905160081b169251151560481b169260018060501b0319161717176002555f5b83518110156101fb5760018060a01b0360208260051b8601015116805f52600360205260ff60405f2054166101e957906001915f52600360205260405f208260ff198254161790550161019b565b637010e27960e11b5f5260045260245ffd5b505f84815260046020526040902080546001600160401b0319166001600160401b039092169190911790556001600160a01b03811661024e575061023d61048c565b505b6040516119a7908161050f8239f35b8061025b61026192610396565b5061040c565b5061023f565b82516001600160a01b031681830155602090920191600101610153565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf69081019083015b8181106102bd5750610148565b5f81556001016102b0565b634e487b7160e01b5f52604160045260245ffd5b866365846b7f60e01b5f5260045260245260445ffd5b5084515f19870160ff811161030a5760ff16106100d1565b634e487b7160e01b5f52601160045260245ffd5b6343d0a89360e11b5f5260045ffd5b5f80fd5b6020809161033e8461036e565b815201910190610081565b6040519190601f01601f191682016001600160401b038111838210176102c857604052565b51906001600160a01b038216820361032d57565b51906001600160401b038216820361032d57565b6001600160a01b0381165f9081525f516020611f365f395f51905f52602052604090205460ff16610407576001600160a01b03165f8181525f516020611f365f395f51905f5260205260408120805460ff191660011790553391905f516020611eb65f395f51905f528180a4600190565b505f90565b6001600160a01b0381165f9081525f516020611ed65f395f51905f52602052604090205460ff16610407576001600160a01b03165f8181525f516020611ed65f395f51905f5260205260408120805460ff191660011790553391905f516020611f165f395f51905f52905f516020611eb65f395f51905f529080a4600190565b5f80525f516020611ed65f395f51905f526020525f516020611ef65f395f51905f525460ff1661050a575f8080525f516020611ed65f395f51905f526020525f516020611ef65f395f51905f52805460ff1916600117905533905f516020611f165f395f51905f525f516020611eb65f395f51905f528280a4600190565b5f9056fe6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146106ab575080630bece356146105e8578063248a9ca3146105be5780632f2ff15d1461058f57806336568abe146105335780634d6d9ffb146104865780635832611b146104405780635972185a14610406578063682ed5f01461035157806391d1485414610308578063a217fddf146102ee578063a845a7f314610261578063d547741f1461022b578063ddba65371461015f5763ef913a4b146100c1575f80fd5b3461015b575f60031936011261015b576101576040516020808201526080604082015261014b816100f460c08201611228565b60ff600254818116606085015267ffffffffffffffff8160081c16608085015260481c16151560a0830152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810183528261085e565b604051918291826107cd565b0390f35b5f80fd5b3461015b5761016d36610749565b505060ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475460ff16156101f6575b7fda81d7c2000000000000000000000000000000000000000000000000000000005f5260045ffd5b6101fe611277565b6101ce565b7f928b1233000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461015b5761025f61023c3661079a565b9061025a610255825f525f602052600160405f20015490565b6112ff565b61175e565b005b3461015b575f60031936011261015b5760ff6002541660405161028e8161028781611228565b038261085e565b60405190604082019260408352815180945260206060840192015f945b8086106102c057505082935060208301520390f35b909260208060019273ffffffffffffffffffffffffffffffffffffffff8751168152019401950194906102ab565b3461015b575f60031936011261015b5760206040515f8152f35b3461015b576103163661079a565b905f525f60205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060ff60405f2054166040519015158152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811161015b5760a0600319823603011261015b5760ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475490916103f19160ff16156103f9575b6004016110cb565b604051908152f35b610401611277565b6103e9565b3461015b575f60031936011261015b5760206040517fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b8152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811680910361015b575f526004602052602067ffffffffffffffff60405f205416604051908152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811161015b576080600319823603011261015b5760ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475490916103f19160ff1615610526575b600401610ef5565b61052e611277565b61051e565b3461015b576105413661079a565b3373ffffffffffffffffffffffffffffffffffffffff8216036105675761025f9161175e565b7f6697b232000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461015b5761025f6105a03661079a565b906105b9610255825f525f602052600160405f20015490565b61168c565b3461015b57602060031936011261015b5760206103f16004355f525f602052600160405f20015490565b3461015b576105f636610749565b60ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475461065e929060ff161561069e57610a3c565b6040516003821015610671576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6106a6611277565b610a3c565b3461015b57602060031936011261015b57600435907fffffffff00000000000000000000000000000000000000000000000000000000821680920361015b57817f7965db0b000000000000000000000000000000000000000000000000000000006020931490811561071f575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483610718565b90602060031983011261015b5760043567ffffffffffffffff811161015b578260238201121561015b5780600401359267ffffffffffffffff841161015b576024848301011161015b576024019190565b600319604091011261015b576004359060243573ffffffffffffffffffffffffffffffffffffffff8116810361015b5790565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602060409481855280519182918282880152018686015e5f8582860101520116010190565b6040810190811067ffffffffffffffff82111761083157604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761083157604052565b92919267ffffffffffffffff821161083157604051916108e760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461085e565b82948184528183011161015b578281602093845f960137010152565b9080601f8301121561015b5781602061091e9335910161089f565b90565b67ffffffffffffffff81116108315760051b60200190565b60208183031261015b5780359067ffffffffffffffff821161015b57019060408282031261015b576040519161096e83610815565b803567ffffffffffffffff811161015b578261098b918301610903565b835260208101359067ffffffffffffffff821161015b57019080601f8301121561015b5781356109ba81610921565b926109c8604051948561085e565b81845260208085019260051b8201019183831161015b5760208201905b8382106109f9575050505050602082015290565b813567ffffffffffffffff811161015b57602091610a1c87848094880101610903565b8152019101906109e5565b519067ffffffffffffffff8216820361015b57565b610a4891810190610939565b60205f818351604051918183925191829101835e8101838152039060025afa15610c915760205f8051604051838101917f01000000000000000000000000000000000000000000000000000000000000008352602182015260218152610aaf60418261085e565b604051918291518091835e8101838152039060025afa15610c9157610ada5f516020830151906113eb565b5160408180518101031261015b57604051610af481610815565b610b0f6040610b0560208501610a27565b9384845201610a27565b67ffffffffffffffff60208301938285521680151580610c7e575b15610c4457505067ffffffffffffffff8151165f52600460205267ffffffffffffffff60405f20541680610bf3575067ffffffffffffffff9051918183169260025490838260081c168511610bb6575b50505116905f52600460205260405f20907fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008254161790555f90565b68ffffffffffffffff007fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000ff9160081b169116176002555f80610b7a565b9167ffffffffffffffff9150511603610c0b57600290565b69010000000000000000007fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff6002541617600255600190565b67ffffffffffffffff92507fdcbc5460000000000000000000000000000000000000000000000000000000005f526004521660245260445ffd5b5067ffffffffffffffff82161515610b2a565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561015b570180359067ffffffffffffffff821161015b57602001918160051b3603831361015b57565b3567ffffffffffffffff8116810361015b5790565b15610d0d5750565b67ffffffffffffffff907ff7caaa5c000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561015b570180359067ffffffffffffffff821161015b5760200191813603831361015b57565b60208183031261015b5780519067ffffffffffffffff821161015b57019060408282031261015b5760405191610dc983610815565b610dd281610a27565b835260208101519067ffffffffffffffff821161015b570181601f8201121561015b57805190610e0182610921565b92610e0f604051948561085e565b82845260208085019360061b8301019181831161015b57602001925b828410610e3e5750505050602082015290565b60408483031261015b5760206040918251610e5881610815565b865181528287015183820152815201930192610e2b565b15610e78575050565b9067ffffffffffffffff80927fc7441689000000000000000000000000000000000000000000000000000000005f52166004521660245260445ffd5b8051821015610ec85760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b6060810190610f048282610c9c565b90506001610f128484610c9c565b9290500361109b5750610fb8610f2a60408301610cf0565b9267ffffffffffffffff841692835f526004602052610fb367ffffffffffffffff60405f20541694610f5e87871515610d05565b610f9e610f76610f6e8580610d43565b810190610939565b610f8e610f838251611365565b6020830151906113eb565b5160208082518301019101610d94565b9667ffffffffffffffff885116918214610e6f565b610c9c565b15610ec857610fd3610fcc82602093610d43565b369161089f565b818151910120920180515115611073575f5b8151805182101561104b57610ffb828692610eb4565b51511461100a57600101610fe5565b60209293506110199151610eb4565b5101516110235790565b7f9a960b4f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f291fc442000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f1e6c84da000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f88b3170e000000000000000000000000000000000000000000000000000000005f52600160045260245260445ffd5b608081016110d98183610d43565b9050156112005760608201906110ef8284610c9c565b905060016110fd8486610c9c565b9290500361109b575061111260408401610cf0565b9061117467ffffffffffffffff831693845f52600460205261116e67ffffffffffffffff60405f2054169561114986881515610d05565b611159610f76610f6e8a80610d43565b9567ffffffffffffffff875116918214610e6f565b85610c9c565b15610ec85760209161118c610fcc8361119894610d43565b83815191012095610d43565b908092918101031261015b576020903591019081515115611073575f5b8251805182101561104b576111cb828792610eb4565b515114806111e8575b6111e0576001016111b5565b505050905090565b508160206111f7838651610eb4565b510151146111d4565b7f1208b21b000000000000000000000000000000000000000000000000000000005f5260045ffd5b602060015491828152019060015f5260205f20905f5b81811061124b5750505090565b825473ffffffffffffffffffffffffffffffffffffffff1684526020909301926001928301920161123e565b335f9081527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e0602052604090205460ff16156112af57565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004527fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b60245260445ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260ff60405f205416156113365750565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b5f60208092604051918183925191829101835e8101838152039060025afa15610c915760205f8051604051838101917f020000000000000000000000000000000000000000000000000000000000000083526021820152602181526113cb60418261085e565b604051918291518091835e8101838152039060025afa15610c91575f5190565b91909182511561166457825160ff60025416907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019060ff82116116375760ff8651921610156116095750508251927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061147e61146886610921565b95611476604051978861085e565b808752610921565b013660208601375f5b81518110156116025761149a8183610eb4565b5160418151036115cc5773ffffffffffffffffffffffffffffffffffffffff6114cf6114c68387611826565b90929192611860565b169081156115925750805f52600360205260ff60405f20541615611567575f5b82811061150c5750906001916115058288610eb4565b5201611487565b8173ffffffffffffffffffffffffffffffffffffffff61152c838a610eb4565b51161461153b576001016114ef565b507fe021c4f2000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7fd7e8a2c2000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6115c8906040519182917f9e3c1b3d000000000000000000000000000000000000000000000000000000008352600483016107cd565b0390fd5b6115c8906040519182917f2ee17a3d000000000000000000000000000000000000000000000000000000008352600483016107cd565b5050509050565b7f378b0805000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f301b38b6000000000000000000000000000000000000000000000000000000005f5260045ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f205416155f1461175857805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f2054165f1461175857805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b81519190604183036118565761184f9250602082015190606060408401519301515f1a9061190b565b9192909190565b50505f9160029190565b60048110156106715780611872575050565b600181036118a2577ff645eedf000000000000000000000000000000000000000000000000000000005f5260045ffd5b600281036118d657507ffce698f7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6003146118e05750565b7fd78bce0c000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161198f579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa15610c91575f5173ffffffffffffffffffffffffffffffffffffffff81161561198557905f905f90565b505f906001905f90565b5050505f916003919056fea164736f6c634300081c000a2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac47bd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10bad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5","sourceMap":"815:11749:29:-:0;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1;;;;;815:11749:29;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;815:11749:29;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;2648:28;815:11749;;2723:19;;;:69;;;815:11749;;;;;;;-1:-1:-1;815:11749:29;;;;;;-1:-1:-1;;;;;815:11749:29;;;;;;;;;;;;;;2893:217;;815:11749;;;;;2893:217;;815:11749;;;;;;;;;;;2893:217;815:11749;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;-1:-1:-1;815:11749:29;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;3171:3:29;815:11749;;3141:28;;;;;815:11749;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;3251:11;815:11749;;;;-1:-1:-1;815:11749:29;;;;;;;;-1:-1:-1;815:11749:29;3251:11;815:11749;;;-1:-1:-1;815:11749:29;;;;;;;;;;;3126:13;;815:11749;;;;-1:-1:-1;815:11749:29;;;;-1:-1:-1;815:11749:29;3141:28;-1:-1:-1;;815:11749:29;;;3372:27;815:11749;;;;;;;-1:-1:-1;;;;;;815:11749:29;-1:-1:-1;;;;;815:11749:29;;;;;;;;;-1:-1:-1;;;;;815:11749:29;;;;3496:44;;;:::i;:::-;;3451:249;815:11749;;;;;;;;;3451:249;3587:43;;3644:45;3587:43;;:::i;:::-;;3644:45;:::i;:::-;;3451:249;;815:11749;;;-1:-1:-1;;;;;815:11749:29;;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;;;;-1:-1:-1;815:11749:29;;;;;;-1:-1:-1;815:11749:29;2723:69;-1:-1:-1;815:11749:29;;-1:-1:-1;;815:11749:29;;;;;;;;;-1:-1:-1;2723:69:29;;815:11749;;;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;-1:-1:-1;815:11749:29;;-1:-1:-1;815:11749:29;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;-1:-1:-1;;;;;815:11749:29;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;815:11749:29;;;;;;:::o;:::-;;;-1:-1:-1;;;;;815:11749:29;;;;;;:::o;6155:316:85:-;-1:-1:-1;;;;;815:11749:29;;2675:1;815:11749;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;;;;;;;;;-1:-1:-1;;;;;815:11749:29;2675:1;815:11749;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;;;;;;-1:-1:-1;;815:11749:29;;;;;735:10:113;;815:11749:29;-1:-1:-1;;;;;;;;;;;2675:1:29;;6346:40:85;6323:4;6400:11;:::o;6248:217::-;6442:12;2675:1:29;6442:12:85;:::o;6155:316::-;-1:-1:-1;;;;;815:11749:29;;;;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;;;;;;;;;-1:-1:-1;;;;;815:11749:29;;;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;;;;;;-1:-1:-1;;815:11749:29;;;;;735:10:113;;815:11749:29;-1:-1:-1;;;;;;;;;;;1507:33:29;-1:-1:-1;;;;;;;;;;;6346:40:85;815:11749:29;6346:40:85;6323:4;6400:11;:::o;6155:316::-;815:11749:29;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;-1:-1:-1;;;;;;;;;;;815:11749:29;;;;;;;;;-1:-1:-1;;;;;;;;;;;815:11749:29;;-1:-1:-1;;;;;;;;;;;815:11749:29;;-1:-1:-1;;815:11749:29;6323:4:85;815:11749:29;;;735:10:113;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;815:11749:29;;6346:40:85;6323:4;6400:11;:::o;6248:217::-;815:11749:29;6442:12:85;:::o","linkReferences":{}},"deployedBytecode":{"object":"0x6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146106ab575080630bece356146105e8578063248a9ca3146105be5780632f2ff15d1461058f57806336568abe146105335780634d6d9ffb146104865780635832611b146104405780635972185a14610406578063682ed5f01461035157806391d1485414610308578063a217fddf146102ee578063a845a7f314610261578063d547741f1461022b578063ddba65371461015f5763ef913a4b146100c1575f80fd5b3461015b575f60031936011261015b576101576040516020808201526080604082015261014b816100f460c08201611228565b60ff600254818116606085015267ffffffffffffffff8160081c16608085015260481c16151560a0830152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810183528261085e565b604051918291826107cd565b0390f35b5f80fd5b3461015b5761016d36610749565b505060ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475460ff16156101f6575b7fda81d7c2000000000000000000000000000000000000000000000000000000005f5260045ffd5b6101fe611277565b6101ce565b7f928b1233000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461015b5761025f61023c3661079a565b9061025a610255825f525f602052600160405f20015490565b6112ff565b61175e565b005b3461015b575f60031936011261015b5760ff6002541660405161028e8161028781611228565b038261085e565b60405190604082019260408352815180945260206060840192015f945b8086106102c057505082935060208301520390f35b909260208060019273ffffffffffffffffffffffffffffffffffffffff8751168152019401950194906102ab565b3461015b575f60031936011261015b5760206040515f8152f35b3461015b576103163661079a565b905f525f60205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060ff60405f2054166040519015158152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811161015b5760a0600319823603011261015b5760ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475490916103f19160ff16156103f9575b6004016110cb565b604051908152f35b610401611277565b6103e9565b3461015b575f60031936011261015b5760206040517fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b8152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811680910361015b575f526004602052602067ffffffffffffffff60405f205416604051908152f35b3461015b57602060031936011261015b5760043567ffffffffffffffff811161015b576080600319823603011261015b5760ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475490916103f19160ff1615610526575b600401610ef5565b61052e611277565b61051e565b3461015b576105413661079a565b3373ffffffffffffffffffffffffffffffffffffffff8216036105675761025f9161175e565b7f6697b232000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461015b5761025f6105a03661079a565b906105b9610255825f525f602052600160405f20015490565b61168c565b3461015b57602060031936011261015b5760206103f16004355f525f602052600160405f20015490565b3461015b576105f636610749565b60ff60025460481c16610203575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475461065e929060ff161561069e57610a3c565b6040516003821015610671576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6106a6611277565b610a3c565b3461015b57602060031936011261015b57600435907fffffffff00000000000000000000000000000000000000000000000000000000821680920361015b57817f7965db0b000000000000000000000000000000000000000000000000000000006020931490811561071f575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483610718565b90602060031983011261015b5760043567ffffffffffffffff811161015b578260238201121561015b5780600401359267ffffffffffffffff841161015b576024848301011161015b576024019190565b600319604091011261015b576004359060243573ffffffffffffffffffffffffffffffffffffffff8116810361015b5790565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602060409481855280519182918282880152018686015e5f8582860101520116010190565b6040810190811067ffffffffffffffff82111761083157604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761083157604052565b92919267ffffffffffffffff821161083157604051916108e760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461085e565b82948184528183011161015b578281602093845f960137010152565b9080601f8301121561015b5781602061091e9335910161089f565b90565b67ffffffffffffffff81116108315760051b60200190565b60208183031261015b5780359067ffffffffffffffff821161015b57019060408282031261015b576040519161096e83610815565b803567ffffffffffffffff811161015b578261098b918301610903565b835260208101359067ffffffffffffffff821161015b57019080601f8301121561015b5781356109ba81610921565b926109c8604051948561085e565b81845260208085019260051b8201019183831161015b5760208201905b8382106109f9575050505050602082015290565b813567ffffffffffffffff811161015b57602091610a1c87848094880101610903565b8152019101906109e5565b519067ffffffffffffffff8216820361015b57565b610a4891810190610939565b60205f818351604051918183925191829101835e8101838152039060025afa15610c915760205f8051604051838101917f01000000000000000000000000000000000000000000000000000000000000008352602182015260218152610aaf60418261085e565b604051918291518091835e8101838152039060025afa15610c9157610ada5f516020830151906113eb565b5160408180518101031261015b57604051610af481610815565b610b0f6040610b0560208501610a27565b9384845201610a27565b67ffffffffffffffff60208301938285521680151580610c7e575b15610c4457505067ffffffffffffffff8151165f52600460205267ffffffffffffffff60405f20541680610bf3575067ffffffffffffffff9051918183169260025490838260081c168511610bb6575b50505116905f52600460205260405f20907fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000008254161790555f90565b68ffffffffffffffff007fffffffffffffffffffffffffffffffffffffffffffffff0000000000000000ff9160081b169116176002555f80610b7a565b9167ffffffffffffffff9150511603610c0b57600290565b69010000000000000000007fffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffffff6002541617600255600190565b67ffffffffffffffff92507fdcbc5460000000000000000000000000000000000000000000000000000000005f526004521660245260445ffd5b5067ffffffffffffffff82161515610b2a565b6040513d5f823e3d90fd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561015b570180359067ffffffffffffffff821161015b57602001918160051b3603831361015b57565b3567ffffffffffffffff8116810361015b5790565b15610d0d5750565b67ffffffffffffffff907ff7caaa5c000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561015b570180359067ffffffffffffffff821161015b5760200191813603831361015b57565b60208183031261015b5780519067ffffffffffffffff821161015b57019060408282031261015b5760405191610dc983610815565b610dd281610a27565b835260208101519067ffffffffffffffff821161015b570181601f8201121561015b57805190610e0182610921565b92610e0f604051948561085e565b82845260208085019360061b8301019181831161015b57602001925b828410610e3e5750505050602082015290565b60408483031261015b5760206040918251610e5881610815565b865181528287015183820152815201930192610e2b565b15610e78575050565b9067ffffffffffffffff80927fc7441689000000000000000000000000000000000000000000000000000000005f52166004521660245260445ffd5b8051821015610ec85760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b6060810190610f048282610c9c565b90506001610f128484610c9c565b9290500361109b5750610fb8610f2a60408301610cf0565b9267ffffffffffffffff841692835f526004602052610fb367ffffffffffffffff60405f20541694610f5e87871515610d05565b610f9e610f76610f6e8580610d43565b810190610939565b610f8e610f838251611365565b6020830151906113eb565b5160208082518301019101610d94565b9667ffffffffffffffff885116918214610e6f565b610c9c565b15610ec857610fd3610fcc82602093610d43565b369161089f565b818151910120920180515115611073575f5b8151805182101561104b57610ffb828692610eb4565b51511461100a57600101610fe5565b60209293506110199151610eb4565b5101516110235790565b7f9a960b4f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f291fc442000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f1e6c84da000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f88b3170e000000000000000000000000000000000000000000000000000000005f52600160045260245260445ffd5b608081016110d98183610d43565b9050156112005760608201906110ef8284610c9c565b905060016110fd8486610c9c565b9290500361109b575061111260408401610cf0565b9061117467ffffffffffffffff831693845f52600460205261116e67ffffffffffffffff60405f2054169561114986881515610d05565b611159610f76610f6e8a80610d43565b9567ffffffffffffffff875116918214610e6f565b85610c9c565b15610ec85760209161118c610fcc8361119894610d43565b83815191012095610d43565b908092918101031261015b576020903591019081515115611073575f5b8251805182101561104b576111cb828792610eb4565b515114806111e8575b6111e0576001016111b5565b505050905090565b508160206111f7838651610eb4565b510151146111d4565b7f1208b21b000000000000000000000000000000000000000000000000000000005f5260045ffd5b602060015491828152019060015f5260205f20905f5b81811061124b5750505090565b825473ffffffffffffffffffffffffffffffffffffffff1684526020909301926001928301920161123e565b335f9081527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e0602052604090205460ff16156112af57565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004527fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b60245260445ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260ff60405f205416156113365750565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b5f60208092604051918183925191829101835e8101838152039060025afa15610c915760205f8051604051838101917f020000000000000000000000000000000000000000000000000000000000000083526021820152602181526113cb60418261085e565b604051918291518091835e8101838152039060025afa15610c91575f5190565b91909182511561166457825160ff60025416907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82019060ff82116116375760ff8651921610156116095750508251927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061147e61146886610921565b95611476604051978861085e565b808752610921565b013660208601375f5b81518110156116025761149a8183610eb4565b5160418151036115cc5773ffffffffffffffffffffffffffffffffffffffff6114cf6114c68387611826565b90929192611860565b169081156115925750805f52600360205260ff60405f20541615611567575f5b82811061150c5750906001916115058288610eb4565b5201611487565b8173ffffffffffffffffffffffffffffffffffffffff61152c838a610eb4565b51161461153b576001016114ef565b507fe021c4f2000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7fd7e8a2c2000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6115c8906040519182917f9e3c1b3d000000000000000000000000000000000000000000000000000000008352600483016107cd565b0390fd5b6115c8906040519182917f2ee17a3d000000000000000000000000000000000000000000000000000000008352600483016107cd565b5050509050565b7f378b0805000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f301b38b6000000000000000000000000000000000000000000000000000000005f5260045ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f205416155f1461175857805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f2054165f1461175857805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b81519190604183036118565761184f9250602082015190606060408401519301515f1a9061190b565b9192909190565b50505f9160029190565b60048110156106715780611872575050565b600181036118a2577ff645eedf000000000000000000000000000000000000000000000000000000005f5260045ffd5b600281036118d657507ffce698f7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6003146118e05750565b7fd78bce0c000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841161198f579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa15610c91575f5173ffffffffffffffffffffffffffffffffffffffff81161561198557905f905f90565b505f906001905f90565b5050505f916003919056fea164736f6c634300081c000a","sourceMap":"815:11749:29:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;3825:23;;;;815:11749;;;;;;3825:23;815:11749;;;;;;:::i;:::-;;;;;;;;;;;;1787:4;;;815:11749;;;;;;;;;;;;;;3825:23;;;;;;;;:::i;:::-;815:11749;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;12225:20;815:11749;;;;;;;;;;;;;;;;12444:42;12440:105;;815:11749;9710:21;815:11749;9710:21;815:11749;;9710:21;12440:105;12513:20;;:::i;:::-;12440:105;;815:11749;;;;;;;;;;;4723:26:85;815:11749:29;;;:::i;:::-;4693:18:85;2484:4;4693:18;;3877:6;815:11749:29;3877:6:85;815:11749:29;;3877:22:85;815:11749:29;3877:6:85;815:11749:29;3877:22:85;815:11749:29;3786:120:85;;4693:18;2484:4;:::i;:::-;4723:26;:::i;:::-;815:11749:29;;;;;;-1:-1:-1;;815:11749:29;;;;;;4065:27;815:11749;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4034:11;815:11749;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;2930:29:85;815:11749:29;-1:-1:-1;815:11749:29;;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;12225:20;815:11749;;;;;;;;;;;;;;;;;;5841:1853;;815:11749;;12444:42;12440:105;;815:11749;;;5841:1853;:::i;:::-;815:11749;;;;;;12440:105;12513:20;;:::i;:::-;12440:105;;815:11749;;;;;-1:-1:-1;;815:11749:29;;;;;;;;1507:33;815:11749;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;;;;1787:4;815:11749;;1787:4;815:11749;;;;1787:4;;815:11749;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;-1:-1:-1;;815:11749:29;;;;;;;;12225:20;815:11749;;;;;;;;;;;;;;;;;;7733:1799;;815:11749;;12444:42;12440:105;;815:11749;;;7733:1799;:::i;12440:105::-;12513:20;;:::i;:::-;12440:105;;815:11749;;;;;;;:::i;:::-;735:10:113;815:11749:29;;;5397:34:85;5393:102;;5505:37;;;:::i;5393:102::-;5454:30;815:11749:29;5454:30:85;815:11749:29;;5454:30:85;815:11749:29;;;;4306:25:85;815:11749:29;;;:::i;:::-;4276:18:85;2484:4;4276:18;;3877:6;815:11749:29;3877:6:85;815:11749:29;;3877:22:85;815:11749:29;3877:6:85;815:11749:29;3877:22:85;815:11749:29;3786:120:85;;2484:4;4306:25;:::i;815:11749:29:-;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;3877:6:85;815:11749:29;3877:6:85;815:11749:29;;3877:22:85;815:11749:29;3877:6:85;815:11749:29;3877:22:85;815:11749:29;3786:120:85;;815:11749:29;;;;;;;:::i;:::-;;12225:20;815:11749;;;;;;;;;;;;;;4341:1461;;815:11749;;;12444:42;12440:105;;4341:1461;:::i;:::-;815:11749;;;;;;;;;;;;;;;;;;;;;;;12440:105;12513:20;;:::i;:::-;4341:1461;:::i;815:11749::-;;;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;;;;2649:47:85;2664:32;815:11749:29;2649:47:85;;:87;;;;;815:11749:29;;;;;;;2649:87:85;844:25:122;829:40;;;2649:87:85;;;815:11749:29;;;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;815:11749:29;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;1787:4;;815:11749;;;;;;;;1787:4::o;4341:1461::-;4562:58;4341:1461;4562:58;;;;:::i;:::-;815:11749;-1:-1:-1;4668:21:29;;;815:11749;;;;;;;;;;;;;;;;;;10125:12;;;;;;;;815:11749;-1:-1:-1;10125:12:29;;815:11749;;10099:39;;;815:11749;;;;;;;;;10099:39;;;;;;:::i;:::-;815:11749;;;;;;;;;;;;;;;10092:47;;10125:12;10092:47;;;;;4759:16;-1:-1:-1;10092:47:29;815:11749;4759:16;;;;;:::i;:::-;4859:21;815:11749;;;;4848:70;;1787:4;;;;815:11749;;;;;:::i;:::-;1787:4;815:11749;1787:4;815:11749;4848:70;;1787:4;:::i;:::-;;;;;;;:::i;:::-;815:11749;;1787:4;;;;;;815:11749;4937:16;;;:39;;;4341:1461;1787:4;;;;;815:11749;1787:4;;815:11749;-1:-1:-1;1787:4:29;5227:27;815:11749;1787:4;815:11749;;-1:-1:-1;1787:4:29;;815:11749;5282:22;5278:276;;1787:4;815:11749;1787:4;;815:11749;;;;1787:4;10125:12;1787:4;;;;;;815:11749;5568:39;;5564:109;;4341:1461;1787:4;;;815:11749;1787:4;-1:-1:-1;1787:4:29;5227:27;815:11749;1787:4;815:11749;-1:-1:-1;1787:4:29;;;;;;;;;-1:-1:-1;4341:1461:29;:::o;5564:109::-;1787:4;;;;;;;;;10125:12;1787:4;5564:109;;;;5278:276;1787:4;815:11749;1787:4;;;815:11749;5324:36;5320:169;;10125:12;5502:41;:::o;5320:169::-;1787:4;;10125:12;1787:4;;;10125:12;1787:4;;5425:49;:::o;1787:4::-;815:11749;1787:4;;;-1:-1:-1;1787:4:29;;815:11749;;1787:4;815:11749;1787:4;-1:-1:-1;1787:4:29;4937:39;815:11749;;;;4957:19;;4937:39;;10092:47;815:11749;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1910:4::-;;;;;;;;;;;;;;;;;;;;;;;;;;815:11749;;;;;:::i;:::-;1787:4;;;:::i;:::-;1910;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;815:11749;;1910:4;815:11749;;;;:::i;:::-;1910:4;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;815:11749;;;;;;:::i;:::-;1910:4;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;815:11749;1910:4;;;;;815:11749;1910:4;815:11749;;1787:4;815:11749;1787:4;1910;;;815:11749;;1910:4;;;;;;;;;;;;:::o;:::-;;;;;;;;;;7733:1799;7940:9;;;;;;;;:::i;:::-;7984;;7960:1;7984:9;;;;:::i;:::-;7940:21;;;;815:11749;;8104:31;9118:9;8104:31;;;;;:::i;:::-;815:11749;;;;1787:4;;-1:-1:-1;1787:4:29;8157:27;8104:16;1787:4;8893:103;815:11749;8104:31;-1:-1:-1;1787:4:29;;815:11749;8215:7;8207:57;8215:7;;;;8207:57;:::i;:::-;8739:71;8324:59;8335:10;;;;:::i;:::-;8324:59;;;;:::i;:::-;8522:16;8410:67;8430:21;;8410:67;:::i;:::-;8104:16;8522;;;;;:::i;:::-;8750:21;8104:16;815:11749;;;8739:71;;;;;;:::i;:::-;1787:4;815:11749;1787:4;;815:11749;8901:39;;;8893:103;:::i;:::-;9118:9;:::i;:::-;1910:4;;;;;;8104:16;1910:4;;:::i;:::-;;;;:::i;:::-;815:11749;;;;;9108:23;9149:25;;;;815:11749;9149:36;1910:4;;-1:-1:-1;9270:3:29;9236:25;;815:11749;;9232:36;;;;;9293:28;;;;;:::i;:::-;;1910:4;9293:45;9289:198;;7960:1;1910:4;9217:13;;9289:198;8104:16;9366:25;;;:28;:25;;:28;:::i;:::-;;:39;1910:4;;;9454:18;:::o;1910:4::-;;-1:-1:-1;1910:4:29;8157:27;-1:-1:-1;1910:4:29;9232:36;9514:11;-1:-1:-1;9514:11:29;8157:27;-1:-1:-1;9514:11:29;1910:4;;-1:-1:-1;1910:4:29;8157:27;-1:-1:-1;1910:4:29;815:11749;;;;7960:1;815:11749;;;;;;;5841:1853;6042:10;;;;;;;:::i;:::-;:22;;;815:11749;;6097:9;;;;;;;;:::i;:::-;6141;;6117:1;6141:9;;;;:::i;:::-;6097:21;;;;815:11749;;6261:31;;;;;;:::i;:::-;815:11749;7265:9;815:11749;;;1787:4;;6063:1;1787:4;6314:27;6261:16;1787:4;7050:103;815:11749;6261:31;6063:1;1787:4;;815:11749;6372:7;6364:57;6372:7;;;;6364:57;:::i;:::-;6896:71;6481:59;6492:10;;;;:::i;6896:71::-;1787:4;815:11749;1787:4;;815:11749;7058:39;;;7050:103;:::i;:::-;7265:9;;:::i;:::-;1910:4;;;6261:16;1910:4;;;;7315:10;1910:4;;:::i;:::-;815:11749;;;;;7255:23;7315:10;;:::i;:::-;7304:33;;;;;;815:11749;;;;6261:16;815:11749;;7355:25;;;;;815:11749;7355:36;1910:4;;6063:1;7476:3;7442:25;;815:11749;;7438:36;;;;;7499:28;;;;;:::i;:::-;;1910:4;7499:45;:97;;;7476:3;7495:154;;6117:1;1910:4;7423:13;;7495:154;7616:18;;;;;;:::o;7499:97::-;7548:25;;6261:16;7548:28;:25;;;:28;:::i;:::-;;:39;1910:4;7548:48;7499:97;;815:11749;;6063:1;815:11749;;6063:1;815:11749;;;4034:11;815:11749;;;;;;;4034:11;-1:-1:-1;815:11749:29;;-1:-1:-1;815:11749:29;;-1:-1:-1;815:11749:29;;;;;;;;;;:::o;:::-;;;;;;;;;;;;4034:11;815:11749;;;;;;;3175:103:85;735:10:113;2930:6:85;815:11749:29;;;;;;;;;;;;3495:23:85;3491:108;;3175:103::o;3491:108::-;3541:47;2930:6;3541:47;735:10:113;3541:47:85;815:11749:29;1507:33;815:11749;;;2930:6:85;3541:47;3175:103;815:11749:29;2930:6:85;815:11749:29;2930:6:85;815:11749:29;;;2930:6:85;815:11749:29;;735:10:113;815:11749:29;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;3495:23:85;3491:108;;3175:103;:::o;3491:108::-;3541:47;2930:6;3541:47;735:10:113;3541:47:85;815:11749:29;;;;2930:6:85;3541:47;9980:166:29;-1:-1:-1;815:11749:29;9980:166;;815:11749;;;;;;;;;;;;;;;;;;10125:12;;;;;;;;815:11749;-1:-1:-1;10125:12:29;;815:11749;;10099:39;;;815:11749;;;;;;;;;10099:39;;;;;;:::i;:::-;815:11749;;;;;;;;;;;;;;;10092:47;;10125:12;10092:47;;;;;-1:-1:-1;10092:47:29;9980:166;:::o;10562:773::-;;;;815:11749;;10672:21;815:11749;;;;;10765:27;815:11749;;;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;10692:1;11003:3;815:11749;;10980:21;;;;;11041:13;;;;:::i;:::-;;1660:2;815:11749;;11811:42;1660:2;;815:11749;3927:8:121;3871:27;;;;:::i;:::-;3927:8;;;;;:::i;:::-;815:11749:29;11970:23;;;1660:2;;815:11749;;10692:1;815:11749;12041:11;815:11749;;;;10692:1;815:11749;;;1660:2;;;10692:1;11184:5;;;;;;11299:19;;10765:11;11299:19;;;;;:::i;:::-;815:11749;1910:4;10965:13;;11191:3;11222:7;815:11749;11222:7;;;;:::i;:::-;815:11749;;11222:20;815:11749;;10765:11;1910:4;11169:13;;815:11749;;;10692:1;815:11749;;;;10692:1;815:11749;1660:2;;10692:1;1660:2;;815:11749;;10692:1;1660:2;;;815:11749;;;1660:2;;;;;;;;;;:::i;:::-;;;;;;815:11749;;;1660:2;;;;;;;;;;:::i;10980:21::-;;;;;;10562:773::o;815:11749::-;;10692:1;815:11749;;;;;;10692:1;815:11749;;;10692:1;815:11749;;;;;10692:1;815:11749;;;10692:1;815:11749;;10692:1;815:11749;6155:316:85;815:11749:29;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;6252:23:85;6248:217;815:11749:29;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;-1:-1:-1;815:11749:29;6323:4:85;815:11749:29;;;;;;;;735:10:113;815:11749:29;;6346:40:85;;815:11749:29;6346:40:85;;6323:4;6400:11;:::o;6248:217::-;6442:12;;815:11749:29;6442:12:85;:::o;6708:317::-;815:11749:29;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;;-1:-1:-1;815:11749:29;;;6802:217:85;815:11749:29;;;;;;;;;;;;;;;-1:-1:-1;815:11749:29;;;;-1:-1:-1;815:11749:29;;;;;;;;735:10:113;815:11749:29;;6900:40:85;;815:11749:29;6900:40:85;;815:11749:29;6954:11:85;:::o;2129:778:121:-;815:11749:29;;;2129:778:121;2319:2;2299:22;;2319:2;;2751:25;2535:196;;;;;;;;;;;;;;;-1:-1:-1;2535:196:121;2751:25;;:::i;:::-;2744:32;;;;;:::o;2295:606::-;2807:83;;2823:1;2807:83;2827:35;2807:83;;:::o;7280:532::-;815:11749:29;;;;;;7366:29:121;;;7411:7;;:::o;7362:444::-;815:11749:29;7462:38:121;;815:11749:29;;7523:23:121;7375:20;7523:23;815:11749:29;7375:20:121;7523:23;7458:348;7576:35;7567:44;;7576:35;;7634:46;;7375:20;7634:46;815:11749:29;;;7375:20:121;7634:46;7563:243;7710:30;7701:39;7697:109;;7563:243;7280:532::o;7697:109::-;7763:32;7375:20;7763:32;815:11749:29;;;7375:20:121;7763:32;5203:1551;;;6283:66;6270:79;;6266:164;;815:11749:29;;;;;;-1:-1:-1;815:11749:29;;;;;;;;;;;;;;;;;;;6541:24:121;;;;;;;;;-1:-1:-1;6541:24:121;815:11749:29;;;6579:20:121;6575:113;;6698:49;-1:-1:-1;6698:49:121;-1:-1:-1;5203:1551:121;:::o;6575:113::-;6615:62;-1:-1:-1;6615:62:121;6541:24;6615:62;-1:-1:-1;6615:62:121;:::o;6266:164::-;6365:54;;;6381:1;6365:54;6385:30;6365:54;;:::o","linkReferences":{}},"methodIdentifiers":{"DEFAULT_ADMIN_ROLE()":"a217fddf","PROOF_SUBMITTER_ROLE()":"5972185a","getAttestationSet()":"a845a7f3","getClientState()":"ef913a4b","getConsensusTimestamp(uint64)":"5832611b","getRoleAdmin(bytes32)":"248a9ca3","grantRole(bytes32,address)":"2f2ff15d","hasRole(bytes32,address)":"91d14854","misbehaviour(bytes)":"ddba6537","renounceRole(bytes32,address)":"36568abe","revokeRole(bytes32,address)":"d547741f","supportsInterface(bytes4)":"01ffc9a7","updateClient(bytes)":"0bece356","verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":"682ed5f0","verifyNonMembership((bytes,(uint64,uint64),bytes[]))":"4d6d9ffb"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"attestorAddresses\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"minRequiredSigs\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"initialHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"initialTimestampSeconds\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"roleManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"minRequired\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"attestationCount\",\"type\":\"uint256\"}],\"name\":\"BadQuorum\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"height\",\"type\":\"uint64\"}],\"name\":\"ConsensusTimestampNotFound\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"DuplicateSigner\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ECDSAInvalidSignature\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"ECDSAInvalidSignatureLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"ECDSAInvalidSignatureS\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyPackets\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptySignatures\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyValue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FeatureNotSupported\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FrozenClientState\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"expected\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"provided\",\"type\":\"uint64\"}],\"name\":\"HeightMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expectedLength\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"providedLength\",\"type\":\"uint256\"}],\"name\":\"InvalidPathLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"InvalidSignatureLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"height\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"timestamp\",\"type\":\"uint64\"}],\"name\":\"InvalidState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NoAttestors\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotMember\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"NotNonMember\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"name\":\"SignatureInvalid\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"validSigners\",\"type\":\"uint256\"},{\"internalType\":\"uint8\",\"name\":\"minRequired\",\"type\":\"uint8\"}],\"name\":\"ThresholdNotMet\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer\",\"type\":\"address\"}],\"name\":\"UnknownSigner\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PROOF_SUBMITTER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getAttestationSet\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"attestorAddresses\",\"type\":\"address[]\"},{\"internalType\":\"uint8\",\"name\":\"minRequiredSigs\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getClientState\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"name\":\"getConsensusTimestamp\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"name\":\"misbehaviour\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callerConfirmation\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"updateMsg\",\"type\":\"bytes\"}],\"name\":\"updateClient\",\"outputs\":[{\"internalType\":\"enum ILightClientMsgs.UpdateResult\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"revisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"internalType\":\"struct IICS02ClientMsgs.Height\",\"name\":\"proofHeight\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"value\",\"type\":\"bytes\"}],\"internalType\":\"struct ILightClientMsgs.MsgVerifyMembership\",\"name\":\"msg_\",\"type\":\"tuple\"}],\"name\":\"verifyMembership\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"revisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"internalType\":\"struct IICS02ClientMsgs.Height\",\"name\":\"proofHeight\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ILightClientMsgs.MsgVerifyNonMembership\",\"name\":\"msg_\",\"type\":\"tuple\"}],\"name\":\"verifyNonMembership\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"errors\":{\"AccessControlBadConfirmation()\":[{\"details\":\"The caller of a function is not the expected one. NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\"}],\"AccessControlUnauthorizedAccount(address,bytes32)\":[{\"details\":\"The `account` is missing a role.\"}],\"BadQuorum(uint8,uint256)\":[{\"params\":{\"attestationCount\":\"The size of the configured attestation set.\",\"minRequired\":\"The provided minimum required signatures.\"}}],\"ConsensusTimestampNotFound(uint64)\":[{\"params\":{\"height\":\"The height that has no associated timestamp.\"}}],\"DuplicateSigner(address)\":[{\"params\":{\"signer\":\"Address of the duplicate signer.\"}}],\"ECDSAInvalidSignature()\":[{\"details\":\"The signature derives the `address(0)`.\"}],\"ECDSAInvalidSignatureLength(uint256)\":[{\"details\":\"The signature has an invalid length.\"}],\"ECDSAInvalidSignatureS(bytes32)\":[{\"details\":\"The signature has an S value that is in the upper half order.\"}],\"HeightMismatch(uint64,uint64)\":[{\"params\":{\"expected\":\"The expected height from the proofHeight field.\",\"provided\":\"The height that was present in the attested payload.\"}}],\"InvalidPathLength(uint256,uint256)\":[{\"params\":{\"expectedLength\":\"The expected length of the path.\",\"providedLength\":\"The length of the provided path.\"}}],\"InvalidSignatureLength(bytes)\":[{\"params\":{\"signature\":\"The invalid signature.\"}}],\"InvalidState(uint64,uint64)\":[{\"params\":{\"height\":\"The height of the attested state.\",\"timestamp\":\"The timestamp of the attested state.\"}}],\"SignatureInvalid(bytes)\":[{\"params\":{\"signature\":\"The invalid signature.\"}}],\"ThresholdNotMet(uint256,uint8)\":[{\"params\":{\"minRequired\":\"The minimum required signatures.\",\"validSigners\":\"Number of valid, unique attestation signatures.\"}}],\"UnknownSigner(address)\":[{\"params\":{\"signer\":\"Address of the unknown signer.\"}}]},\"events\":{\"RoleAdminChanged(bytes32,bytes32,bytes32)\":{\"details\":\"Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite {RoleAdminChanged} not being emitted to signal this.\"},\"RoleGranted(bytes32,address,address)\":{\"details\":\"Emitted when `account` is granted `role`. `sender` is the account that originated the contract call. This account bears the admin role (for the granted role). Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.\"},\"RoleRevoked(bytes32,address,address)\":{\"details\":\"Emitted when `account` is revoked `role`. `sender` is the account that originated the contract call: - if using `revokeRole`, it is the admin role bearer - if using `renounceRole`, it is the role bearer (i.e. `account`)\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"params\":{\"attestorAddresses\":\"The configured attestor addresses (EOAs)\",\"initialHeight\":\"The initial known height\",\"initialTimestampSeconds\":\"The initial timestamp in seconds for the initial height\",\"minRequiredSigs\":\"The quorum threshold\",\"roleManager\":\"Address that will administer roles and be allowed to submit proofs; if zero, anyone can submit\"}},\"getAttestationSet()\":{\"details\":\"The attestation set is fixed for the initial scope; rotation is out of scope.\",\"returns\":{\"attestorAddresses\":\"The configured attestor EOA addresses\",\"minRequiredSigs\":\"The minimum number of unique valid signatures required\"}},\"getClientState()\":{\"returns\":{\"_0\":\"The client state.\"}},\"getConsensusTimestamp(uint64)\":{\"params\":{\"revisionHeight\":\"The height for which to query the timestamp\"},\"returns\":{\"_0\":\"The trusted timestamp in unix seconds\"}},\"getRoleAdmin(bytes32)\":{\"details\":\"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}.\"},\"grantRole(bytes32,address)\":{\"details\":\"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event.\"},\"hasRole(bytes32,address)\":{\"details\":\"Returns `true` if `account` has been granted `role`.\"},\"misbehaviour(bytes)\":{\"params\":{\"misbehaviourMsg\":\"The misbehaviour message\"}},\"renounceRole(bytes32,address)\":{\"details\":\"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event.\"},\"revokeRole(bytes32,address)\":{\"details\":\"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event.\"},\"supportsInterface(bytes4)\":{\"details\":\"Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] to learn more about how these ids are created. This function call must use less than 30 000 gas.\"},\"updateClient(bytes)\":{\"params\":{\"updateMsg\":\"The encoded update message e.g., an SP1 proof.\"},\"returns\":{\"_0\":\"The result of the update operation\"}},\"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))\":{\"details\":\"Notice that this message is not view, as it may update the client state for caching purposes.\",\"params\":{\"msg_\":\"The membership message\"},\"returns\":{\"_0\":\"The unix timestamp of the verification height in the counterparty chain in seconds.\"}},\"verifyNonMembership((bytes,(uint64,uint64),bytes[]))\":{\"details\":\"Notice that this message is not view, as it may update the client state for caching purposes.\",\"params\":{\"msg_\":\"The membership message\"},\"returns\":{\"_0\":\"The unix timestamp of the verification height in the counterparty chain in seconds.\"}}},\"stateVariables\":{\"PROOF_SUBMITTER_ROLE\":{\"details\":\"If `address(0)` has this role, then anyone can submit proofs. If this client is used with `ICS26Router`, the router must be granted this role.\",\"return\":\"The role identifier\",\"returns\":{\"_0\":\"The role identifier\"}}},\"title\":\"Attestation-based IBC Light Client\",\"version\":1},\"userdoc\":{\"errors\":{\"BadQuorum(uint8,uint256)\":[{\"notice\":\"Reverts when the quorum threshold is zero or exceeds the attestation count.\"}],\"ConsensusTimestampNotFound(uint64)\":[{\"notice\":\"Missing trusted timestamp for the given height.\"}],\"DuplicateSigner(address)\":[{\"notice\":\"The recovered signer appears more than once among the signatures.\"}],\"EmptyPackets()\":[{\"notice\":\"The provided packet attestations were empty.\"}],\"EmptySignatures()\":[{\"notice\":\"The provided signatures were empty.\"}],\"EmptyValue()\":[{\"notice\":\"The provided membership value was empty.\"}],\"FeatureNotSupported()\":[{\"notice\":\"Reverts for functions that are out of scope for this implementation.\"}],\"FrozenClientState()\":[{\"notice\":\"Reverts when an action is attempted on a frozen client state.\"}],\"HeightMismatch(uint64,uint64)\":[{\"notice\":\"The attested height did not match the provided proof height.\"}],\"InvalidPathLength(uint256,uint256)\":[{\"notice\":\"The provided proof path length is invalid.\"}],\"InvalidSignatureLength(bytes)\":[{\"notice\":\"ECDSA signature has an invalid length.\"}],\"InvalidState(uint64,uint64)\":[{\"notice\":\"The attested state is invalid.\"}],\"NoAttestors()\":[{\"notice\":\"Reverts when the attestation set is empty during initialization.\"}],\"NotMember()\":[{\"notice\":\"The provided value is not a member of the attested set.\"}],\"NotNonMember()\":[{\"notice\":\"The attested commitment is not zero (receipt exists, so packet was received).\"}],\"SignatureInvalid(bytes)\":[{\"notice\":\"ECDSA signature failed to recover a valid signer.\"}],\"ThresholdNotMet(uint256,uint8)\":[{\"notice\":\"The number of valid unique signatures is below the required threshold.\"}],\"UnknownSigner(address)\":[{\"notice\":\"The recovered signer is not part of the attestation set.\"}]},\"kind\":\"user\",\"methods\":{\"PROOF_SUBMITTER_ROLE()\":{\"notice\":\"The role identifier for the proof submitter role\"},\"constructor\":{\"notice\":\"Initializes the attestor light client with its fixed attestor set and initial height/timestamp.\"},\"getAttestationSet()\":{\"notice\":\"Returns the attestation set configuration.\"},\"getClientState()\":{\"notice\":\"Returns the client state.\"},\"getConsensusTimestamp(uint64)\":{\"notice\":\"Returns the trusted consensus timestamp (in seconds) at the given revision height.\"},\"misbehaviour(bytes)\":{\"notice\":\"Misbehaviour handling, moves the light client to the frozen state if misbehaviour is detected\"},\"updateClient(bytes)\":{\"notice\":\"Updating the client and consensus state\"},\"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))\":{\"notice\":\"Querying the membership of a key-value pair\"},\"verifyNonMembership((bytes,(uint64,uint64),bytes[]))\":{\"notice\":\"Querying the non-membership of a key\"}},\"notice\":\"Implements an IBC light client that trusts an off-chain m-of-n attestor set.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/light-clients/attestation/AttestationLightClient.sol\":\"AttestationLightClient\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[\":@openzeppelin-contracts/=node_modules/@openzeppelin/contracts/\",\":@openzeppelin-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/\",\":@openzeppelin/=node_modules/@openzeppelin/\",\":@sp1-contracts/=node_modules/sp1-contracts/contracts/src/\",\":@uniswap/permit2/=node_modules/@uniswap/permit2/\",\":forge-std/=node_modules/forge-std/src/\",\":sp1-contracts/=node_modules/sp1-contracts/\"],\"viaIR\":true},\"sources\":{\"contracts/interfaces/ILightClient.sol\":{\"keccak256\":\"0x1caae9e4f741a86c0056d5f7713a35f3bd96db7a9fd52fc9f1cf79aad76a442f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4a536db3b926560f69547c17e4f451a2631ddea0f42bd224c7fd8382d74429f6\",\"dweb:/ipfs/QmcQ33kxcmmowXgMbQGTBgiKBjF7v8UiP4y73bB9hhDizC\"]},\"contracts/light-clients/attestation/AttestationLightClient.sol\":{\"keccak256\":\"0xb287ccfd4e7b3baa99215a62484a99117b3be10d9765899c221f3fb0d54876fd\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://752979b4fe813e9cee697e2c29c7b119f8f0f84e2a9b6b1d0db97685a4435660\",\"dweb:/ipfs/QmTxfSdTKcrfaKKKtsX5Wy8RVMYcyYfkKCaDm2brNYTLFz\"]},\"contracts/light-clients/attestation/errors/IAttestationLightClientErrors.sol\":{\"keccak256\":\"0x0d3592b3c36cfcde83e2510bb9179b41befb486cbb65f529ff68c09fad751564\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8d9bd1170d3954f7af6bd9fe79efbeff8f943073d27eb7bbecc8b9c66a0bdc62\",\"dweb:/ipfs/QmRuorHcJVhDJdYf7y43tyZuDowoBGkeRjH2z9C939XuDa\"]},\"contracts/light-clients/attestation/interfaces/IAttestationLightClient.sol\":{\"keccak256\":\"0x115ffce5474e6127fdb1f2b9284ea6d51ec978efd47f4425ba693c6c69d8b0af\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://bebb9e7d156b1a3ec682ff9cb8c9680b718bf0ae8e217ed153811dd399e456e8\",\"dweb:/ipfs/QmcjbxSBU4jvZMxn7PYYM6fxLCovWj1rKAtZazXY2RSA8U\"]},\"contracts/light-clients/attestation/msgs/IAttestationLightClientMsgs.sol\":{\"keccak256\":\"0x08148f1409f7e3c8968916950d920b8a4352796191875a4672ad2404e3541ef5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://94c4b12d851c5e7b581d2794c88481df658eaf55ad4e54ddef14423c90986a67\",\"dweb:/ipfs/QmZysvpbY1Xe7kNVw1iHZsPaVMUMqLauJssyrceQybBgXw\"]},\"contracts/light-clients/attestation/msgs/IAttestationMsgs.sol\":{\"keccak256\":\"0xba7901ec5fb4a4845272501a1a0b183309cd0ae37d9ec5f9e3c6ce699bfd281f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://72d0768a801a3c70910c10a1fc48c29c62d902dc602e20d72a1388ba498e8a4b\",\"dweb:/ipfs/QmUKLdbX2NwSyHFrh2WUHLm4zdFivu3Ryw2XyjT3R9Szse\"]},\"contracts/msgs/IICS02ClientMsgs.sol\":{\"keccak256\":\"0xb5fdb25319d5c32b3f527982d45ec1e9bec5215515581aee034d853006ea01a0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6b8939bf552c62c952a491008bcd9b59ab7dda20a9482bf93438e05793881542\",\"dweb:/ipfs/QmfWmzNCp8bd4pP26Lfw5HHp4dSZnCDqZDjVN7SD8P7eDh\"]},\"contracts/msgs/ILightClientMsgs.sol\":{\"keccak256\":\"0xa0565e7748b8b3284d72e1aa6ef4cc8264fcaa5f4e0761d22d64686953db6378\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c4476010d7090bfaa74127526f2051c6bc82ff42a09b936e848880a1f15a6065\",\"dweb:/ipfs/QmY87JgRsFQXSZdGXNjNHEJCkdTHzFXH4vZEJ3q7MnHuP8\"]},\"node_modules/@openzeppelin/contracts/access/AccessControl.sol\":{\"keccak256\":\"0x1a6b4f6b7798ab80929d491b89d5427a9b3338c0fd1acd0ba325f69c6f1646af\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7bb7f346c12a14dc622bc105ce3c47202fbc89f4b153a28a63bb68193297330c\",\"dweb:/ipfs/QmagwF8P3bUBXwdo159ueEnY9dLSvEWwK24kk2op58egwG\"]},\"node_modules/@openzeppelin/contracts/access/IAccessControl.sol\":{\"keccak256\":\"0xbff9f59c84e5337689161ce7641c0ef8e872d6a7536fbc1f5133f128887aba3c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b308f882e796f7b79c9502deacb0a62983035c6f6f4e962b319ba6a1f4a77d3d\",\"dweb:/ipfs/QmaWCW7ahEQqFjwhSUhV7Ae7WhfNvzSpE7DQ58hvEooqPL\"]},\"node_modules/@openzeppelin/contracts/utils/Context.sol\":{\"keccak256\":\"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12\",\"dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF\"]},\"node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol\":{\"keccak256\":\"0x69f54c02b7d81d505910ec198c11ed4c6a728418a868b906b4a0cf29946fda84\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://8e25e4bdb7ae1f21d23bfee996e22736fc0ab44cfabedac82a757b1edc5623b9\",\"dweb:/ipfs/QmQdWQvB6JCP9ZMbzi8EvQ1PTETqkcTWrbcVurS7DKpa5n\"]},\"node_modules/@openzeppelin/contracts/utils/introspection/ERC165.sol\":{\"keccak256\":\"0x2d9dc2fe26180f74c11c13663647d38e259e45f95eb88f57b61d2160b0109d3e\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://81233d1f98060113d9922180bb0f14f8335856fe9f339134b09335e9f678c377\",\"dweb:/ipfs/QmWh6R35SarhAn4z2wH8SU456jJSYL2FgucfTFgbHJJN4E\"]},\"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617\",\"dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address[]","name":"attestorAddresses","type":"address[]"},{"internalType":"uint8","name":"minRequiredSigs","type":"uint8"},{"internalType":"uint64","name":"initialHeight","type":"uint64"},{"internalType":"uint64","name":"initialTimestampSeconds","type":"uint64"},{"internalType":"address","name":"roleManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"type":"error","name":"AccessControlBadConfirmation"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"type":"error","name":"AccessControlUnauthorizedAccount"},{"inputs":[{"internalType":"uint8","name":"minRequired","type":"uint8"},{"internalType":"uint256","name":"attestationCount","type":"uint256"}],"type":"error","name":"BadQuorum"},{"inputs":[{"internalType":"uint64","name":"height","type":"uint64"}],"type":"error","name":"ConsensusTimestampNotFound"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"type":"error","name":"DuplicateSigner"},{"inputs":[],"type":"error","name":"ECDSAInvalidSignature"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"type":"error","name":"ECDSAInvalidSignatureLength"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"type":"error","name":"ECDSAInvalidSignatureS"},{"inputs":[],"type":"error","name":"EmptyPackets"},{"inputs":[],"type":"error","name":"EmptySignatures"},{"inputs":[],"type":"error","name":"EmptyValue"},{"inputs":[],"type":"error","name":"FeatureNotSupported"},{"inputs":[],"type":"error","name":"FrozenClientState"},{"inputs":[{"internalType":"uint64","name":"expected","type":"uint64"},{"internalType":"uint64","name":"provided","type":"uint64"}],"type":"error","name":"HeightMismatch"},{"inputs":[{"internalType":"uint256","name":"expectedLength","type":"uint256"},{"internalType":"uint256","name":"providedLength","type":"uint256"}],"type":"error","name":"InvalidPathLength"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"type":"error","name":"InvalidSignatureLength"},{"inputs":[{"internalType":"uint64","name":"height","type":"uint64"},{"internalType":"uint64","name":"timestamp","type":"uint64"}],"type":"error","name":"InvalidState"},{"inputs":[],"type":"error","name":"NoAttestors"},{"inputs":[],"type":"error","name":"NotMember"},{"inputs":[],"type":"error","name":"NotNonMember"},{"inputs":[{"internalType":"bytes","name":"signature","type":"bytes"}],"type":"error","name":"SignatureInvalid"},{"inputs":[{"internalType":"uint256","name":"validSigners","type":"uint256"},{"internalType":"uint8","name":"minRequired","type":"uint8"}],"type":"error","name":"ThresholdNotMet"},{"inputs":[{"internalType":"address","name":"signer","type":"address"}],"type":"error","name":"UnknownSigner"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"previousAdminRole","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"newAdminRole","type":"bytes32","indexed":true}],"type":"event","name":"RoleAdminChanged","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleGranted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleRevoked","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"PROOF_SUBMITTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getAttestationSet","outputs":[{"internalType":"address[]","name":"attestorAddresses","type":"address[]"},{"internalType":"uint8","name":"minRequiredSigs","type":"uint8"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getClientState","outputs":[{"internalType":"bytes","name":"","type":"bytes"}]},{"inputs":[{"internalType":"uint64","name":"revisionHeight","type":"uint64"}],"stateMutability":"view","type":"function","name":"getConsensusTimestamp","outputs":[{"internalType":"uint64","name":"","type":"uint64"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"grantRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"view","type":"function","name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function","name":"misbehaviour"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"renounceRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"revokeRole"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"stateMutability":"view","type":"function","name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes","name":"updateMsg","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"updateClient","outputs":[{"internalType":"enum ILightClientMsgs.UpdateResult","name":"","type":"uint8"}]},{"inputs":[{"internalType":"struct ILightClientMsgs.MsgVerifyMembership","name":"msg_","type":"tuple","components":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"struct IICS02ClientMsgs.Height","name":"proofHeight","type":"tuple","components":[{"internalType":"uint64","name":"revisionNumber","type":"uint64"},{"internalType":"uint64","name":"revisionHeight","type":"uint64"}]},{"internalType":"bytes[]","name":"path","type":"bytes[]"},{"internalType":"bytes","name":"value","type":"bytes"}]}],"stateMutability":"view","type":"function","name":"verifyMembership","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"struct ILightClientMsgs.MsgVerifyNonMembership","name":"msg_","type":"tuple","components":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"struct IICS02ClientMsgs.Height","name":"proofHeight","type":"tuple","components":[{"internalType":"uint64","name":"revisionNumber","type":"uint64"},{"internalType":"uint64","name":"revisionHeight","type":"uint64"}]},{"internalType":"bytes[]","name":"path","type":"bytes[]"}]}],"stateMutability":"view","type":"function","name":"verifyNonMembership","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{"constructor":{"params":{"attestorAddresses":"The configured attestor addresses (EOAs)","initialHeight":"The initial known height","initialTimestampSeconds":"The initial timestamp in seconds for the initial height","minRequiredSigs":"The quorum threshold","roleManager":"Address that will administer roles and be allowed to submit proofs; if zero, anyone can submit"}},"getAttestationSet()":{"details":"The attestation set is fixed for the initial scope; rotation is out of scope.","returns":{"attestorAddresses":"The configured attestor EOA addresses","minRequiredSigs":"The minimum number of unique valid signatures required"}},"getClientState()":{"returns":{"_0":"The client state."}},"getConsensusTimestamp(uint64)":{"params":{"revisionHeight":"The height for which to query the timestamp"},"returns":{"_0":"The trusted timestamp in unix seconds"}},"getRoleAdmin(bytes32)":{"details":"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}."},"grantRole(bytes32,address)":{"details":"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event."},"hasRole(bytes32,address)":{"details":"Returns `true` if `account` has been granted `role`."},"misbehaviour(bytes)":{"params":{"misbehaviourMsg":"The misbehaviour message"}},"renounceRole(bytes32,address)":{"details":"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event."},"revokeRole(bytes32,address)":{"details":"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event."},"supportsInterface(bytes4)":{"details":"Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] to learn more about how these ids are created. This function call must use less than 30 000 gas."},"updateClient(bytes)":{"params":{"updateMsg":"The encoded update message e.g., an SP1 proof."},"returns":{"_0":"The result of the update operation"}},"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":{"details":"Notice that this message is not view, as it may update the client state for caching purposes.","params":{"msg_":"The membership message"},"returns":{"_0":"The unix timestamp of the verification height in the counterparty chain in seconds."}},"verifyNonMembership((bytes,(uint64,uint64),bytes[]))":{"details":"Notice that this message is not view, as it may update the client state for caching purposes.","params":{"msg_":"The membership message"},"returns":{"_0":"The unix timestamp of the verification height in the counterparty chain in seconds."}}},"version":1},"userdoc":{"kind":"user","methods":{"PROOF_SUBMITTER_ROLE()":{"notice":"The role identifier for the proof submitter role"},"constructor":{"notice":"Initializes the attestor light client with its fixed attestor set and initial height/timestamp."},"getAttestationSet()":{"notice":"Returns the attestation set configuration."},"getClientState()":{"notice":"Returns the client state."},"getConsensusTimestamp(uint64)":{"notice":"Returns the trusted consensus timestamp (in seconds) at the given revision height."},"misbehaviour(bytes)":{"notice":"Misbehaviour handling, moves the light client to the frozen state if misbehaviour is detected"},"updateClient(bytes)":{"notice":"Updating the client and consensus state"},"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":{"notice":"Querying the membership of a key-value pair"},"verifyNonMembership((bytes,(uint64,uint64),bytes[]))":{"notice":"Querying the non-membership of a key"}},"version":1}},"settings":{"remappings":["@openzeppelin-contracts/=node_modules/@openzeppelin/contracts/","@openzeppelin-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/","@openzeppelin/=node_modules/@openzeppelin/","@sp1-contracts/=node_modules/sp1-contracts/contracts/src/","@uniswap/permit2/=node_modules/@uniswap/permit2/","forge-std/=node_modules/forge-std/src/","sp1-contracts/=node_modules/sp1-contracts/"],"optimizer":{"enabled":true,"runs":10000},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"contracts/light-clients/attestation/AttestationLightClient.sol":"AttestationLightClient"},"evmVersion":"cancun","libraries":{},"viaIR":true},"sources":{"contracts/interfaces/ILightClient.sol":{"keccak256":"0x1caae9e4f741a86c0056d5f7713a35f3bd96db7a9fd52fc9f1cf79aad76a442f","urls":["bzz-raw://4a536db3b926560f69547c17e4f451a2631ddea0f42bd224c7fd8382d74429f6","dweb:/ipfs/QmcQ33kxcmmowXgMbQGTBgiKBjF7v8UiP4y73bB9hhDizC"],"license":"MIT"},"contracts/light-clients/attestation/AttestationLightClient.sol":{"keccak256":"0xb287ccfd4e7b3baa99215a62484a99117b3be10d9765899c221f3fb0d54876fd","urls":["bzz-raw://752979b4fe813e9cee697e2c29c7b119f8f0f84e2a9b6b1d0db97685a4435660","dweb:/ipfs/QmTxfSdTKcrfaKKKtsX5Wy8RVMYcyYfkKCaDm2brNYTLFz"],"license":"MIT"},"contracts/light-clients/attestation/errors/IAttestationLightClientErrors.sol":{"keccak256":"0x0d3592b3c36cfcde83e2510bb9179b41befb486cbb65f529ff68c09fad751564","urls":["bzz-raw://8d9bd1170d3954f7af6bd9fe79efbeff8f943073d27eb7bbecc8b9c66a0bdc62","dweb:/ipfs/QmRuorHcJVhDJdYf7y43tyZuDowoBGkeRjH2z9C939XuDa"],"license":"MIT"},"contracts/light-clients/attestation/interfaces/IAttestationLightClient.sol":{"keccak256":"0x115ffce5474e6127fdb1f2b9284ea6d51ec978efd47f4425ba693c6c69d8b0af","urls":["bzz-raw://bebb9e7d156b1a3ec682ff9cb8c9680b718bf0ae8e217ed153811dd399e456e8","dweb:/ipfs/QmcjbxSBU4jvZMxn7PYYM6fxLCovWj1rKAtZazXY2RSA8U"],"license":"MIT"},"contracts/light-clients/attestation/msgs/IAttestationLightClientMsgs.sol":{"keccak256":"0x08148f1409f7e3c8968916950d920b8a4352796191875a4672ad2404e3541ef5","urls":["bzz-raw://94c4b12d851c5e7b581d2794c88481df658eaf55ad4e54ddef14423c90986a67","dweb:/ipfs/QmZysvpbY1Xe7kNVw1iHZsPaVMUMqLauJssyrceQybBgXw"],"license":"MIT"},"contracts/light-clients/attestation/msgs/IAttestationMsgs.sol":{"keccak256":"0xba7901ec5fb4a4845272501a1a0b183309cd0ae37d9ec5f9e3c6ce699bfd281f","urls":["bzz-raw://72d0768a801a3c70910c10a1fc48c29c62d902dc602e20d72a1388ba498e8a4b","dweb:/ipfs/QmUKLdbX2NwSyHFrh2WUHLm4zdFivu3Ryw2XyjT3R9Szse"],"license":"MIT"},"contracts/msgs/IICS02ClientMsgs.sol":{"keccak256":"0xb5fdb25319d5c32b3f527982d45ec1e9bec5215515581aee034d853006ea01a0","urls":["bzz-raw://6b8939bf552c62c952a491008bcd9b59ab7dda20a9482bf93438e05793881542","dweb:/ipfs/QmfWmzNCp8bd4pP26Lfw5HHp4dSZnCDqZDjVN7SD8P7eDh"],"license":"MIT"},"contracts/msgs/ILightClientMsgs.sol":{"keccak256":"0xa0565e7748b8b3284d72e1aa6ef4cc8264fcaa5f4e0761d22d64686953db6378","urls":["bzz-raw://c4476010d7090bfaa74127526f2051c6bc82ff42a09b936e848880a1f15a6065","dweb:/ipfs/QmY87JgRsFQXSZdGXNjNHEJCkdTHzFXH4vZEJ3q7MnHuP8"],"license":"MIT"},"node_modules/@openzeppelin/contracts/access/AccessControl.sol":{"keccak256":"0x1a6b4f6b7798ab80929d491b89d5427a9b3338c0fd1acd0ba325f69c6f1646af","urls":["bzz-raw://7bb7f346c12a14dc622bc105ce3c47202fbc89f4b153a28a63bb68193297330c","dweb:/ipfs/QmagwF8P3bUBXwdo159ueEnY9dLSvEWwK24kk2op58egwG"],"license":"MIT"},"node_modules/@openzeppelin/contracts/access/IAccessControl.sol":{"keccak256":"0xbff9f59c84e5337689161ce7641c0ef8e872d6a7536fbc1f5133f128887aba3c","urls":["bzz-raw://b308f882e796f7b79c9502deacb0a62983035c6f6f4e962b319ba6a1f4a77d3d","dweb:/ipfs/QmaWCW7ahEQqFjwhSUhV7Ae7WhfNvzSpE7DQ58hvEooqPL"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/Context.sol":{"keccak256":"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2","urls":["bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12","dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol":{"keccak256":"0x69f54c02b7d81d505910ec198c11ed4c6a728418a868b906b4a0cf29946fda84","urls":["bzz-raw://8e25e4bdb7ae1f21d23bfee996e22736fc0ab44cfabedac82a757b1edc5623b9","dweb:/ipfs/QmQdWQvB6JCP9ZMbzi8EvQ1PTETqkcTWrbcVurS7DKpa5n"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/introspection/ERC165.sol":{"keccak256":"0x2d9dc2fe26180f74c11c13663647d38e259e45f95eb88f57b61d2160b0109d3e","urls":["bzz-raw://81233d1f98060113d9922180bb0f14f8335856fe9f339134b09335e9f678c377","dweb:/ipfs/QmWh6R35SarhAn4z2wH8SU456jJSYL2FgucfTFgbHJJN4E"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol":{"keccak256":"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c","urls":["bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617","dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u"],"license":"MIT"}},"version":1},"id":29} \ No newline at end of file diff --git a/abi/bytecode/SP1ICS07Tendermint.json b/abi/bytecode/SP1ICS07Tendermint.json index 261300ea0..681cb79b8 100644 --- a/abi/bytecode/SP1ICS07Tendermint.json +++ b/abi/bytecode/SP1ICS07Tendermint.json @@ -1 +1 @@ -{"abi":[{"type":"constructor","inputs":[{"name":"updateClientProgramVkey","type":"bytes32","internalType":"bytes32"},{"name":"membershipProgramVkey","type":"bytes32","internalType":"bytes32"},{"name":"updateClientAndMembershipProgramVkey","type":"bytes32","internalType":"bytes32"},{"name":"misbehaviourProgramVkey","type":"bytes32","internalType":"bytes32"},{"name":"sp1Verifier","type":"address","internalType":"address"},{"name":"_clientState","type":"bytes","internalType":"bytes"},{"name":"_consensusState","type":"bytes32","internalType":"bytes32"},{"name":"roleManager","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"ALLOWED_SP1_CLOCK_DRIFT","inputs":[],"outputs":[{"name":"","type":"uint16","internalType":"uint16"}],"stateMutability":"view"},{"type":"function","name":"DEFAULT_ADMIN_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"MEMBERSHIP_PROGRAM_VKEY","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"MISBEHAVIOUR_PROGRAM_VKEY","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"PROOF_SUBMITTER_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"UPDATE_CLIENT_PROGRAM_VKEY","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"VERIFIER","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ISP1Verifier"}],"stateMutability":"view"},{"type":"function","name":"clientState","inputs":[],"outputs":[{"name":"chainId","type":"string","internalType":"string"},{"name":"trustLevel","type":"tuple","internalType":"struct IICS07TendermintMsgs.TrustThreshold","components":[{"name":"numerator","type":"uint8","internalType":"uint8"},{"name":"denominator","type":"uint8","internalType":"uint8"}]},{"name":"latestHeight","type":"tuple","internalType":"struct IICS02ClientMsgs.Height","components":[{"name":"revisionNumber","type":"uint64","internalType":"uint64"},{"name":"revisionHeight","type":"uint64","internalType":"uint64"}]},{"name":"trustingPeriod","type":"uint32","internalType":"uint32"},{"name":"unbondingPeriod","type":"uint32","internalType":"uint32"},{"name":"isFrozen","type":"bool","internalType":"bool"},{"name":"zkAlgorithm","type":"uint8","internalType":"enum IICS07TendermintMsgs.SupportedZkAlgorithm"}],"stateMutability":"view"},{"type":"function","name":"getClientState","inputs":[],"outputs":[{"name":"","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"getConsensusStateHash","inputs":[{"name":"revisionHeight","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"getRoleAdmin","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"grantRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"hasRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"misbehaviour","inputs":[{"name":"misbehaviourMsg","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"multicall","inputs":[{"name":"data","type":"bytes[]","internalType":"bytes[]"}],"outputs":[{"name":"results","type":"bytes[]","internalType":"bytes[]"}],"stateMutability":"nonpayable"},{"type":"function","name":"renounceRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"callerConfirmation","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"revokeRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"updateClient","inputs":[{"name":"updateMsg","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"uint8","internalType":"enum ILightClientMsgs.UpdateResult"}],"stateMutability":"nonpayable"},{"type":"function","name":"verifyMembership","inputs":[{"name":"msg_","type":"tuple","internalType":"struct ILightClientMsgs.MsgVerifyMembership","components":[{"name":"proof","type":"bytes","internalType":"bytes"},{"name":"proofHeight","type":"tuple","internalType":"struct IICS02ClientMsgs.Height","components":[{"name":"revisionNumber","type":"uint64","internalType":"uint64"},{"name":"revisionHeight","type":"uint64","internalType":"uint64"}]},{"name":"path","type":"bytes[]","internalType":"bytes[]"},{"name":"value","type":"bytes","internalType":"bytes"}]}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"verifyNonMembership","inputs":[{"name":"msg_","type":"tuple","internalType":"struct ILightClientMsgs.MsgVerifyNonMembership","components":[{"name":"proof","type":"bytes","internalType":"bytes"},{"name":"proofHeight","type":"tuple","internalType":"struct IICS02ClientMsgs.Height","components":[{"name":"revisionNumber","type":"uint64","internalType":"uint64"},{"name":"revisionHeight","type":"uint64","internalType":"uint64"}]},{"name":"path","type":"bytes[]","internalType":"bytes[]"}]}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"event","name":"RoleAdminChanged","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"previousAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"newAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"AccessControlBadConfirmation","inputs":[]},{"type":"error","name":"AccessControlUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"neededRole","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"AddressEmptyCode","inputs":[{"name":"target","type":"address","internalType":"address"}]},{"type":"error","name":"CannotHandleMisbehavior","inputs":[]},{"type":"error","name":"ChainIdMismatch","inputs":[{"name":"expected","type":"string","internalType":"string"},{"name":"actual","type":"string","internalType":"string"}]},{"type":"error","name":"ClientStateMismatch","inputs":[{"name":"expected","type":"bytes","internalType":"bytes"},{"name":"actual","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"ConsensusStateHashMismatch","inputs":[{"name":"expected","type":"bytes32","internalType":"bytes32"},{"name":"actual","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"ConsensusStateNotFound","inputs":[]},{"type":"error","name":"ConsensusStateRootMismatch","inputs":[{"name":"expected","type":"bytes32","internalType":"bytes32"},{"name":"actual","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"EmptyValue","inputs":[]},{"type":"error","name":"FailedCall","inputs":[]},{"type":"error","name":"FeatureNotSupported","inputs":[]},{"type":"error","name":"FrozenClientState","inputs":[]},{"type":"error","name":"InvalidMembershipProof","inputs":[]},{"type":"error","name":"KeyValuePairNotInCache","inputs":[{"name":"path","type":"bytes[]","internalType":"bytes[]"},{"name":"value","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"LengthIsOutOfRange","inputs":[{"name":"length","type":"uint256","internalType":"uint256"},{"name":"min","type":"uint256","internalType":"uint256"},{"name":"max","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"MembershipProofKeyNotFound","inputs":[{"name":"path","type":"bytes[]","internalType":"bytes[]"}]},{"type":"error","name":"MembershipProofValueMismatch","inputs":[{"name":"expected","type":"bytes","internalType":"bytes"},{"name":"actual","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"ProofHeightMismatch","inputs":[{"name":"expectedRevisionNumber","type":"uint64","internalType":"uint64"},{"name":"expectedRevisionHeight","type":"uint64","internalType":"uint64"},{"name":"actualRevisionNumber","type":"uint64","internalType":"uint64"},{"name":"actualRevisionHeight","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"ProofIsInTheFuture","inputs":[{"name":"now","type":"uint256","internalType":"uint256"},{"name":"proofTimestamp","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"ProofIsTooOld","inputs":[{"name":"now","type":"uint256","internalType":"uint256"},{"name":"proofTimestamp","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"TrustThresholdMismatch","inputs":[{"name":"expectedNumerator","type":"uint256","internalType":"uint256"},{"name":"expectedDenominator","type":"uint256","internalType":"uint256"},{"name":"actualNumerator","type":"uint256","internalType":"uint256"},{"name":"actualDenominator","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"TrustingPeriodMismatch","inputs":[{"name":"expected","type":"uint256","internalType":"uint256"},{"name":"actual","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"TrustingPeriodTooLong","inputs":[{"name":"trustingPeriod","type":"uint256","internalType":"uint256"},{"name":"unbondingPeriod","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"UnbondingPeriodMismatch","inputs":[{"name":"expected","type":"uint256","internalType":"uint256"},{"name":"actual","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"UnknownMembershipProofType","inputs":[{"name":"proofType","type":"uint8","internalType":"uint8"}]},{"type":"error","name":"UnknownZkAlgorithm","inputs":[{"name":"algorithm","type":"uint8","internalType":"uint8"}]},{"type":"error","name":"VerificationKeyMismatch","inputs":[{"name":"expected","type":"bytes32","internalType":"bytes32"},{"name":"actual","type":"bytes32","internalType":"bytes32"}]}],"bytecode":{"object":"0x61012060405234610541576137358038038061001a81610564565b928339810161010082820312610541578151916020810151604082015160608301519061004960808501610589565b60a08501519095906001600160401b0381116105415785019080601f8301121561054157815161007b9260200161059d565b9261008d60e060c08701519601610589565b9660805260a05260c05260e05280518101906020820190602081840312610541576020810151906001600160401b038211610541570191829003601f1981019061012013610541576040519160e083016001600160401b0381118482101761052d5760405260208401516001600160401b038111610541576020908501019080601f830112156105415781516101259260200161059d565b825260408112610541576040610139610545565b916101458286016105dd565b8352610153606086016105dd565b602084015260208401928352603f19011261054157610170610545565b61017c608085016105eb565b815261018a60a085016105eb565b6020820152604083019081526101a260c085016105ff565b90606084019182526101b660e086016105ff565b92608085019384526101008601519586151587036105415760a0860196875261012001519460028610156105415760c08101958652518051906001600160401b03821161052d57600154600181811c91168015610523575b602082101461050f57601f81116104ac575b50602090601f831160011461043f5763ffffffff95949392915f9183610434575b50508160011b915f199060031b1c1916176001555b5160ff81511661ff00602060025493015160081b169161ffff191617176002555160018060401b0381511660035491602068010000000000000000600160801b0391015160401b169160018060801b031916171760035551169267ffffffff00000000600454925160201b169051151560401b92519160028310156104205769ff00000000000000000068ff00000000000000009360481b169469ff000000000000000000199160018060481b0319161716179116171760045560018060401b0360035460401c165f52600560205260405f205560018060a01b03166101005260045463ffffffff8116610708810163ffffffff811161040c5763ffffffff809360201c1692839116116103f757826001600160a01b0381166103de575061037c610706565b505b604051612f0c908161078982396080518181816105570152611482015260a0518181816101f90152611e3e015260c051818181610cf001526121c2015260e0518181816102a00152610afe015261010051818181610cb50152611a570152f35b806103eb6103f192610610565b50610686565b5061037e565b6333fae18560e21b5f5260045260245260445ffd5b634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b015190505f80610241565b90601f1983169160015f52815f20925f5b818110610494575091600193918563ffffffff99989796941061047c575b505050811b01600155610256565b01515f1960f88460031b161c191690555f808061046e565b92936020600181928786015181550195019301610450565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f840160051c81019160208510610505575b601f0160051c01905b8181106104fa5750610220565b5f81556001016104ed565b90915081906104e4565b634e487b7160e01b5f52602260045260245ffd5b90607f169061020e565b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b60408051919082016001600160401b0381118382101761052d57604052565b6040519190601f01601f191682016001600160401b0381118382101761052d57604052565b51906001600160a01b038216820361054157565b9192916001600160401b03821161052d576105c1601f8301601f1916602001610564565b938285528282011161054157815f926020928387015e84010152565b519060ff8216820361054157565b51906001600160401b038216820361054157565b519063ffffffff8216820361054157565b6001600160a01b0381165f9081525f5160206137155f395f51905f52602052604090205460ff16610681576001600160a01b03165f8181525f5160206137155f395f51905f5260205260408120805460ff191660011790553391905f5160206136955f395f51905f528180a4600190565b505f90565b6001600160a01b0381165f9081525f5160206136b55f395f51905f52602052604090205460ff16610681576001600160a01b03165f8181525f5160206136b55f395f51905f5260205260408120805460ff191660011790553391905f5160206136f55f395f51905f52905f5160206136955f395f51905f529080a4600190565b5f80525f5160206136b55f395f51905f526020525f5160206136d55f395f51905f525460ff16610784575f8080525f5160206136b55f395f51905f526020525f5160206136d55f395f51905f52805460ff1916600117905533905f5160206136f55f395f51905f525f5160206136955f395f51905f528280a4600190565b5f9056fe6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a714610d13575080630225293e14610cd957806308c84e7014610c895780630bece35614610bc657806323842fb814610b96578063248a9ca314610b6c5780632c3ee47414610b505780632f2ff15d14610b21578063314d4dff14610ae757806336568abe14610a8b5780634d6d9ffb146109a95780635972185a1461096f578063682ed5f01461084b57806391d1485414610802578063a217fddf146107e8578063ac9650d81461066a578063bd3ce6b01461057a578063ca7242f914610540578063d547741f1461050a578063ddba65371461021c578063e45a6d0d146101e25763ef913a4b1461010e575f80fd5b346101de575f6003193601126101de576101da60405160208082015261012060408201526101c6816101436101608201610f03565b60ff600254818116606085015260081c16608083015267ffffffffffffffff60035481811660a085015260401c1660c083015260ff60045463ffffffff811660e085015263ffffffff8160201c16610100850152818160401c16151561012085015260481c166101b28161109c565b61014083015203601f198101835282611079565b604051918291602083526020830190610e35565b0390f35b5f80fd5b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5761022a36610db1565b6004549160ff8360401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754610298929060ff16156104d5575b810190611116565b6102c78151517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b6020815101518051810160208101916020818303126101de57602081015167ffffffffffffffff81116101de57019061018090829003126101de576040519061030f82611009565b602081015167ffffffffffffffff81116101de5783908201602001906103349161129d565b825261034260408201611385565b60208301908152906103578460608301611256565b604084019081529161036c8560a08401611256565b60608501908152946103818160e085016113a2565b92608086019384526101400190610397916113a2565b9360a081019485525190516fffffffffffffffffffffffffffffffff166103bd9161265e565b516040805182516fffffffffffffffffffffffffffffffff166020808301918252840151828401529190920151606080840191909152825290610401608082611079565b51902090516020015167ffffffffffffffff1661041d90611686565b61042a91908181146118b9565b516040805182516fffffffffffffffffffffffffffffffff16602080830191825284015182840152919092015160608084019190915282529061046e608082611079565b51902090516020015167ffffffffffffffff1661048a90611686565b61049791908181146118b9565b516104a190611a40565b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff1668010000000000000000176004555f80f35b6104dd6117cb565b610290565b7f928b1233000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101de5761053e61051b36610e02565b90610539610534825f525f602052600160405f20015490565b611853565b611be2565b005b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de575f6003193601126101de576040516105a18161059a81610f03565b0382611079565b6101206040516105b081610fc0565b60025460ff8116825260ff602083019160081c16815260ff6040516105d481610fc0565b67ffffffffffffffff600354818116835281602084019160401c168152816004549385808660481c1697816106146040519d8d8f9e8f9081520190610e35565b9a511660208c0152511660408a0152511660608801525116608086015263ffffffff811660a086015263ffffffff8160201c1660c086015260401c16151560e08401526106608161109c565b6101008301520390f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de57366023820112156101de57806004013567ffffffffffffffff81116101de57602482013660248360051b850101116101de576020906040516106cf8382611079565b5f815282810191601f1984013684376106e785611772565b956106f56040519788611079565b858752601f1961070487611772565b01855f5b8281106107d8575050505f5b868110156107c5576001906107a15f8061076e8861073a60248760051b8a01018a6116cd565b8d8d6040959395519483869484860198893784019083820190898252519283915e010185815203601f198101835282611079565b5190305af43d156107bd573d90610784826110a6565b916107926040519384611079565b82523d5f8b84013e5b30612d1f565b6107ab828b61178a565b526107b6818a61178a565b5001610714565b60609061079b565b604051868152806101da8189018b610e5a565b606082828c010152018690610708565b346101de575f6003193601126101de5760206040515f8152f35b346101de5761081036610e02565b905f525f60205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060ff60405f2054166040519015158152f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de578060040160a060031983360301126101de5760ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475460ff1615610962575b60848201916108ee83836116cd565b90501561093a576020928261090681610932956116cd565b602461092a61092361091b606489018761171e565b9790966116cd565b36916110c2565b950191611d2b565b604051908152f35b7f1208b21b000000000000000000000000000000000000000000000000000000005f5260045ffd5b61096a6117cb565b6108df565b346101de575f6003193601126101de5760206040517fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b8152f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de5780600401608060031983360301126101de5760ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754909261093292909160ff1615610a7e575b610a61610a5482806116cd565b919092606485019061171e565b929091602460405195610a748988611079565b5f87520191611d2b565b610a866117cb565b610a47565b346101de57610a9936610e02565b3373ffffffffffffffffffffffffffffffffffffffff821603610abf5761053e91611be2565b7f6697b232000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5761053e610b3236610e02565b90610b4b610534825f525f602052600160405f20015490565b611b10565b346101de575f6003193601126101de5760206040516107088152f35b346101de5760206003193601126101de5760206109326004355f525f602052600160405f20015490565b346101de5760206003193601126101de5760043567ffffffffffffffff811681036101de57610932602091611686565b346101de57610bd436610db1565b60ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754610c3c929060ff1615610c7c5761146e565b6040516003821015610c4f576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b610c846117cb565b61146e565b346101de575f6003193601126101de57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5760206003193601126101de57600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036101de57817f7965db0b0000000000000000000000000000000000000000000000000000000060209314908115610d87575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483610d80565b9060206003198301126101de5760043567ffffffffffffffff81116101de57826023820112156101de5780600401359267ffffffffffffffff84116101de57602484830101116101de576024019190565b60031960409101126101de576004359060243573ffffffffffffffffffffffffffffffffffffffff811681036101de5790565b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b9080602083519182815201916020808360051b8301019401925f915b838310610e8557505050505090565b9091929394602080610ea383601f1986600196030187528951610e35565b97019301930191939290610e76565b90600182811c92168015610ef9575b6020831014610ecc57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610ec1565b6001545f9291610f1282610eb2565b8082529160018116908115610f865750600114610f2d575050565b60015f9081529293509091907fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b838310610f6c575060209250010190565b600181602092949394548385870101520191019190610f5b565b60209495507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091509291921683830152151560051b010190565b6040810190811067ffffffffffffffff821117610fdc57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60c0810190811067ffffffffffffffff821117610fdc57604052565b6020810190811067ffffffffffffffff821117610fdc57604052565b6060810190811067ffffffffffffffff821117610fdc57604052565b60e0810190811067ffffffffffffffff821117610fdc57604052565b90601f601f19910116810190811067ffffffffffffffff821117610fdc57604052565b60021115610c4f57565b67ffffffffffffffff8111610fdc57601f01601f191660200190565b9291926110ce826110a6565b916110dc6040519384611079565b8294818452818301116101de578281602093845f960137010152565b9080601f830112156101de57816020611113933591016110c2565b90565b6020818303126101de5780359067ffffffffffffffff82116101de5701906020828203126101de576040519161114b83611025565b80359067ffffffffffffffff82116101de57016060818303126101de576040519161117583611041565b81358352602082013567ffffffffffffffff81116101de57816111999184016110f8565b6020840152604082013567ffffffffffffffff81116101de576111bc92016110f8565b6040820152815290565b156111cf575050565b7fd56bdc26000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b929192611209826110a6565b916112176040519384611079565b8294818452818301116101de578281602093845f96015e010152565b519060ff821682036101de57565b519067ffffffffffffffff821682036101de57565b91908260409103126101de5760405161126e81610fc0565b602061128781839561127f81611241565b855201611241565b910152565b519063ffffffff821682036101de57565b91908281039261012084126101de57604051916112b98361105d565b8294825167ffffffffffffffff81116101de57830182601f820112156101de576040916112ef84836020601f19955191016111fd565b865201126101de576113349060405161130781610fc0565b61131360208501611233565b815261132160408501611233565b6020820152602085015260608301611256565b604083015261134560a0820161128c565b606083015261135660c0820161128c565b608083015260e08101519081151582036101de576101009160a084015201519060028210156101de5760c00152565b51906fffffffffffffffffffffffffffffffff821682036101de57565b91908260609103126101de576040516113ba81611041565b60408082946113c881611385565b8452602081015160208501520151910152565b9190610180838203126101de57604051906113f582611009565b819380519167ffffffffffffffff83116101de576101408261141e8360a096611287960161129d565b865261142d83602083016113a2565b602087015261143f83608083016113a2565b604087015261145060e08201611385565b6060870152611463836101008301611256565b608087015201611256565b61147a91810190611116565b6114a98151517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b60208151015180518101906020818303126101de5760208101519167ffffffffffffffff83116101de576114e49260208092019201016113db565b906114ee826118f0565b6114f782611983565b916003831015610c4f578261162957908167ffffffffffffffff6020604060a0611113960193845184848201511685600354851c1681116115a1575b5050015160405161158681611578858201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b03601f198101835282611079565b51902092510151165f52600560205260405f20555b51611a40565b6115e08661162293511667ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006003541617600355565b7fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff00000000000000006003549260401b16911617600355565b5f80611533565b506001820361166e5761111390680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff600454161760045561159b565b6002821461167f576111139061159b565b5050600290565b67ffffffffffffffff165f52600560205260405f205480156116a55790565b7f5b48b457000000000000000000000000000000000000000000000000000000005f5260045ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101de570180359067ffffffffffffffff82116101de576020019181360383136101de57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101de570180359067ffffffffffffffff82116101de57602001918160051b360383136101de57565b67ffffffffffffffff8111610fdc5760051b60200190565b805182101561179e5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b335f9081527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e0602052604090205460ff161561180357565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004527fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b60245260445ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260ff60405f2054161561188a5750565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b156118c2575050565b7f6d23e8a3000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6119819061191681516fffffffffffffffffffffffffffffffff6060840151169061265e565b61197967ffffffffffffffff602060808185015160405161196b81611578868201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b519020940151015116611686565b8082146118b9565b565b67ffffffffffffffff602060a08301510151165f52600560205260405f205480155f146119b05750505f90565b604082019081516040516119f98161157860208201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b5190201491821592611a17575b505015611a1257600190565b600290565b6fffffffffffffffffffffffffffffffff91925060208291015151169151511611155f80611a06565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169080516040602083015192015192803b156101de575f92611ad892611aea604051968795869485947f41493c600000000000000000000000000000000000000000000000000000000086526004860152606060248601526064850190610e35565b90600319848303016044850152610e35565b03915afa8015611b0557611afb5750565b5f61198191611079565b6040513d5f823e3d90fd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f205416155f14611bdc57805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f2054165f14611bdc57805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b3567ffffffffffffffff811681036101de5790565b929190611ccb81611772565b93611cd96040519586611079565b602085838152019160051b8101918383116101de5781905b838210611cff575050505050565b813567ffffffffffffffff81116101de57602091611d2087849387016110f8565b815201910190611cf1565b90949291939480156125a5578101906020818303126101de5780359067ffffffffffffffff82116101de57016040818303126101de5760405191611d6e83610fc0565b813560028110156101de578352602082013567ffffffffffffffff81116101de57611d9992016110f8565b90602081019182528051611dac8161109c565b611db58161109c565b612022575051908151820160208101926020818303126101de57602081015167ffffffffffffffff81116101de570190608090829003126101de5760405192611dfd84610fc0565b602082015167ffffffffffffffff81116101de5782611e26836020604094611e2e9701016129d0565b8652016113a2565b9460208301958652611e658351517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b6020835101519182518301926020818503126101de5760208101519067ffffffffffffffff82116101de5701926040848203126101de5760405193611ea985610fc0565b6020810151855260408101519167ffffffffffffffff83116101de57611ed6926020809201920101612a36565b94611ef06020850196808852518015158061201657612b83565b5f805b87518051831015611fe357611f0b83611f1e9261178a565b5151611f18368888611cbf565b90612db8565b15611fd957508694611f7d611f92956fffffffffffffffffffffffffffffffff9a9895611f7661159b966020633b9aca009f9d8198611f5d915161178a565b51015190815181518082149182611fc5575b5050612bc1565b6001612c0f565b51950194611f8a86611caa565b875191612e2a565b516001815111611fa6575b50505151160490565b611fb2611fbe92611caa565b90848451511691612eba565b5f80611f9d565b909150898401209089830120145f80611f6f565b9060010190611ef3565b5061159b92506020915094611f7d611f92956fffffffffffffffffffffffffffffffff9a9895633b9aca009c9a98612c0f565b5061ffff811115612b83565b949060018695939495516120358161109c565b61203e8161109c565b146120855760ff86516120508161109c565b6120598161109c565b7fd3a2e6c9000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90919293945051926060602060405161209d81610fc0565b6040516120a981611009565b6040516120b58161105d565b8481526040516120c481610fc0565b5f81525f85820152848201526040516120dc81610fc0565b5f81525f8582015260408201525f858201525f60808201525f60a08201525f60c0820152815261210a612998565b83820152612116612998565b60408201525f8482015260405161212c81610fc0565b5f81525f84820152608082015260405161214581610fc0565b5f81525f8482015260a08201528152015283518401936020818603126101de5760208101519067ffffffffffffffff82116101de5701936020858203126101de576040519461219386611025565b60208101519067ffffffffffffffff82116101de5760206121bc92816121e995019201016129d0565b808652517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b602084510151948551860160208101966020818303126101de57602081015167ffffffffffffffff81116101de570190604090829003126101de576040519661223188610fc0565b602082015167ffffffffffffffff81116101de57816020612254928501016113db565b885260408201519167ffffffffffffffff83116101de576122789201602001612a36565b936122926020880195808752518015158061201657612b83565b60208101956122a087611caa565b67ffffffffffffffff60a08a5101519181602084015116918291161480612586575b6122cb85611caa565b9267ffffffffffffffff6122de8c611caa565b915116911561253857505050506122f99061159b89516118f0565b6123038751611983565b6003811015610c4f5780612507575061231b86611caa565b67ffffffffffffffff8060035460401c169116116124b4575b5060408651015160405161237d8161157860208201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b51902067ffffffffffffffff61239287611caa565b165f52600560205260405f20555b5f805b8551805183101561248957611f0b836123bb9261178a565b1561247f5750633b9aca0096936fffffffffffffffffffffffffffffffff9693611f76879460206123f261240a9660409b5161178a565b51015190815181518082149182612469575050612bc1565b6124318351858101519067ffffffffffffffff602060a08185015193015101511690612e2a565b516001815111612447575b505051015151160490565b61245361246292611caa565b90858585510151511691612eba565b5f8061243c565b9091506020840120906020830120145f80611f6f565b90600101906123a3565b50604095925061240a9150936fffffffffffffffffffffffffffffffff9693633b9aca009895612c0f565b6124c06124f591611caa565b67ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006003541617600355565b6125016115e086611caa565b5f612334565b60019150036123a0577f59bc8fd3000000000000000000000000000000000000000000000000000000005f5260045ffd5b6084945067ffffffffffffffff9081604051957fb8d8167e00000000000000000000000000000000000000000000000000000000875216600486015216602484015260448301526064820152fd5b5061259084611caa565b67ffffffffffffffff808451169116146122c2565b505090916125b860206125cd9201611caa565b91604051946125c686610fc0565b3691611cbf565b8352602083019182526040516125ec81611578866020830195866128ca565b5190205c915190518215612606575050633b9aca00900490565b9061265a612648926040519384937ff06fc33b000000000000000000000000000000000000000000000000000000008552604060048601526044850190610e5a565b90600319848303016024850152610e35565b0390fd5b906fffffffffffffffffffffffffffffffff633b9aca0091160442811161289b5780420342811161286e576107081061283f57508051516126a0600154610eb2565b1480612818575b815190156127d85750602081015160ff815116906002549160ff83169283821492836127bf575b6020015160ff169215612777575050505063ffffffff606082015116906004549163ffffffff831680820361274957505063ffffffff608081920151169160201c1680820361271b575050565b7f1eb5c1eb000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b7f73b1bde3000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6084945060ff90604051947fd382033a000000000000000000000000000000000000000000000000000000008652600486015260081c16602484015260448301526064820152fd5b6020810151600883901c60ff90811691161493506126ce565b61265a906040519182917ff6b6676b0000000000000000000000000000000000000000000000000000000083526040600484015261264860448401610f03565b508051602081519101206040516128328161059a81610f03565b60208151910120146126a7565b7f12e74add000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f20daedb6000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b9067ffffffffffffffff90939293168152604060208201526080810190835191604080830152825180915260a0820190602060a08260051b8501019401915f905b82821061294f57505050506020611113939401519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc082850301910152610e35565b9091929460208061298a837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6089600196030186528951610e35565b97019201920190929161290b565b604051906129a582611041565b5f6040838281528260208201520152565b9080601f830112156101de578151611113926020016111fd565b91906060838203126101de57604051906129e982611041565b819380518352602081015167ffffffffffffffff81116101de5782612a0f9183016129b6565b602084015260408101519167ffffffffffffffff83116101de5760409261128792016129b6565b9080601f830112156101de57815191612a4e83611772565b92612a5c6040519485611079565b80845260208085019160051b830101918383116101de5760208101915b838310612a8857505050505090565b825167ffffffffffffffff81116101de578201906040601f1983880301126101de5760405190612ab782610fc0565b602083015167ffffffffffffffff81116101de5760209084010187601f820112156101de578051612ae781611772565b91612af56040519384611079565b81835260208084019260051b820101918a83116101de5760208201905b838210612b555750505050825260408301519167ffffffffffffffff83116101de57612b46886020809695819601016129b6565b83820152815201920191612a79565b815167ffffffffffffffff81116101de57602091612b788e8480948801016129b6565b815201910190612b12565b15612b8b5750565b7fb0369c31000000000000000000000000000000000000000000000000000000005f52600452600160245261ffff60445260645ffd5b91909115612bcd575050565b9061265a612648926040519384937f5f1ca381000000000000000000000000000000000000000000000000000000008552604060048601526044850190610e35565b91909115612c1b575050565b604051907ffef760c700000000000000000000000000000000000000000000000000000000825280602483016020600485015252604482019260448260051b84010191815f907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603015b838310612c945786860387fd5b90919293947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8782030188528535828112156101de578301906020823592019167ffffffffffffffff81116101de5780360383136101de57602082601f19601f8480600198869897879852868601375f85828601015201160101970198019301919096939296612c87565b90612d5c5750805115612d3457602081519101fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580612daf575b612d6d575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15612d65565b908151815103611bdc575f5b8251811015612e2257612dd7818461178a565b5151612de3828461178a565b515103612e1b57612df4818461178a565b5160208151910120612e06828461178a565b516020815191012003612e1b57600101612dc4565b5050505f90565b505050600190565b91612e7f60209261197960405185810190612e7681611578888591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b51902091611686565b0151808203612e8c575050565b7f4b2dfe98000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b929190925f5b8451811015612ef8578083612ed76001938861178a565b51604051612eee81611578602082019489866128ca565b5190205d01612ec0565b505050905056fea164736f6c634300081c000a2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac47bd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10bad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5","sourceMap":"1355:25496:38:-:0;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;3774:52;1355:25496;3774:52;1355:25496;3836:47;1355:25496;3893:80;1355:25496;3983:51;1355:25496;;4059:60;;;1355:25496;4059:60;;1355:25496;;;;;;;;;4059:60;;1355:25496;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;-1:-1:-1;1355:25496:38;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;4129:21;1355:25496;;;-1:-1:-1;1355:25496:38;;;;;;;;;4220:36;1355:25496;;;;;2472:10;;;1355:25496;2472:10;;;;1355:25496;2472:10;;1355:25496;2472:10;1355:25496;;;;;4288:83;2472:10;;1355:25496;-1:-1:-1;;;;;1355:25496:38;;;;4529:44;;;:::i;:::-;;4484:351;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4484:351;4637:43;;4736:45;4637:43;;:::i;:::-;;4736:45;:::i;:::-;;4484:351;;2472:10;;;;-1:-1:-1;2472:10:38;1355:25496;2472:10;;;;-1:-1:-1;2472:10:38;;1355:25496;;;-1:-1:-1;2472:10:38;;1355:25496;2472:10;;-1:-1:-1;2472:10:38;1355:25496;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;1355:25496:38;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;1355:25496:38;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;-1:-1:-1;;1355:25496:38;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;1355:25496:38;;;;;;:::o;:::-;;;;;;;;;;:::o;6155:316:84:-;-1:-1:-1;;;;;1355:25496:38;;;;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;;;;;;-1:-1:-1;;1355:25496:38;;;;;735:10:112;;1355:25496:38;-1:-1:-1;;;;;;;;;;;1355:25496:38;;6346:40:84;6323:4;6400:11;:::o;6248:217::-;6442:12;1355:25496:38;6442:12:84;:::o;6155:316::-;-1:-1:-1;;;;;1355:25496:38;;;;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;;;;;;-1:-1:-1;;1355:25496:38;;;;;735:10:112;;1355:25496:38;-1:-1:-1;;;;;;;;;;;2576:33:38;-1:-1:-1;;;;;;;;;;;6346:40:84;1355:25496:38;6346:40:84;6323:4;6400:11;:::o;6155:316::-;1355:25496:38;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;;;;;;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;-1:-1:-1;;1355:25496:38;6323:4:84;1355:25496:38;;;735:10:112;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;1355:25496:38;;6346:40:84;6323:4;6400:11;:::o;6248:217::-;1355:25496:38;6442:12:84;:::o","linkReferences":{}},"deployedBytecode":{"object":"0x6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a714610d13575080630225293e14610cd957806308c84e7014610c895780630bece35614610bc657806323842fb814610b96578063248a9ca314610b6c5780632c3ee47414610b505780632f2ff15d14610b21578063314d4dff14610ae757806336568abe14610a8b5780634d6d9ffb146109a95780635972185a1461096f578063682ed5f01461084b57806391d1485414610802578063a217fddf146107e8578063ac9650d81461066a578063bd3ce6b01461057a578063ca7242f914610540578063d547741f1461050a578063ddba65371461021c578063e45a6d0d146101e25763ef913a4b1461010e575f80fd5b346101de575f6003193601126101de576101da60405160208082015261012060408201526101c6816101436101608201610f03565b60ff600254818116606085015260081c16608083015267ffffffffffffffff60035481811660a085015260401c1660c083015260ff60045463ffffffff811660e085015263ffffffff8160201c16610100850152818160401c16151561012085015260481c166101b28161109c565b61014083015203601f198101835282611079565b604051918291602083526020830190610e35565b0390f35b5f80fd5b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5761022a36610db1565b6004549160ff8360401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754610298929060ff16156104d5575b810190611116565b6102c78151517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b6020815101518051810160208101916020818303126101de57602081015167ffffffffffffffff81116101de57019061018090829003126101de576040519061030f82611009565b602081015167ffffffffffffffff81116101de5783908201602001906103349161129d565b825261034260408201611385565b60208301908152906103578460608301611256565b604084019081529161036c8560a08401611256565b60608501908152946103818160e085016113a2565b92608086019384526101400190610397916113a2565b9360a081019485525190516fffffffffffffffffffffffffffffffff166103bd9161265e565b516040805182516fffffffffffffffffffffffffffffffff166020808301918252840151828401529190920151606080840191909152825290610401608082611079565b51902090516020015167ffffffffffffffff1661041d90611686565b61042a91908181146118b9565b516040805182516fffffffffffffffffffffffffffffffff16602080830191825284015182840152919092015160608084019190915282529061046e608082611079565b51902090516020015167ffffffffffffffff1661048a90611686565b61049791908181146118b9565b516104a190611a40565b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff1668010000000000000000176004555f80f35b6104dd6117cb565b610290565b7f928b1233000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101de5761053e61051b36610e02565b90610539610534825f525f602052600160405f20015490565b611853565b611be2565b005b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de575f6003193601126101de576040516105a18161059a81610f03565b0382611079565b6101206040516105b081610fc0565b60025460ff8116825260ff602083019160081c16815260ff6040516105d481610fc0565b67ffffffffffffffff600354818116835281602084019160401c168152816004549385808660481c1697816106146040519d8d8f9e8f9081520190610e35565b9a511660208c0152511660408a0152511660608801525116608086015263ffffffff811660a086015263ffffffff8160201c1660c086015260401c16151560e08401526106608161109c565b6101008301520390f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de57366023820112156101de57806004013567ffffffffffffffff81116101de57602482013660248360051b850101116101de576020906040516106cf8382611079565b5f815282810191601f1984013684376106e785611772565b956106f56040519788611079565b858752601f1961070487611772565b01855f5b8281106107d8575050505f5b868110156107c5576001906107a15f8061076e8861073a60248760051b8a01018a6116cd565b8d8d6040959395519483869484860198893784019083820190898252519283915e010185815203601f198101835282611079565b5190305af43d156107bd573d90610784826110a6565b916107926040519384611079565b82523d5f8b84013e5b30612d1f565b6107ab828b61178a565b526107b6818a61178a565b5001610714565b60609061079b565b604051868152806101da8189018b610e5a565b606082828c010152018690610708565b346101de575f6003193601126101de5760206040515f8152f35b346101de5761081036610e02565b905f525f60205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060ff60405f2054166040519015158152f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de578060040160a060031983360301126101de5760ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475460ff1615610962575b60848201916108ee83836116cd565b90501561093a576020928261090681610932956116cd565b602461092a61092361091b606489018761171e565b9790966116cd565b36916110c2565b950191611d2b565b604051908152f35b7f1208b21b000000000000000000000000000000000000000000000000000000005f5260045ffd5b61096a6117cb565b6108df565b346101de575f6003193601126101de5760206040517fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b8152f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de5780600401608060031983360301126101de5760ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754909261093292909160ff1615610a7e575b610a61610a5482806116cd565b919092606485019061171e565b929091602460405195610a748988611079565b5f87520191611d2b565b610a866117cb565b610a47565b346101de57610a9936610e02565b3373ffffffffffffffffffffffffffffffffffffffff821603610abf5761053e91611be2565b7f6697b232000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5761053e610b3236610e02565b90610b4b610534825f525f602052600160405f20015490565b611b10565b346101de575f6003193601126101de5760206040516107088152f35b346101de5760206003193601126101de5760206109326004355f525f602052600160405f20015490565b346101de5760206003193601126101de5760043567ffffffffffffffff811681036101de57610932602091611686565b346101de57610bd436610db1565b60ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754610c3c929060ff1615610c7c5761146e565b6040516003821015610c4f576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b610c846117cb565b61146e565b346101de575f6003193601126101de57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5760206003193601126101de57600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036101de57817f7965db0b0000000000000000000000000000000000000000000000000000000060209314908115610d87575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483610d80565b9060206003198301126101de5760043567ffffffffffffffff81116101de57826023820112156101de5780600401359267ffffffffffffffff84116101de57602484830101116101de576024019190565b60031960409101126101de576004359060243573ffffffffffffffffffffffffffffffffffffffff811681036101de5790565b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b9080602083519182815201916020808360051b8301019401925f915b838310610e8557505050505090565b9091929394602080610ea383601f1986600196030187528951610e35565b97019301930191939290610e76565b90600182811c92168015610ef9575b6020831014610ecc57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610ec1565b6001545f9291610f1282610eb2565b8082529160018116908115610f865750600114610f2d575050565b60015f9081529293509091907fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b838310610f6c575060209250010190565b600181602092949394548385870101520191019190610f5b565b60209495507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091509291921683830152151560051b010190565b6040810190811067ffffffffffffffff821117610fdc57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60c0810190811067ffffffffffffffff821117610fdc57604052565b6020810190811067ffffffffffffffff821117610fdc57604052565b6060810190811067ffffffffffffffff821117610fdc57604052565b60e0810190811067ffffffffffffffff821117610fdc57604052565b90601f601f19910116810190811067ffffffffffffffff821117610fdc57604052565b60021115610c4f57565b67ffffffffffffffff8111610fdc57601f01601f191660200190565b9291926110ce826110a6565b916110dc6040519384611079565b8294818452818301116101de578281602093845f960137010152565b9080601f830112156101de57816020611113933591016110c2565b90565b6020818303126101de5780359067ffffffffffffffff82116101de5701906020828203126101de576040519161114b83611025565b80359067ffffffffffffffff82116101de57016060818303126101de576040519161117583611041565b81358352602082013567ffffffffffffffff81116101de57816111999184016110f8565b6020840152604082013567ffffffffffffffff81116101de576111bc92016110f8565b6040820152815290565b156111cf575050565b7fd56bdc26000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b929192611209826110a6565b916112176040519384611079565b8294818452818301116101de578281602093845f96015e010152565b519060ff821682036101de57565b519067ffffffffffffffff821682036101de57565b91908260409103126101de5760405161126e81610fc0565b602061128781839561127f81611241565b855201611241565b910152565b519063ffffffff821682036101de57565b91908281039261012084126101de57604051916112b98361105d565b8294825167ffffffffffffffff81116101de57830182601f820112156101de576040916112ef84836020601f19955191016111fd565b865201126101de576113349060405161130781610fc0565b61131360208501611233565b815261132160408501611233565b6020820152602085015260608301611256565b604083015261134560a0820161128c565b606083015261135660c0820161128c565b608083015260e08101519081151582036101de576101009160a084015201519060028210156101de5760c00152565b51906fffffffffffffffffffffffffffffffff821682036101de57565b91908260609103126101de576040516113ba81611041565b60408082946113c881611385565b8452602081015160208501520151910152565b9190610180838203126101de57604051906113f582611009565b819380519167ffffffffffffffff83116101de576101408261141e8360a096611287960161129d565b865261142d83602083016113a2565b602087015261143f83608083016113a2565b604087015261145060e08201611385565b6060870152611463836101008301611256565b608087015201611256565b61147a91810190611116565b6114a98151517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b60208151015180518101906020818303126101de5760208101519167ffffffffffffffff83116101de576114e49260208092019201016113db565b906114ee826118f0565b6114f782611983565b916003831015610c4f578261162957908167ffffffffffffffff6020604060a0611113960193845184848201511685600354851c1681116115a1575b5050015160405161158681611578858201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b03601f198101835282611079565b51902092510151165f52600560205260405f20555b51611a40565b6115e08661162293511667ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006003541617600355565b7fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff00000000000000006003549260401b16911617600355565b5f80611533565b506001820361166e5761111390680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff600454161760045561159b565b6002821461167f576111139061159b565b5050600290565b67ffffffffffffffff165f52600560205260405f205480156116a55790565b7f5b48b457000000000000000000000000000000000000000000000000000000005f5260045ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101de570180359067ffffffffffffffff82116101de576020019181360383136101de57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101de570180359067ffffffffffffffff82116101de57602001918160051b360383136101de57565b67ffffffffffffffff8111610fdc5760051b60200190565b805182101561179e5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b335f9081527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e0602052604090205460ff161561180357565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004527fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b60245260445ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260ff60405f2054161561188a5750565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b156118c2575050565b7f6d23e8a3000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6119819061191681516fffffffffffffffffffffffffffffffff6060840151169061265e565b61197967ffffffffffffffff602060808185015160405161196b81611578868201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b519020940151015116611686565b8082146118b9565b565b67ffffffffffffffff602060a08301510151165f52600560205260405f205480155f146119b05750505f90565b604082019081516040516119f98161157860208201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b5190201491821592611a17575b505015611a1257600190565b600290565b6fffffffffffffffffffffffffffffffff91925060208291015151169151511611155f80611a06565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169080516040602083015192015192803b156101de575f92611ad892611aea604051968795869485947f41493c600000000000000000000000000000000000000000000000000000000086526004860152606060248601526064850190610e35565b90600319848303016044850152610e35565b03915afa8015611b0557611afb5750565b5f61198191611079565b6040513d5f823e3d90fd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f205416155f14611bdc57805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f2054165f14611bdc57805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b3567ffffffffffffffff811681036101de5790565b929190611ccb81611772565b93611cd96040519586611079565b602085838152019160051b8101918383116101de5781905b838210611cff575050505050565b813567ffffffffffffffff81116101de57602091611d2087849387016110f8565b815201910190611cf1565b90949291939480156125a5578101906020818303126101de5780359067ffffffffffffffff82116101de57016040818303126101de5760405191611d6e83610fc0565b813560028110156101de578352602082013567ffffffffffffffff81116101de57611d9992016110f8565b90602081019182528051611dac8161109c565b611db58161109c565b612022575051908151820160208101926020818303126101de57602081015167ffffffffffffffff81116101de570190608090829003126101de5760405192611dfd84610fc0565b602082015167ffffffffffffffff81116101de5782611e26836020604094611e2e9701016129d0565b8652016113a2565b9460208301958652611e658351517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b6020835101519182518301926020818503126101de5760208101519067ffffffffffffffff82116101de5701926040848203126101de5760405193611ea985610fc0565b6020810151855260408101519167ffffffffffffffff83116101de57611ed6926020809201920101612a36565b94611ef06020850196808852518015158061201657612b83565b5f805b87518051831015611fe357611f0b83611f1e9261178a565b5151611f18368888611cbf565b90612db8565b15611fd957508694611f7d611f92956fffffffffffffffffffffffffffffffff9a9895611f7661159b966020633b9aca009f9d8198611f5d915161178a565b51015190815181518082149182611fc5575b5050612bc1565b6001612c0f565b51950194611f8a86611caa565b875191612e2a565b516001815111611fa6575b50505151160490565b611fb2611fbe92611caa565b90848451511691612eba565b5f80611f9d565b909150898401209089830120145f80611f6f565b9060010190611ef3565b5061159b92506020915094611f7d611f92956fffffffffffffffffffffffffffffffff9a9895633b9aca009c9a98612c0f565b5061ffff811115612b83565b949060018695939495516120358161109c565b61203e8161109c565b146120855760ff86516120508161109c565b6120598161109c565b7fd3a2e6c9000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90919293945051926060602060405161209d81610fc0565b6040516120a981611009565b6040516120b58161105d565b8481526040516120c481610fc0565b5f81525f85820152848201526040516120dc81610fc0565b5f81525f8582015260408201525f858201525f60808201525f60a08201525f60c0820152815261210a612998565b83820152612116612998565b60408201525f8482015260405161212c81610fc0565b5f81525f84820152608082015260405161214581610fc0565b5f81525f8482015260a08201528152015283518401936020818603126101de5760208101519067ffffffffffffffff82116101de5701936020858203126101de576040519461219386611025565b60208101519067ffffffffffffffff82116101de5760206121bc92816121e995019201016129d0565b808652517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b602084510151948551860160208101966020818303126101de57602081015167ffffffffffffffff81116101de570190604090829003126101de576040519661223188610fc0565b602082015167ffffffffffffffff81116101de57816020612254928501016113db565b885260408201519167ffffffffffffffff83116101de576122789201602001612a36565b936122926020880195808752518015158061201657612b83565b60208101956122a087611caa565b67ffffffffffffffff60a08a5101519181602084015116918291161480612586575b6122cb85611caa565b9267ffffffffffffffff6122de8c611caa565b915116911561253857505050506122f99061159b89516118f0565b6123038751611983565b6003811015610c4f5780612507575061231b86611caa565b67ffffffffffffffff8060035460401c169116116124b4575b5060408651015160405161237d8161157860208201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b51902067ffffffffffffffff61239287611caa565b165f52600560205260405f20555b5f805b8551805183101561248957611f0b836123bb9261178a565b1561247f5750633b9aca0096936fffffffffffffffffffffffffffffffff9693611f76879460206123f261240a9660409b5161178a565b51015190815181518082149182612469575050612bc1565b6124318351858101519067ffffffffffffffff602060a08185015193015101511690612e2a565b516001815111612447575b505051015151160490565b61245361246292611caa565b90858585510151511691612eba565b5f8061243c565b9091506020840120906020830120145f80611f6f565b90600101906123a3565b50604095925061240a9150936fffffffffffffffffffffffffffffffff9693633b9aca009895612c0f565b6124c06124f591611caa565b67ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006003541617600355565b6125016115e086611caa565b5f612334565b60019150036123a0577f59bc8fd3000000000000000000000000000000000000000000000000000000005f5260045ffd5b6084945067ffffffffffffffff9081604051957fb8d8167e00000000000000000000000000000000000000000000000000000000875216600486015216602484015260448301526064820152fd5b5061259084611caa565b67ffffffffffffffff808451169116146122c2565b505090916125b860206125cd9201611caa565b91604051946125c686610fc0565b3691611cbf565b8352602083019182526040516125ec81611578866020830195866128ca565b5190205c915190518215612606575050633b9aca00900490565b9061265a612648926040519384937ff06fc33b000000000000000000000000000000000000000000000000000000008552604060048601526044850190610e5a565b90600319848303016024850152610e35565b0390fd5b906fffffffffffffffffffffffffffffffff633b9aca0091160442811161289b5780420342811161286e576107081061283f57508051516126a0600154610eb2565b1480612818575b815190156127d85750602081015160ff815116906002549160ff83169283821492836127bf575b6020015160ff169215612777575050505063ffffffff606082015116906004549163ffffffff831680820361274957505063ffffffff608081920151169160201c1680820361271b575050565b7f1eb5c1eb000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b7f73b1bde3000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6084945060ff90604051947fd382033a000000000000000000000000000000000000000000000000000000008652600486015260081c16602484015260448301526064820152fd5b6020810151600883901c60ff90811691161493506126ce565b61265a906040519182917ff6b6676b0000000000000000000000000000000000000000000000000000000083526040600484015261264860448401610f03565b508051602081519101206040516128328161059a81610f03565b60208151910120146126a7565b7f12e74add000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f20daedb6000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b9067ffffffffffffffff90939293168152604060208201526080810190835191604080830152825180915260a0820190602060a08260051b8501019401915f905b82821061294f57505050506020611113939401519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc082850301910152610e35565b9091929460208061298a837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6089600196030186528951610e35565b97019201920190929161290b565b604051906129a582611041565b5f6040838281528260208201520152565b9080601f830112156101de578151611113926020016111fd565b91906060838203126101de57604051906129e982611041565b819380518352602081015167ffffffffffffffff81116101de5782612a0f9183016129b6565b602084015260408101519167ffffffffffffffff83116101de5760409261128792016129b6565b9080601f830112156101de57815191612a4e83611772565b92612a5c6040519485611079565b80845260208085019160051b830101918383116101de5760208101915b838310612a8857505050505090565b825167ffffffffffffffff81116101de578201906040601f1983880301126101de5760405190612ab782610fc0565b602083015167ffffffffffffffff81116101de5760209084010187601f820112156101de578051612ae781611772565b91612af56040519384611079565b81835260208084019260051b820101918a83116101de5760208201905b838210612b555750505050825260408301519167ffffffffffffffff83116101de57612b46886020809695819601016129b6565b83820152815201920191612a79565b815167ffffffffffffffff81116101de57602091612b788e8480948801016129b6565b815201910190612b12565b15612b8b5750565b7fb0369c31000000000000000000000000000000000000000000000000000000005f52600452600160245261ffff60445260645ffd5b91909115612bcd575050565b9061265a612648926040519384937f5f1ca381000000000000000000000000000000000000000000000000000000008552604060048601526044850190610e35565b91909115612c1b575050565b604051907ffef760c700000000000000000000000000000000000000000000000000000000825280602483016020600485015252604482019260448260051b84010191815f907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603015b838310612c945786860387fd5b90919293947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8782030188528535828112156101de578301906020823592019167ffffffffffffffff81116101de5780360383136101de57602082601f19601f8480600198869897879852868601375f85828601015201160101970198019301919096939296612c87565b90612d5c5750805115612d3457602081519101fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580612daf575b612d6d575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15612d65565b908151815103611bdc575f5b8251811015612e2257612dd7818461178a565b5151612de3828461178a565b515103612e1b57612df4818461178a565b5160208151910120612e06828461178a565b516020815191012003612e1b57600101612dc4565b5050505f90565b505050600190565b91612e7f60209261197960405185810190612e7681611578888591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b51902091611686565b0151808203612e8c575050565b7f4b2dfe98000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b929190925f5b8451811015612ef8578083612ed76001938861178a565b51604051612eee81611578602082019489866128ca565b5190205d01612ec0565b505050905056fea164736f6c634300081c000a","sourceMap":"1355:25496:38:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;4960:23;;;;1355:25496;;;;;;4960:23;1355:25496;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4960:23;1355:25496;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;4960:23;-1:-1:-1;;4960:23:38;;;;;;:::i;:::-;1355:25496;;;;;4960:23;1355:25496;;4960:23;1355:25496;;;;:::i;:::-;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;1648:48;1355:25496;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;9619:70;;1355:25496;;;26731:42;26727:105;;1355:25496;9619:70;;;;:::i;:::-;9699:196;9720:30;;1355:25496;9759:25;9720:64;;;9699:196;:::i;:::-;1355:25496;9982:30;;:43;;1355:25496;;9971:95;;1355:25496;9971:95;;1355:25496;;;;;;;;;9971:95;;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;19046:18;1355:25496;;;;19066:11;;;:::i;:::-;19282:29;1355:25496;;;;;;;;19271:41;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;19271:41;;;;1355:25496;;19271:41;:::i;:::-;1355:25496;19261:52;;19381:21;;1355:25496;19381:36;1355:25496;;;19359:59;;;:::i;:::-;19428:179;;19449:54;;;;19428:179;:::i;:::-;19811:29;1355:25496;;;;;;;;19800:41;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;19800:41;;;;1355:25496;;19800:41;:::i;:::-;1355:25496;19790:52;;19910:21;;1355:25496;19910:36;1355:25496;;;19888:59;;;:::i;:::-;19957:179;;19978:54;;;;19957:179;:::i;:::-;10139:30;;;;:::i;:::-;1355:25496;;;;;;;;;26727:105;26800:20;;:::i;:::-;26727:105;;1355:25496;;;;;;;;;;;4723:26:84;1355:25496:38;;;:::i;:::-;4693:18:84;2484:4;4693:18;;3877:6;1355:25496:38;3877:6:84;1355:25496:38;;3877:22:84;1355:25496:38;3877:6:84;1355:25496:38;3877:22:84;1355:25496:38;3786:120:84;;4693:18;2484:4;:::i;:::-;4723:26;:::i;:::-;1355:25496:38;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;1551:51;1355:25496;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;:::i;:::-;2095:51;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;2095:51;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;;1355:25496:38;;;:::i;:::-;;;;;;;;;;1528:13:115;;;1355:25496:38;1560:3:115;1543:15;;;;;;1349:26;1355:25496:38;4107:55:110;1355:25496:38;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;:::i;:::-;4065:25:110;1629:4:115;;4065:25:110;;1355:25496:38;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;1629:4:115;4107:55:110;:::i;:::-;1579:88:115;;;;:::i;:::-;;;;;;:::i;:::-;;1355:25496:38;1528:13:115;;1355:25496:38;;;;;1543:15:115;1355:25496:38;;;;;;;;;;1543:15:115;1355:25496:38;:::i;:::-;;;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;2930:29:84;1355:25496:38;-1:-1:-1;1355:25496:38;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;26731:42;26727:105;;1355:25496;7188:10;;;;;;;;:::i;:::-;:21;;;1355:25496;;;7253:10;;;;7241:64;7253:10;;:::i;:::-;7265:16;1355:25496;7294:10;7283:9;;;;;;:::i;:::-;7294:10;;;;:::i;:::-;1355:25496;;;:::i;:::-;7265:16;;7241:64;;:::i;:::-;1355:25496;;;;;;;;;;;;;26727:105;26800:20;;:::i;:::-;26727:105;;1355:25496;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;2576:33;1355:25496;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;7544:63;;1355:25496;;;;26731:42;26727:105;;1355:25496;7586:9;7556:10;;;;:::i;:::-;7586:9;;;;;;;;:::i;:::-;1355:25496;;;7568:16;1355:25496;;;;;;;:::i;:::-;;;;7568:16;7544:63;;:::i;26727:105::-;26800:20;;:::i;:::-;26727:105;;1355:25496;;;;;;;:::i;:::-;735:10:112;1355:25496:38;;;5397:34:84;5393:102;;5505:37;;;:::i;5393:102::-;5454:30;1355:25496:38;5454:30:84;1355:25496:38;;5454:30:84;1355:25496:38;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;1854:50;1355:25496;;;;;;;4306:25:84;1355:25496:38;;;:::i;:::-;4276:18:84;2484:4;4276:18;;3877:6;1355:25496:38;3877:6:84;1355:25496:38;;3877:22:84;1355:25496:38;3877:6:84;1355:25496:38;3877:22:84;1355:25496:38;3786:120:84;;2484:4;4306:25;:::i;1355:25496:38:-;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;2472:10;1355:25496;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;3877:6:84;1355:25496:38;3877:6:84;1355:25496:38;;3877:22:84;1355:25496:38;3877:6:84;1355:25496:38;3877:22:84;1355:25496:38;3786:120:84;;1355:25496:38;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;5401:1560;;1355:25496;;;26731:42;26727:105;;5401:1560;:::i;:::-;1355:25496;;;;;;;;;;;;;;;;;;;;;;;26727:105;26800:20;;:::i;:::-;5401:1560;:::i;1355:25496::-;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;1950:38;1355:25496;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;1742:66;1355:25496;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;2649:47:84;2664:32;1355:25496:38;2649:47:84;;:87;;;;;1355:25496:38;;;;;;;2649:87:84;844:25:121;829:40;;;2649:87:84;;;1355:25496:38;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;2095:51;1355:25496;;;;;;;:::i;:::-;;;;;2095:51;1355:25496;;;2095:51;;;;1355:25496;;;;;;;:::o;:::-;2095:51;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;:::o;:::-;2095:51;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;1355:25496:38;;;:::o;:::-;;;;;;;;-1:-1:-1;;1355:25496:38;;;;:::o;:::-;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::o;:::-;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;:::i;5401:1560::-;5644:58;5401:1560;5644:58;;;;:::i;:::-;5712:186;5733:24;;1355:25496;5766:26;5733:59;;;5712:186;:::i;:::-;5985:37;:24;;:37;;1355:25496;;5974:89;;1355:25496;5985:37;1355:25496;;;;;;5985:37;5974:89;;1355:25496;;;;;;;;5974:89;5985:37;5974:89;;;1355:25496;;;;:::i;:::-;6108:6;;;;:::i;:::-;6171:26;;;:::i;:::-;1355:25496;;;;;;;6211:52;;;6344:16;;1355:25496;5985:37;1355:25496;6344:16;6899:24;6344:16;;;;;:31;;;;1355:25496;;;;;;;;6344:73;;6340:155;;6207:666;6586:24;;;;1355:25496;;6575:36;;;;;;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6575:36;;-1:-1:-1;;6575:36:38;;;;;;:::i;:::-;1355:25496;6565:47;;6530:16;;:31;1355:25496;;-1:-1:-1;1355:25496:38;6508:21;5985:37;1355:25496;;-1:-1:-1;1355:25496:38;;6207:666;6899:24;;:::i;6340:155::-;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6340:155;;;;6207:666;-1:-1:-1;1355:25496:38;6633:58;;1355:25496;;6899:24;1355:25496;;;6707:20;1355:25496;;;6707:20;1355:25496;6207:666;;6629:244;6771:34;6755:50;;6751:122;;6899:24;6629:244;6207:666;;6751:122;6821:41;;6771:34;6821:41;:::o;5036:228::-;1355:25496;;-1:-1:-1;1355:25496:38;5145:21;1355:25496;;;-1:-1:-1;1355:25496:38;;5200:9;;1355:25496;;5036:228;:::o;1355:25496::-;;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;3175:103:84;735:10:112;2930:6:84;1355:25496:38;;;;;;;;;;;;3495:23:84;3491:108;;3175:103::o;3491:108::-;3541:47;2930:6;3541:47;735:10:112;3541:47:84;1355:25496:38;2576:33;1355:25496;;;2930:6:84;3541:47;3175:103;1355:25496:38;2930:6:84;1355:25496:38;2930:6:84;1355:25496:38;;;2930:6:84;1355:25496:38;;735:10:112;1355:25496:38;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;3495:23:84;3491:108;;3175:103;:::o;3491:108::-;3541:47;2930:6;3541:47;735:10:112;3541:47:84;1355:25496:38;;;;2930:6:84;3541:47;1355:25496:38;;;;;;:::o;:::-;;;;;;;;;;;18217:570;18605:175;18217:570;18383:11;18363:18;;1355:25496;18383:11;;;1355:25496;;18383:11;;:::i;:::-;18537:58;1355:25496;18462:28;18559:20;18462:28;;;;1355:25496;;18451:40;;;;;;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18451:40;1355:25496;18441:51;;18559:20;;;:35;1355:25496;;18537:58;:::i;:::-;18626:52;;;18605:175;:::i;:::-;18217:570::o;22962:1028::-;1355:25496;23184:31;:16;;;;:31;1355:25496;;;;23162:21;23184:31;1355:25496;;;;;23230:32;;23226:758;23230:32;;;23350:43;;1355:25496;23350:43;:::o;23226:758::-;1355:25496;23470:24;;;;;1355:25496;;23459:36;;;23184:31;23459:36;;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23459:36;1355:25496;23449:47;;23427:69;;;;:165;;;23226:758;-1:-1:-1;;23410:574:38;;;23768:42;23761:49;:::o;23410:574::-;23939:34;23932:41;:::o;23427:165::-;1355:25496;23516:28;;;23184:31;23516:28;;;;1355:25496;;23558:24;;1355:25496;;-1:-1:-1;23516:76:38;23427:165;;;;24151:152;1355:25496;24231:8;1355:25496;;;;24284:11;24264:18;;;;24284:11;;;24231:65;;;;;;-1:-1:-1;1355:25496:38;;;;24284:11;1355:25496;24231:65;;;;;;;1355:25496;24231:65;;;;;1355:25496;;;;;;;;;;;:::i;:::-;;-1:-1:-1;;1355:25496:38;;;;;;;;;:::i;:::-;24231:65;;;;;;;;;;24151:152;:::o;24231:65::-;-1:-1:-1;24231:65:38;;;:::i;:::-;24284:11;1355:25496;;-1:-1:-1;1355:25496:38;;;;;6155:316:84;1355:25496:38;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;6252:23:84;6248:217;1355:25496:38;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;-1:-1:-1;1355:25496:38;6323:4:84;1355:25496:38;;;;;;;;735:10:112;1355:25496:38;;6346:40:84;;1355:25496:38;6346:40:84;;6323:4;6400:11;:::o;6248:217::-;6442:12;;1355:25496:38;6442:12:84;:::o;6708:317::-;1355:25496:38;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;6802:217:84;1355:25496:38;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;-1:-1:-1;1355:25496:38;;;;;;;;735:10:112;1355:25496:38;;6900:40:84;;1355:25496:38;6900:40:84;;1355:25496:38;6954:11:84;:::o;1355:25496:38:-;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;8191:1064;;;;;;;8421:17;;8417:181;;8665:52;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;8731:83;;8876:21;;1355:25496;;;10929:60;;1355:25496;10929:60;;1355:25496;;;;;;;;;10929:60;;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;10999:160;11020:14;;1355:25496;11043:23;11020:46;;;10999:160;:::i;:::-;1355:25496;11242:14;;:27;;1355:25496;;;11231:75;;1355:25496;;;;;;;;;11231:75;;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;11231:75;1355:25496;11231:75;;;1355:25496;;;;:::i;:::-;;11316:177;1355:25496;;;;;;;;11337:25;;;:70;;;11316:177;:::i;:::-;1355:25496;;11663:3;11640:14;;1355:25496;;11636:25;;;;;11703:17;;11691:43;11703:17;;:::i;:::-;;:22;1355:25496;;;;;:::i;:::-;11691:43;;:::i;:::-;11690:44;11686:99;;11824:14;;;12132:50;12335:14;11824;1355:25496;11824:14;;;11865:185;12280:27;11824:14;1355:25496;26385:3;11824:14;;;;:17;:14;;:17;:::i;:::-;;:23;;1355:25496;;;;;11894:30;;;:72;;;;11663:3;11865:185;;;:::i;:::-;1355:25496;12132:50;:::i;:::-;1355:25496;12252:26;;;;;;:::i;:::-;12280:27;;;;:::i;12335:14::-;12464;1355:25496;;;12464:25;12460:152;;11616:503;12651:27;;;1355:25496;;;8830:81;:::o;12460:152::-;12519:26;12505:96;12519:26;;:::i;:::-;12563:27;;;;1355:25496;;12505:96;;:::i;:::-;12460:152;;;;11894:72;1355:25496;;;;;;11928:16;1355:25496;;;;11948:18;11928:38;11894:72;;;;11686:99;11758:8;1355:25496;;11621:13;;;11636:25;;12280:27;11636:25;;1355:25496;11636:25;;;12132:50;12335:14;11636:25;1355:25496;11636:25;;;26385:3;11636:25;;;12132:50;:::i;11337:70::-;11366:41;1355:25496;11366:41;;;11316:177;:::i;8727:421::-;1355:25496;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;8932:98;8928:220;;1355:25496;8727:421;1355:25496;;;;:::i;:::-;;;;:::i;:::-;9188:60;1355:25496;9188:60;1355:25496;9188:60;1355:25496;;;9188:60;8928:220;9102:21;;;;;;;1355:25496;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;13696:75;;1355:25496;;;;;;;;;13696:75;;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;13696:75;;13785:208;13696:75;;1355:25496;;;;:::i;:::-;;;;;13833:41;13810:64;;;13785:208;:::i;:::-;1355:25496;14028:14;;:27;;1355:25496;;;14017:95;;1355:25496;14017:95;;1355:25496;;;;;;;;;14017:95;;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;14126:189;1355:25496;;;;;;;;14151:25;;;:70;;;14126:189;:::i;:::-;1355:25496;14355:26;;;;;;:::i;:::-;1355:25496;;14385:25;;:35;;:50;;1355:25496;14385:50;;1355:25496;;;;;;14355:80;:184;;;8928:220;14598:26;;;:::i;:::-;14646;1355:25496;14646:26;;;:::i;:::-;1355:25496;;;;;;;14897:25;;;;14954:14;14897:25;;;;;:::i;14954:14::-;15080:45;15099:25;;15080:45;:::i;:::-;1355:25496;;;;;;15143:52;;;15284:26;;;;:::i;:::-;1355:25496;;;;;;;;;15284:68;15280:153;;15139:599;15543:25;1355:25496;15543:25;;:43;;1355:25496;;15532:55;;;1355:25496;15532:55;;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15532:55;1355:25496;15522:66;;1355:25496;15472:26;;;:::i;:::-;1355:25496;;;15450:21;1355:25496;;;;;;15139:599;1355:25496;;15927:3;15904:14;;1355:25496;;15900:25;;;;;15967:17;;15955:43;15967:17;;:::i;15955:43::-;15954:44;15950:99;;16088:14;26385:3;16088:14;;1355:25496;16088:14;;16129:185;16088:14;;1355:25496;16088:17;16396:50;16088:14;1355:25496;16088:14;;:17;:::i;:::-;;:23;;1355:25496;;;;;16158:30;;;:72;;;;16129:185;;;:::i;16396:50::-;16632:43;16506:25;;:43;;;;:48;1355:25496;;;16506:48;;;1355:25496;16568:35;;;:50;1355:25496;;16632:43;;:::i;:::-;16799:14;1355:25496;;;16799:25;16795:198;;15880:503;17032:25;;;:43;;1355:25496;;;9046:91;:::o;16795:198::-;16871:26;16840:142;16871:26;;:::i;:::-;16915:25;;;;;:43;;1355:25496;;16840:142;;:::i;:::-;16795:198;;;;16158:72;1355:25496;;;;;;16192:16;1355:25496;;;;16212:18;16192:38;16158:72;;;;15950:99;16022:8;1355:25496;;15885:13;;;15900:25;;1355:25496;15900:25;;;16396:50;15900:25;;;1355:25496;15900:25;;26385:3;15900:25;;16396:50;:::i;15280:153::-;1355:25496;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;15280:153;;;15139:599;1355:25496;;;15613:58;15139:599;15609:129;15698:25;1355:25496;15698:25;;1355:25496;15698:25;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14355:184;14459:26;;;;:::i;:::-;1355:25496;;;;;;;14459:80;14355:184;;8417:181;8522:26;;;;;;1355:25496;8522:26;;;:::i;:::-;1355:25496;;;;;;;:::i;:::-;;;;:::i;:::-;;;8522:26;8550:35;;1355:25496;;;;;25522:31;;;;8522:26;25522:31;;;;;:::i;:::-;1355:25496;25512:42;;4288:69:119;25671:11:38;;25684:12;;25632:14;;1355:25496;;8489:98;;26385:3;8489:98;1355:25496;8482:105;:::o;1355:25496::-;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;-1:-1:-1;;1355:25496:38;;;;;;;;;:::i;:::-;;;;20400:2140;;1355:25496;26385:3;20400:2140;1355:25496;;20608:15;20583:40;;1355:25496;;20608:15;;1355:25496;20608:15;1355:25496;;;;2472:10;-1:-1:-1;1355:25496:38;;21386:25;;;1355:25496;;21429:11;1355:25496;;:::i;:::-;21380:76;:180;;;20400:2140;21611:25;;1355:25496;;;;21678:28;;;;;1355:25496;;;;;21720:22;1355:25496;;;;;21678:74;;;;:172;;;;20400:2140;21678:28;22062:40;1355:25496;;;;;;;22157:32;;;;1355:25496;22157:32;;;1355:25496;;;22193:26;1355:25496;;;;;22157:62;;;1355:25496;;22358:33;;1355:25496;22358:33;;;;1355:25496;;;21678:28;1355:25496;;22358:64;;;1355:25496;;20400:2140;;:::o;1355:25496::-;;-1:-1:-1;1355:25496:38;22193:26;1355:25496;;;;-1:-1:-1;1355:25496:38;;;-1:-1:-1;1355:25496:38;22193:26;1355:25496;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21678:172;:28;21772:40;;1355:25496;;;;;;;;;;;21772:78;;-1:-1:-1;21678:172:38;;1355:25496;;;;;;;;;;;;;;;;;;;;;:::i;21380:180::-;21492:25;;;1355:25496;;;;;21476:43;1355:25496;;;;;;;:::i;:::-;;;;;;21523:37;21476:84;21380:180;;1355:25496;;-1:-1:-1;1355:25496:38;20608:15;1355:25496;;;;;-1:-1:-1;1355:25496:38;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;-1:-1:-1;1355:25496:38;20608:15;1355:25496;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;1355:25496:38;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::o;:::-;;;;;;8961:69;1355:25496;;14284:16;1355:25496;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;4437:582:110;;4609:8;;-1:-1:-1;1355:25496:38;;5690:21:110;:17;;5815:105;;;;;;5686:301;5957:19;5710:1;5957:19;;5710:1;5957:19;4605:408;1355:25496:38;;4857:22:110;:49;;;4605:408;4853:119;;4985:17;;:::o;4853:119::-;1355:25496:38;4933:24:110;;4878:1;4933:24;1355:25496:38;4933:24:110;1355:25496:38;;4878:1:110;4933:24;4857:49;4883:18;;;:23;4857:49;;358:427:47;;1355:25496:38;;;;452:20:47;448:63;;1355:25496:38;554:3:47;1355:25496:38;;540:12:47;;;;;577:4;;;;:::i;:::-;;1355:25496:38;592:4:47;;;;:::i;:::-;;1355:25496:38;577:26:47;573:77;;677:4;;;;:::i;:::-;;1355:25496:38;;;;;667:15:47;696:4;;;;:::i;:::-;;1355:25496:38;;;;;686:15:47;667:34;663:85;;1355:25496:38;;525:13:47;;663:85;721:12;;;1355:25496:38;721:12:47;:::o;540:::-;;;;1355:25496:38;358:427:47;:::o;17339:759:38:-;;17732:177;17609:33;17339:759;17688:34;1355:25496;;17609:33;;;;;;;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17609:33;1355:25496;17599:44;;17688:34;;:::i;17732:177::-;17965:26;1355:25496;17941:50;;;1355:25496;;17339:759;;:::o;1355:25496::-;;-1:-1:-1;1355:25496:38;;;;;;-1:-1:-1;1355:25496:38;24776:318;;;;;24914:1;24937:3;1355:25496;;24917:18;;;;;25011:10;;;1355:25496;25011:10;;;:::i;:::-;;1355:25496;;24987:35;;;;;;;;;;:::i;:::-;1355:25496;24977:46;;4520:68:119;1355:25496:38;24902:13;;24917:18;;;;;;24776:318::o","linkReferences":{},"immutableReferences":{"6367":[{"start":1367,"length":32},{"start":5250,"length":32}],"6370":[{"start":505,"length":32},{"start":7742,"length":32}],"6373":[{"start":3312,"length":32},{"start":8642,"length":32}],"6376":[{"start":672,"length":32},{"start":2814,"length":32}],"6380":[{"start":3253,"length":32},{"start":6743,"length":32}]}},"methodIdentifiers":{"ALLOWED_SP1_CLOCK_DRIFT()":"2c3ee474","DEFAULT_ADMIN_ROLE()":"a217fddf","MEMBERSHIP_PROGRAM_VKEY()":"e45a6d0d","MISBEHAVIOUR_PROGRAM_VKEY()":"314d4dff","PROOF_SUBMITTER_ROLE()":"5972185a","UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY()":"0225293e","UPDATE_CLIENT_PROGRAM_VKEY()":"ca7242f9","VERIFIER()":"08c84e70","clientState()":"bd3ce6b0","getClientState()":"ef913a4b","getConsensusStateHash(uint64)":"23842fb8","getRoleAdmin(bytes32)":"248a9ca3","grantRole(bytes32,address)":"2f2ff15d","hasRole(bytes32,address)":"91d14854","misbehaviour(bytes)":"ddba6537","multicall(bytes[])":"ac9650d8","renounceRole(bytes32,address)":"36568abe","revokeRole(bytes32,address)":"d547741f","supportsInterface(bytes4)":"01ffc9a7","updateClient(bytes)":"0bece356","verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":"682ed5f0","verifyNonMembership((bytes,(uint64,uint64),bytes[]))":"4d6d9ffb"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"updateClientProgramVkey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"membershipProgramVkey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"updateClientAndMembershipProgramVkey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"misbehaviourProgramVkey\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"sp1Verifier\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_clientState\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"_consensusState\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"roleManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"AddressEmptyCode\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotHandleMisbehavior\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"expected\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"actual\",\"type\":\"string\"}],\"name\":\"ChainIdMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"expected\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"actual\",\"type\":\"bytes\"}],\"name\":\"ClientStateMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConsensusStateHashMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConsensusStateNotFound\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConsensusStateRootMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyValue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FeatureNotSupported\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FrozenClientState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMembershipProof\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"value\",\"type\":\"bytes\"}],\"name\":\"KeyValuePairNotInCache\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"min\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"max\",\"type\":\"uint256\"}],\"name\":\"LengthIsOutOfRange\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"}],\"name\":\"MembershipProofKeyNotFound\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"expected\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"actual\",\"type\":\"bytes\"}],\"name\":\"MembershipProofValueMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"expectedRevisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"expectedRevisionHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"actualRevisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"actualRevisionHeight\",\"type\":\"uint64\"}],\"name\":\"ProofHeightMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"now\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofTimestamp\",\"type\":\"uint256\"}],\"name\":\"ProofIsInTheFuture\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"now\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofTimestamp\",\"type\":\"uint256\"}],\"name\":\"ProofIsTooOld\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expectedNumerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expectedDenominator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualNumerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualDenominator\",\"type\":\"uint256\"}],\"name\":\"TrustThresholdMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"TrustingPeriodMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"trustingPeriod\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unbondingPeriod\",\"type\":\"uint256\"}],\"name\":\"TrustingPeriodTooLong\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"UnbondingPeriodMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"proofType\",\"type\":\"uint8\"}],\"name\":\"UnknownMembershipProofType\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"algorithm\",\"type\":\"uint8\"}],\"name\":\"UnknownZkAlgorithm\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"VerificationKeyMismatch\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ALLOWED_SP1_CLOCK_DRIFT\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MEMBERSHIP_PROGRAM_VKEY\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MISBEHAVIOUR_PROGRAM_VKEY\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PROOF_SUBMITTER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"UPDATE_CLIENT_PROGRAM_VKEY\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"VERIFIER\",\"outputs\":[{\"internalType\":\"contract ISP1Verifier\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"clientState\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"chainId\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"numerator\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"denominator\",\"type\":\"uint8\"}],\"internalType\":\"struct IICS07TendermintMsgs.TrustThreshold\",\"name\":\"trustLevel\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"revisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"internalType\":\"struct IICS02ClientMsgs.Height\",\"name\":\"latestHeight\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"trustingPeriod\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"unbondingPeriod\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isFrozen\",\"type\":\"bool\"},{\"internalType\":\"enum IICS07TendermintMsgs.SupportedZkAlgorithm\",\"name\":\"zkAlgorithm\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getClientState\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"name\":\"getConsensusStateHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"misbehaviourMsg\",\"type\":\"bytes\"}],\"name\":\"misbehaviour\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"data\",\"type\":\"bytes[]\"}],\"name\":\"multicall\",\"outputs\":[{\"internalType\":\"bytes[]\",\"name\":\"results\",\"type\":\"bytes[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callerConfirmation\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"updateMsg\",\"type\":\"bytes\"}],\"name\":\"updateClient\",\"outputs\":[{\"internalType\":\"enum ILightClientMsgs.UpdateResult\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"revisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"internalType\":\"struct IICS02ClientMsgs.Height\",\"name\":\"proofHeight\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"value\",\"type\":\"bytes\"}],\"internalType\":\"struct ILightClientMsgs.MsgVerifyMembership\",\"name\":\"msg_\",\"type\":\"tuple\"}],\"name\":\"verifyMembership\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"revisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"internalType\":\"struct IICS02ClientMsgs.Height\",\"name\":\"proofHeight\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ILightClientMsgs.MsgVerifyNonMembership\",\"name\":\"msg_\",\"type\":\"tuple\"}],\"name\":\"verifyNonMembership\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"srdtrk\",\"errors\":{\"AccessControlBadConfirmation()\":[{\"details\":\"The caller of a function is not the expected one. NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\"}],\"AccessControlUnauthorizedAccount(address,bytes32)\":[{\"details\":\"The `account` is missing a role.\"}],\"AddressEmptyCode(address)\":[{\"details\":\"There's no code at `target` (it is not a contract).\"}],\"CannotHandleMisbehavior()\":[{\"details\":\"Misbehavior cannot be handled in membership handler, so it is returned as an error.\"}],\"ChainIdMismatch(string,string)\":[{\"params\":{\"actual\":\"The actual chain ID.\",\"expected\":\"The expected chain ID.\"}}],\"ClientStateMismatch(bytes,bytes)\":[{\"params\":{\"actual\":\"The actual client state.\",\"expected\":\"The expected client state.\"}}],\"ConsensusStateHashMismatch(bytes32,bytes32)\":[{\"params\":{\"actual\":\"The actual consensus state hash.\",\"expected\":\"The expected consensus state hash.\"}}],\"ConsensusStateRootMismatch(bytes32,bytes32)\":[{\"params\":{\"actual\":\"The actual consensus state root.\",\"expected\":\"The expected consensus state root.\"}}],\"FailedCall()\":[{\"details\":\"A call to an address target failed. The target may have reverted.\"}],\"KeyValuePairNotInCache(bytes[],bytes)\":[{\"params\":{\"path\":\"The path of the key-value pair.\",\"value\":\"The value of the key-value pair.\"}}],\"LengthIsOutOfRange(uint256,uint256,uint256)\":[{\"params\":{\"length\":\"The length of the value.\",\"max\":\"The maximum length of the value.\",\"min\":\"The minimum length of the value.\"}}],\"MembershipProofKeyNotFound(bytes[])\":[{\"params\":{\"path\":\"The path of the key-value pair.\"}}],\"MembershipProofValueMismatch(bytes,bytes)\":[{\"params\":{\"actual\":\"The actual value.\",\"expected\":\"The expected value.\"}}],\"ProofHeightMismatch(uint64,uint64,uint64,uint64)\":[{\"params\":{\"actualRevisionHeight\":\"The actual revision height.\",\"actualRevisionNumber\":\"The actual revision number.\",\"expectedRevisionHeight\":\"The expected revision height.\",\"expectedRevisionNumber\":\"The expected revision number.\"}}],\"ProofIsInTheFuture(uint256,uint256)\":[{\"params\":{\"now\":\"The current timestamp in seconds.\",\"proofTimestamp\":\"The timestamp in the proof in seconds.\"}}],\"ProofIsTooOld(uint256,uint256)\":[{\"params\":{\"now\":\"The current timestamp in seconds.\",\"proofTimestamp\":\"The timestamp in the proof in seconds.\"}}],\"TrustThresholdMismatch(uint256,uint256,uint256,uint256)\":[{\"params\":{\"actualDenominator\":\"The actual denominator of the trust threshold.\",\"actualNumerator\":\"The actual numerator of the trust threshold.\",\"expectedDenominator\":\"The expected denominator of the trust threshold.\",\"expectedNumerator\":\"The expected numerator of the trust threshold.\"}}],\"TrustingPeriodMismatch(uint256,uint256)\":[{\"params\":{\"actual\":\"The actual trusting period in seconds.\",\"expected\":\"The expected trusting period in seconds.\"}}],\"TrustingPeriodTooLong(uint256,uint256)\":[{\"params\":{\"trustingPeriod\":\"The trusting period in seconds.\",\"unbondingPeriod\":\"The unbonding period in seconds.\"}}],\"UnbondingPeriodMismatch(uint256,uint256)\":[{\"params\":{\"actual\":\"The actual unbonding period in seconds.\",\"expected\":\"The expected unbonding period in seconds.\"}}],\"UnknownMembershipProofType(uint8)\":[{\"params\":{\"proofType\":\"The unknown membership proof type.\"}}],\"UnknownZkAlgorithm(uint8)\":[{\"params\":{\"algorithm\":\"The unknown zk algorithm.\"}}],\"VerificationKeyMismatch(bytes32,bytes32)\":[{\"params\":{\"actual\":\"The actual verification key.\",\"expected\":\"The expected verification key.\"}}]},\"events\":{\"RoleAdminChanged(bytes32,bytes32,bytes32)\":{\"details\":\"Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite {RoleAdminChanged} not being emitted to signal this.\"},\"RoleGranted(bytes32,address,address)\":{\"details\":\"Emitted when `account` is granted `role`. `sender` is the account that originated the contract call. This account bears the admin role (for the granted role). Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.\"},\"RoleRevoked(bytes32,address,address)\":{\"details\":\"Emitted when `account` is revoked `role`. `sender` is the account that originated the contract call: - if using `revokeRole`, it is the admin role bearer - if using `renounceRole`, it is the role bearer (i.e. `account`)\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"params\":{\"_clientState\":\"The encoded initial client state.\",\"_consensusState\":\"The encoded initial consensus state.\",\"membershipProgramVkey\":\"The verification key for the verify (non)membership program.\",\"misbehaviourProgramVkey\":\"The verification key for the misbehaviour program.\",\"roleManager\":\"Manages the proof submitters and can submit proofs. Should be the ICS26Router if used in IBC.\",\"sp1Verifier\":\"The address of the SP1 verifier contract.\",\"updateClientAndMembershipProgramVkey\":\"The verification key for the update client and membership program.\",\"updateClientProgramVkey\":\"The verification key for the update client program.\"}},\"getClientState()\":{\"returns\":{\"_0\":\"The client state.\"}},\"getConsensusStateHash(uint64)\":{\"params\":{\"revisionHeight\":\"The revision height.\"},\"returns\":{\"_0\":\"The consensus state at the given revision height.\"}},\"getRoleAdmin(bytes32)\":{\"details\":\"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}.\"},\"grantRole(bytes32,address)\":{\"details\":\"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event.\"},\"hasRole(bytes32,address)\":{\"details\":\"Returns `true` if `account` has been granted `role`.\"},\"misbehaviour(bytes)\":{\"details\":\"The misbehavior is verfied in the sp1 program. Here we only check the public values which contain the trusted headers.\",\"params\":{\"misbehaviourMsg\":\"The misbehaviour message\"}},\"multicall(bytes[])\":{\"custom:oz-upgrades-unsafe-allow-reachable\":\"delegatecall\",\"details\":\"Receives and executes a batch of function calls on this contract.\"},\"renounceRole(bytes32,address)\":{\"details\":\"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event.\"},\"revokeRole(bytes32,address)\":{\"details\":\"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event.\"},\"supportsInterface(bytes4)\":{\"details\":\"Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] to learn more about how these ids are created. This function call must use less than 30 000 gas.\"},\"updateClient(bytes)\":{\"details\":\"This function verifies the public values and forwards the proof to the SP1 verifier.\",\"params\":{\"updateMsg\":\"The encoded update message e.g., an SP1 proof.\"},\"returns\":{\"_0\":\"The result of the update operation\"}},\"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))\":{\"details\":\"Notice that this message is not view, as it may update the client state for caching purposes.\",\"params\":{\"msg_\":\"The membership message\"},\"returns\":{\"_0\":\"The unix timestamp of the verification height in the counterparty chain in seconds.\"}},\"verifyNonMembership((bytes,(uint64,uint64),bytes[]))\":{\"details\":\"Notice that this message is not view, as it may update the client state for caching purposes.\",\"params\":{\"msg_\":\"The membership message\"},\"returns\":{\"_0\":\"The unix timestamp of the verification height in the counterparty chain in seconds.\"}}},\"stateVariables\":{\"ALLOWED_SP1_CLOCK_DRIFT\":{\"return\":\"The allowed prover clock drift in seconds.\",\"returns\":{\"_0\":\"The allowed prover clock drift in seconds.\"}},\"MEMBERSHIP_PROGRAM_VKEY\":{\"return\":\"The verification key for the membership program.\",\"returns\":{\"_0\":\"The verification key for the membership program.\"}},\"MISBEHAVIOUR_PROGRAM_VKEY\":{\"return\":\"The verification key for the misbehaviour program.\",\"returns\":{\"_0\":\"The verification key for the misbehaviour program.\"}},\"PROOF_SUBMITTER_ROLE\":{\"details\":\"The proof submitter role is used to whitelist addresses that can submit proofsIf `address(0)` has this role, then anyone can submit proofsIf this client is hooked up to ICS26Router, the router must be given this role\",\"return\":\"The role identifier\",\"returns\":{\"_0\":\"The role identifier\"}},\"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY\":{\"return\":\"The verification key for the update client and membership program.\",\"returns\":{\"_0\":\"The verification key for the update client and membership program.\"}},\"UPDATE_CLIENT_PROGRAM_VKEY\":{\"return\":\"The verification key for the update client program.\",\"returns\":{\"_0\":\"The verification key for the update client program.\"}},\"VERIFIER\":{\"return\":\"The SP1 verifier contract.\",\"returns\":{\"_0\":\"The SP1 verifier contract.\"}},\"_consensusStateHashes\":{\"details\":\"Revision number need not be keyed as it is not allowed to change.\"}},\"title\":\"SP1 ICS07 Tendermint Light Client\",\"version\":1},\"userdoc\":{\"errors\":{\"CannotHandleMisbehavior()\":[{\"notice\":\"The error that is returned when the update client and membership program contains misbehavior.\"}],\"ChainIdMismatch(string,string)\":[{\"notice\":\"The error that is returned when the chain ID does not match the expected value.\"}],\"ClientStateMismatch(bytes,bytes)\":[{\"notice\":\"The error that is returned when the client state does not match the expected value.\"}],\"ConsensusStateHashMismatch(bytes32,bytes32)\":[{\"notice\":\"The error that is returned when the consensus state hash does not match the expected value.\"}],\"ConsensusStateNotFound()\":[{\"notice\":\"The error that is returned when the consensus state is not found.\"}],\"ConsensusStateRootMismatch(bytes32,bytes32)\":[{\"notice\":\"The error that is returned when the consensus state root does not match the expected value.\"}],\"EmptyValue()\":[{\"notice\":\"Returned when the membership value is empty.\"}],\"FeatureNotSupported()\":[{\"notice\":\"Returned when the feature is not supported.\"}],\"FrozenClientState()\":[{\"notice\":\"The error that is returned when the client state is frozen.\"}],\"InvalidMembershipProof()\":[{\"notice\":\"Returned when the membership proof is invalid.\"}],\"KeyValuePairNotInCache(bytes[],bytes)\":[{\"notice\":\"Returned when a key-value pair is not in the cache.\"}],\"LengthIsOutOfRange(uint256,uint256,uint256)\":[{\"notice\":\"The error that is returned when the length of a value is out of range.\"}],\"MembershipProofKeyNotFound(bytes[])\":[{\"notice\":\"The error that is returned when the key-value pair's path is not contained in the proof.\"}],\"MembershipProofValueMismatch(bytes,bytes)\":[{\"notice\":\"The error that is returned when the key-value pair's value does not match the expected value.\"}],\"ProofHeightMismatch(uint64,uint64,uint64,uint64)\":[{\"notice\":\"The error that is returned when the proof height does not match the expected value.\"}],\"ProofIsInTheFuture(uint256,uint256)\":[{\"notice\":\"The error that is returned when a proof is in the future.\"}],\"ProofIsTooOld(uint256,uint256)\":[{\"notice\":\"The error that is returned when a proof is too old.\"}],\"TrustThresholdMismatch(uint256,uint256,uint256,uint256)\":[{\"notice\":\"The error that is returned when the trust threshold does not match the expected value.\"}],\"TrustingPeriodMismatch(uint256,uint256)\":[{\"notice\":\"The error that is returned when the trusting period does not match the expected value.\"}],\"TrustingPeriodTooLong(uint256,uint256)\":[{\"notice\":\"The error that is returned when the trusting period is longer than the unbonding period.\"}],\"UnbondingPeriodMismatch(uint256,uint256)\":[{\"notice\":\"The error that is returned when the unbonding period does not match the expected value.\"}],\"UnknownMembershipProofType(uint8)\":[{\"notice\":\"The error that is returned when the membership proof type is unknown.\"}],\"UnknownZkAlgorithm(uint8)\":[{\"notice\":\"The error that is returned when the zk algorithm is unknown.\"}],\"VerificationKeyMismatch(bytes32,bytes32)\":[{\"notice\":\"The error that is returned when the verification key does not match the expected value.\"}]},\"kind\":\"user\",\"methods\":{\"ALLOWED_SP1_CLOCK_DRIFT()\":{\"notice\":\"Constant allowed prover clock drift in seconds.\"},\"MEMBERSHIP_PROGRAM_VKEY()\":{\"notice\":\"Immutable membership program verification key.\"},\"MISBEHAVIOUR_PROGRAM_VKEY()\":{\"notice\":\"Immutable misbehaviour program verification key.\"},\"PROOF_SUBMITTER_ROLE()\":{\"notice\":\"The role identifier for the proof submitter role\"},\"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY()\":{\"notice\":\"Immutable update client and membership program verification key.\"},\"UPDATE_CLIENT_PROGRAM_VKEY()\":{\"notice\":\"Immutable update client program verification key.\"},\"VERIFIER()\":{\"notice\":\"Immutable SP1 verifier contract address.\"},\"clientState()\":{\"notice\":\"The ICS07Tendermint client state\"},\"constructor\":{\"notice\":\"The constructor sets the program verification key and the initial client and consensus states.\"},\"getClientState()\":{\"notice\":\"Returns the client state.\"},\"getConsensusStateHash(uint64)\":{\"notice\":\"Returns the consensus state keccak256 hash at the given revision height.\"},\"misbehaviour(bytes)\":{\"notice\":\"Misbehaviour handling, moves the light client to the frozen state if misbehaviour is detected\"},\"updateClient(bytes)\":{\"notice\":\"Updating the client and consensus state\"},\"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))\":{\"notice\":\"Querying the membership of a key-value pair\"},\"verifyNonMembership((bytes,(uint64,uint64),bytes[]))\":{\"notice\":\"Querying the non-membership of a key\"}},\"notice\":\"This contract implements an ICS07 IBC tendermint light client using SP1.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/light-clients/sp1-ics07/SP1ICS07Tendermint.sol\":\"SP1ICS07Tendermint\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[\":@openzeppelin-contracts/=node_modules/@openzeppelin/contracts/\",\":@openzeppelin-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/\",\":@openzeppelin/=node_modules/@openzeppelin/\",\":@sp1-contracts/=node_modules/sp1-contracts/contracts/src/\",\":@uniswap/permit2/=node_modules/@uniswap/permit2/\",\":forge-std/=node_modules/forge-std/src/\",\":sp1-contracts/=node_modules/sp1-contracts/\"],\"viaIR\":true},\"sources\":{\"contracts/interfaces/ILightClient.sol\":{\"keccak256\":\"0x1caae9e4f741a86c0056d5f7713a35f3bd96db7a9fd52fc9f1cf79aad76a442f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4a536db3b926560f69547c17e4f451a2631ddea0f42bd224c7fd8382d74429f6\",\"dweb:/ipfs/QmcQ33kxcmmowXgMbQGTBgiKBjF7v8UiP4y73bB9hhDizC\"]},\"contracts/light-clients/sp1-ics07/SP1ICS07Tendermint.sol\":{\"keccak256\":\"0x2e03f0f1e058e7184ea70dc2a423a3d32a609620b6a1e8b764ec9c287b0b6187\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9e3c0b2f3b3af9cfc8a3ab085e0decffcd40230a27530142293f0b79a4aefe51\",\"dweb:/ipfs/Qmf3qEAMWdiGAMEXSae6mzZ7Sncsbr4QzDC4JzMeiNwgei\"]},\"contracts/light-clients/sp1-ics07/errors/ISP1ICS07TendermintErrors.sol\":{\"keccak256\":\"0xe1a0526f9913308f761af480ef7d87091d6038659b9058f89c50083d49768db1\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a31d6d4a059182b3dfd3e99595d82a134963c448c9152ef35b42c62ccca87635\",\"dweb:/ipfs/QmfQ2Cfa4kxWem3f88o9UnFQgL8oRU6vKkDFWSGmLsVLqN\"]},\"contracts/light-clients/sp1-ics07/interfaces/ISP1ICS07Tendermint.sol\":{\"keccak256\":\"0x0f00aea96b194d1a9db14f708f0de1fe7723fedc7b72bb8ea182e768ffc0a731\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://3111a76ebabf4b57917e93669892df895a54a93a4963e384d8c13a1c47877b44\",\"dweb:/ipfs/QmPCZ7Z4dJpowkzMhizUAW1j8QdJLD3DDjMbYq8G5VeXc1\"]},\"contracts/light-clients/sp1-ics07/msgs/IICS07TendermintMsgs.sol\":{\"keccak256\":\"0xae0ae6ff2742192ebaaee297c7d7473a1e1b4dc73393d45a944b0b7a8a647703\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://43b2f6492a1f727416bbcf55876d9c08ac1f66f4724efc75b935c5022555be20\",\"dweb:/ipfs/QmfTaPFNSFuUEsEBgJjnF2Jr2knayS7YwatfjP7dgdcAyH\"]},\"contracts/light-clients/sp1-ics07/msgs/IMembershipMsgs.sol\":{\"keccak256\":\"0x9dff6833fddbc11caf27387c9f8cba5dca9d1e430c9643d4e4a2e8eb09924d96\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://efa6cfc654510dfcef6f4286e3e66446082bcd5671662ae6e1c6fd0f893a5a4a\",\"dweb:/ipfs/QmRWAg1kHvmRtXgDm4ajVC7tEWQnzTWmHeHMYQJfyFuDGq\"]},\"contracts/light-clients/sp1-ics07/msgs/IMisbehaviourMsgs.sol\":{\"keccak256\":\"0x165b37d890b06eccd47558bf1efd6495e18541627758736a1f5b9c33d0333832\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://15ab15cd5439147abed697bc9644b0175754845608bf3d57c81c2c864d31ce46\",\"dweb:/ipfs/QmYcV88YsM1sZCmhP8LKUFHq3hJ6bsvd66W5axBnsBBoUM\"]},\"contracts/light-clients/sp1-ics07/msgs/ISP1Msgs.sol\":{\"keccak256\":\"0x8ede29b02182df4be9c075b3b48083277e5dd93bb3e7ce1eecfea9728db49aed\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://48edb4bbcd440454fffceb4f85b83c08c3973a212239d7da07c079634fe413dd\",\"dweb:/ipfs/Qmf7ma1nXWghfZKrJJvLpPuY5aN2xZZjHY7kWP3KVFCoDJ\"]},\"contracts/light-clients/sp1-ics07/msgs/IUcAndMembershipMsgs.sol\":{\"keccak256\":\"0xef3619c79de37cabe95d9d8af9d145fc2ea2dd55137407fb4b0082b0491170d0\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://93646d813cdacf5e38e609e54baa67c60d749f0f111d00cd18b003c3cb328204\",\"dweb:/ipfs/QmVcuwPujbiZ644a4jRw2V1LZxhhGsm2HKMXweVqndwUnZ\"]},\"contracts/light-clients/sp1-ics07/msgs/IUpdateClientMsgs.sol\":{\"keccak256\":\"0x370cdfca1c65f1898c7da80507e66972a6fe51a5d5c4e7aeabdd7060ce875aed\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://ff34288a142b6f40d6843818b9ec0a542db016bc0d4a7be582eb2e255655c43e\",\"dweb:/ipfs/QmXwbJHDbqZdw3JTT48Le1ZWGqmKAG7omf2ePvDfBHomzj\"]},\"contracts/light-clients/sp1-ics07/utils/Paths.sol\":{\"keccak256\":\"0x0e4eec6cd6cd0ef684383946419a2bf53c8f800033acfc298db81688e5aa178b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://824b58c13ff96b6ee38b5d25435fcc1305c45735f52df6347dba3dacdc144ffa\",\"dweb:/ipfs/QmT7qomKM46ChfVVwfBQHwjpoHW9vrG9nXkGXsPeZRLJ7w\"]},\"contracts/msgs/IICS02ClientMsgs.sol\":{\"keccak256\":\"0xb5fdb25319d5c32b3f527982d45ec1e9bec5215515581aee034d853006ea01a0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6b8939bf552c62c952a491008bcd9b59ab7dda20a9482bf93438e05793881542\",\"dweb:/ipfs/QmfWmzNCp8bd4pP26Lfw5HHp4dSZnCDqZDjVN7SD8P7eDh\"]},\"contracts/msgs/ILightClientMsgs.sol\":{\"keccak256\":\"0xa0565e7748b8b3284d72e1aa6ef4cc8264fcaa5f4e0761d22d64686953db6378\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c4476010d7090bfaa74127526f2051c6bc82ff42a09b936e848880a1f15a6065\",\"dweb:/ipfs/QmY87JgRsFQXSZdGXNjNHEJCkdTHzFXH4vZEJ3q7MnHuP8\"]},\"node_modules/@openzeppelin/contracts/access/AccessControl.sol\":{\"keccak256\":\"0x1a6b4f6b7798ab80929d491b89d5427a9b3338c0fd1acd0ba325f69c6f1646af\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7bb7f346c12a14dc622bc105ce3c47202fbc89f4b153a28a63bb68193297330c\",\"dweb:/ipfs/QmagwF8P3bUBXwdo159ueEnY9dLSvEWwK24kk2op58egwG\"]},\"node_modules/@openzeppelin/contracts/access/IAccessControl.sol\":{\"keccak256\":\"0xbff9f59c84e5337689161ce7641c0ef8e872d6a7536fbc1f5133f128887aba3c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b308f882e796f7b79c9502deacb0a62983035c6f6f4e962b319ba6a1f4a77d3d\",\"dweb:/ipfs/QmaWCW7ahEQqFjwhSUhV7Ae7WhfNvzSpE7DQ58hvEooqPL\"]},\"node_modules/@openzeppelin/contracts/utils/Address.sol\":{\"keccak256\":\"0x6d0ae6e206645341fd122d278c2cb643dea260c190531f2f3f6a0426e77b00c0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://032d1201d839435be2c85b72e33206b3ea980c569d6ebf7fa57d811ab580a82f\",\"dweb:/ipfs/QmeqQjAtMvdZT2tG7zm39itcRJkuwu8AEReK6WRnLJ18DD\"]},\"node_modules/@openzeppelin/contracts/utils/Context.sol\":{\"keccak256\":\"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12\",\"dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF\"]},\"node_modules/@openzeppelin/contracts/utils/Errors.sol\":{\"keccak256\":\"0x6afa713bfd42cf0f7656efa91201007ac465e42049d7de1d50753a373648c123\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://ba1d02f4847670a1b83dec9f7d37f0b0418d6043447b69f3a29a5f9efc547fcf\",\"dweb:/ipfs/QmQ7iH2keLNUKgq2xSWcRmuBE5eZ3F5whYAkAGzCNNoEWB\"]},\"node_modules/@openzeppelin/contracts/utils/Multicall.sol\":{\"keccak256\":\"0xa4c645387f16eae227cd378108e8e0a8acbcf2b600e393629bbd8422241c495f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5e272440acfbec7f51afdb60a42597837b267de83ba7bd08d7e4dbc40171567c\",\"dweb:/ipfs/QmPYzRwXpjUJEGRBWaSnzHz8gym1AKJ8yfoCgbo7gcwmEf\"]},\"node_modules/@openzeppelin/contracts/utils/TransientSlot.sol\":{\"keccak256\":\"0xac673fa1e374d9e6107504af363333e3e5f6344d2e83faf57d9bfd41d77cc946\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5982478dbbb218e9dd5a6e83f5c0e8d1654ddf20178484b43ef21dd2246809de\",\"dweb:/ipfs/QmaB1hS68n2kG8vTbt7EPEzmrGhkUbfiFyykGGLsAr9X22\"]},\"node_modules/@openzeppelin/contracts/utils/introspection/ERC165.sol\":{\"keccak256\":\"0x2d9dc2fe26180f74c11c13663647d38e259e45f95eb88f57b61d2160b0109d3e\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://81233d1f98060113d9922180bb0f14f8335856fe9f339134b09335e9f678c377\",\"dweb:/ipfs/QmWh6R35SarhAn4z2wH8SU456jJSYL2FgucfTFgbHJJN4E\"]},\"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617\",\"dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u\"]},\"node_modules/sp1-contracts/contracts/src/ISP1Verifier.sol\":{\"keccak256\":\"0x9e3ba64860bea920772dcf16be7946de2a2900d80bd51e9c0771184138f4f4d3\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0ec7230ca1fdd74edc6ab597d80bb345282aed3f0db4788ed96b4cc373ff46a3\",\"dweb:/ipfs/QmXPuSS5gzxMhFKWr1gsxBVu6WHh53ZZEvWkGgzrkM6Y7Q\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"bytes32","name":"updateClientProgramVkey","type":"bytes32"},{"internalType":"bytes32","name":"membershipProgramVkey","type":"bytes32"},{"internalType":"bytes32","name":"updateClientAndMembershipProgramVkey","type":"bytes32"},{"internalType":"bytes32","name":"misbehaviourProgramVkey","type":"bytes32"},{"internalType":"address","name":"sp1Verifier","type":"address"},{"internalType":"bytes","name":"_clientState","type":"bytes"},{"internalType":"bytes32","name":"_consensusState","type":"bytes32"},{"internalType":"address","name":"roleManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"type":"error","name":"AccessControlBadConfirmation"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"type":"error","name":"AccessControlUnauthorizedAccount"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"type":"error","name":"AddressEmptyCode"},{"inputs":[],"type":"error","name":"CannotHandleMisbehavior"},{"inputs":[{"internalType":"string","name":"expected","type":"string"},{"internalType":"string","name":"actual","type":"string"}],"type":"error","name":"ChainIdMismatch"},{"inputs":[{"internalType":"bytes","name":"expected","type":"bytes"},{"internalType":"bytes","name":"actual","type":"bytes"}],"type":"error","name":"ClientStateMismatch"},{"inputs":[{"internalType":"bytes32","name":"expected","type":"bytes32"},{"internalType":"bytes32","name":"actual","type":"bytes32"}],"type":"error","name":"ConsensusStateHashMismatch"},{"inputs":[],"type":"error","name":"ConsensusStateNotFound"},{"inputs":[{"internalType":"bytes32","name":"expected","type":"bytes32"},{"internalType":"bytes32","name":"actual","type":"bytes32"}],"type":"error","name":"ConsensusStateRootMismatch"},{"inputs":[],"type":"error","name":"EmptyValue"},{"inputs":[],"type":"error","name":"FailedCall"},{"inputs":[],"type":"error","name":"FeatureNotSupported"},{"inputs":[],"type":"error","name":"FrozenClientState"},{"inputs":[],"type":"error","name":"InvalidMembershipProof"},{"inputs":[{"internalType":"bytes[]","name":"path","type":"bytes[]"},{"internalType":"bytes","name":"value","type":"bytes"}],"type":"error","name":"KeyValuePairNotInCache"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"},{"internalType":"uint256","name":"min","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"type":"error","name":"LengthIsOutOfRange"},{"inputs":[{"internalType":"bytes[]","name":"path","type":"bytes[]"}],"type":"error","name":"MembershipProofKeyNotFound"},{"inputs":[{"internalType":"bytes","name":"expected","type":"bytes"},{"internalType":"bytes","name":"actual","type":"bytes"}],"type":"error","name":"MembershipProofValueMismatch"},{"inputs":[{"internalType":"uint64","name":"expectedRevisionNumber","type":"uint64"},{"internalType":"uint64","name":"expectedRevisionHeight","type":"uint64"},{"internalType":"uint64","name":"actualRevisionNumber","type":"uint64"},{"internalType":"uint64","name":"actualRevisionHeight","type":"uint64"}],"type":"error","name":"ProofHeightMismatch"},{"inputs":[{"internalType":"uint256","name":"now","type":"uint256"},{"internalType":"uint256","name":"proofTimestamp","type":"uint256"}],"type":"error","name":"ProofIsInTheFuture"},{"inputs":[{"internalType":"uint256","name":"now","type":"uint256"},{"internalType":"uint256","name":"proofTimestamp","type":"uint256"}],"type":"error","name":"ProofIsTooOld"},{"inputs":[{"internalType":"uint256","name":"expectedNumerator","type":"uint256"},{"internalType":"uint256","name":"expectedDenominator","type":"uint256"},{"internalType":"uint256","name":"actualNumerator","type":"uint256"},{"internalType":"uint256","name":"actualDenominator","type":"uint256"}],"type":"error","name":"TrustThresholdMismatch"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"type":"error","name":"TrustingPeriodMismatch"},{"inputs":[{"internalType":"uint256","name":"trustingPeriod","type":"uint256"},{"internalType":"uint256","name":"unbondingPeriod","type":"uint256"}],"type":"error","name":"TrustingPeriodTooLong"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"type":"error","name":"UnbondingPeriodMismatch"},{"inputs":[{"internalType":"uint8","name":"proofType","type":"uint8"}],"type":"error","name":"UnknownMembershipProofType"},{"inputs":[{"internalType":"uint8","name":"algorithm","type":"uint8"}],"type":"error","name":"UnknownZkAlgorithm"},{"inputs":[{"internalType":"bytes32","name":"expected","type":"bytes32"},{"internalType":"bytes32","name":"actual","type":"bytes32"}],"type":"error","name":"VerificationKeyMismatch"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"previousAdminRole","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"newAdminRole","type":"bytes32","indexed":true}],"type":"event","name":"RoleAdminChanged","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleGranted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleRevoked","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"ALLOWED_SP1_CLOCK_DRIFT","outputs":[{"internalType":"uint16","name":"","type":"uint16"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"MEMBERSHIP_PROGRAM_VKEY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"MISBEHAVIOUR_PROGRAM_VKEY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"PROOF_SUBMITTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"UPDATE_CLIENT_PROGRAM_VKEY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"VERIFIER","outputs":[{"internalType":"contract ISP1Verifier","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"clientState","outputs":[{"internalType":"string","name":"chainId","type":"string"},{"internalType":"struct IICS07TendermintMsgs.TrustThreshold","name":"trustLevel","type":"tuple","components":[{"internalType":"uint8","name":"numerator","type":"uint8"},{"internalType":"uint8","name":"denominator","type":"uint8"}]},{"internalType":"struct IICS02ClientMsgs.Height","name":"latestHeight","type":"tuple","components":[{"internalType":"uint64","name":"revisionNumber","type":"uint64"},{"internalType":"uint64","name":"revisionHeight","type":"uint64"}]},{"internalType":"uint32","name":"trustingPeriod","type":"uint32"},{"internalType":"uint32","name":"unbondingPeriod","type":"uint32"},{"internalType":"bool","name":"isFrozen","type":"bool"},{"internalType":"enum IICS07TendermintMsgs.SupportedZkAlgorithm","name":"zkAlgorithm","type":"uint8"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getClientState","outputs":[{"internalType":"bytes","name":"","type":"bytes"}]},{"inputs":[{"internalType":"uint64","name":"revisionHeight","type":"uint64"}],"stateMutability":"view","type":"function","name":"getConsensusStateHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"grantRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"view","type":"function","name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes","name":"misbehaviourMsg","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"misbehaviour"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function","name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"renounceRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"revokeRole"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"stateMutability":"view","type":"function","name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes","name":"updateMsg","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"updateClient","outputs":[{"internalType":"enum ILightClientMsgs.UpdateResult","name":"","type":"uint8"}]},{"inputs":[{"internalType":"struct ILightClientMsgs.MsgVerifyMembership","name":"msg_","type":"tuple","components":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"struct IICS02ClientMsgs.Height","name":"proofHeight","type":"tuple","components":[{"internalType":"uint64","name":"revisionNumber","type":"uint64"},{"internalType":"uint64","name":"revisionHeight","type":"uint64"}]},{"internalType":"bytes[]","name":"path","type":"bytes[]"},{"internalType":"bytes","name":"value","type":"bytes"}]}],"stateMutability":"nonpayable","type":"function","name":"verifyMembership","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"struct ILightClientMsgs.MsgVerifyNonMembership","name":"msg_","type":"tuple","components":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"struct IICS02ClientMsgs.Height","name":"proofHeight","type":"tuple","components":[{"internalType":"uint64","name":"revisionNumber","type":"uint64"},{"internalType":"uint64","name":"revisionHeight","type":"uint64"}]},{"internalType":"bytes[]","name":"path","type":"bytes[]"}]}],"stateMutability":"nonpayable","type":"function","name":"verifyNonMembership","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{"constructor":{"params":{"_clientState":"The encoded initial client state.","_consensusState":"The encoded initial consensus state.","membershipProgramVkey":"The verification key for the verify (non)membership program.","misbehaviourProgramVkey":"The verification key for the misbehaviour program.","roleManager":"Manages the proof submitters and can submit proofs. Should be the ICS26Router if used in IBC.","sp1Verifier":"The address of the SP1 verifier contract.","updateClientAndMembershipProgramVkey":"The verification key for the update client and membership program.","updateClientProgramVkey":"The verification key for the update client program."}},"getClientState()":{"returns":{"_0":"The client state."}},"getConsensusStateHash(uint64)":{"params":{"revisionHeight":"The revision height."},"returns":{"_0":"The consensus state at the given revision height."}},"getRoleAdmin(bytes32)":{"details":"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}."},"grantRole(bytes32,address)":{"details":"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event."},"hasRole(bytes32,address)":{"details":"Returns `true` if `account` has been granted `role`."},"misbehaviour(bytes)":{"details":"The misbehavior is verfied in the sp1 program. Here we only check the public values which contain the trusted headers.","params":{"misbehaviourMsg":"The misbehaviour message"}},"multicall(bytes[])":{"custom:oz-upgrades-unsafe-allow-reachable":"delegatecall","details":"Receives and executes a batch of function calls on this contract."},"renounceRole(bytes32,address)":{"details":"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event."},"revokeRole(bytes32,address)":{"details":"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event."},"supportsInterface(bytes4)":{"details":"Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] to learn more about how these ids are created. This function call must use less than 30 000 gas."},"updateClient(bytes)":{"details":"This function verifies the public values and forwards the proof to the SP1 verifier.","params":{"updateMsg":"The encoded update message e.g., an SP1 proof."},"returns":{"_0":"The result of the update operation"}},"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":{"details":"Notice that this message is not view, as it may update the client state for caching purposes.","params":{"msg_":"The membership message"},"returns":{"_0":"The unix timestamp of the verification height in the counterparty chain in seconds."}},"verifyNonMembership((bytes,(uint64,uint64),bytes[]))":{"details":"Notice that this message is not view, as it may update the client state for caching purposes.","params":{"msg_":"The membership message"},"returns":{"_0":"The unix timestamp of the verification height in the counterparty chain in seconds."}}},"version":1},"userdoc":{"kind":"user","methods":{"ALLOWED_SP1_CLOCK_DRIFT()":{"notice":"Constant allowed prover clock drift in seconds."},"MEMBERSHIP_PROGRAM_VKEY()":{"notice":"Immutable membership program verification key."},"MISBEHAVIOUR_PROGRAM_VKEY()":{"notice":"Immutable misbehaviour program verification key."},"PROOF_SUBMITTER_ROLE()":{"notice":"The role identifier for the proof submitter role"},"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY()":{"notice":"Immutable update client and membership program verification key."},"UPDATE_CLIENT_PROGRAM_VKEY()":{"notice":"Immutable update client program verification key."},"VERIFIER()":{"notice":"Immutable SP1 verifier contract address."},"clientState()":{"notice":"The ICS07Tendermint client state"},"constructor":{"notice":"The constructor sets the program verification key and the initial client and consensus states."},"getClientState()":{"notice":"Returns the client state."},"getConsensusStateHash(uint64)":{"notice":"Returns the consensus state keccak256 hash at the given revision height."},"misbehaviour(bytes)":{"notice":"Misbehaviour handling, moves the light client to the frozen state if misbehaviour is detected"},"updateClient(bytes)":{"notice":"Updating the client and consensus state"},"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":{"notice":"Querying the membership of a key-value pair"},"verifyNonMembership((bytes,(uint64,uint64),bytes[]))":{"notice":"Querying the non-membership of a key"}},"version":1}},"settings":{"remappings":["@openzeppelin-contracts/=node_modules/@openzeppelin/contracts/","@openzeppelin-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/","@openzeppelin/=node_modules/@openzeppelin/","@sp1-contracts/=node_modules/sp1-contracts/contracts/src/","@uniswap/permit2/=node_modules/@uniswap/permit2/","forge-std/=node_modules/forge-std/src/","sp1-contracts/=node_modules/sp1-contracts/"],"optimizer":{"enabled":true,"runs":10000},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"contracts/light-clients/sp1-ics07/SP1ICS07Tendermint.sol":"SP1ICS07Tendermint"},"evmVersion":"cancun","libraries":{},"viaIR":true},"sources":{"contracts/interfaces/ILightClient.sol":{"keccak256":"0x1caae9e4f741a86c0056d5f7713a35f3bd96db7a9fd52fc9f1cf79aad76a442f","urls":["bzz-raw://4a536db3b926560f69547c17e4f451a2631ddea0f42bd224c7fd8382d74429f6","dweb:/ipfs/QmcQ33kxcmmowXgMbQGTBgiKBjF7v8UiP4y73bB9hhDizC"],"license":"MIT"},"contracts/light-clients/sp1-ics07/SP1ICS07Tendermint.sol":{"keccak256":"0x2e03f0f1e058e7184ea70dc2a423a3d32a609620b6a1e8b764ec9c287b0b6187","urls":["bzz-raw://9e3c0b2f3b3af9cfc8a3ab085e0decffcd40230a27530142293f0b79a4aefe51","dweb:/ipfs/Qmf3qEAMWdiGAMEXSae6mzZ7Sncsbr4QzDC4JzMeiNwgei"],"license":"MIT"},"contracts/light-clients/sp1-ics07/errors/ISP1ICS07TendermintErrors.sol":{"keccak256":"0xe1a0526f9913308f761af480ef7d87091d6038659b9058f89c50083d49768db1","urls":["bzz-raw://a31d6d4a059182b3dfd3e99595d82a134963c448c9152ef35b42c62ccca87635","dweb:/ipfs/QmfQ2Cfa4kxWem3f88o9UnFQgL8oRU6vKkDFWSGmLsVLqN"],"license":"MIT"},"contracts/light-clients/sp1-ics07/interfaces/ISP1ICS07Tendermint.sol":{"keccak256":"0x0f00aea96b194d1a9db14f708f0de1fe7723fedc7b72bb8ea182e768ffc0a731","urls":["bzz-raw://3111a76ebabf4b57917e93669892df895a54a93a4963e384d8c13a1c47877b44","dweb:/ipfs/QmPCZ7Z4dJpowkzMhizUAW1j8QdJLD3DDjMbYq8G5VeXc1"],"license":"MIT"},"contracts/light-clients/sp1-ics07/msgs/IICS07TendermintMsgs.sol":{"keccak256":"0xae0ae6ff2742192ebaaee297c7d7473a1e1b4dc73393d45a944b0b7a8a647703","urls":["bzz-raw://43b2f6492a1f727416bbcf55876d9c08ac1f66f4724efc75b935c5022555be20","dweb:/ipfs/QmfTaPFNSFuUEsEBgJjnF2Jr2knayS7YwatfjP7dgdcAyH"],"license":"UNLICENSED"},"contracts/light-clients/sp1-ics07/msgs/IMembershipMsgs.sol":{"keccak256":"0x9dff6833fddbc11caf27387c9f8cba5dca9d1e430c9643d4e4a2e8eb09924d96","urls":["bzz-raw://efa6cfc654510dfcef6f4286e3e66446082bcd5671662ae6e1c6fd0f893a5a4a","dweb:/ipfs/QmRWAg1kHvmRtXgDm4ajVC7tEWQnzTWmHeHMYQJfyFuDGq"],"license":"UNLICENSED"},"contracts/light-clients/sp1-ics07/msgs/IMisbehaviourMsgs.sol":{"keccak256":"0x165b37d890b06eccd47558bf1efd6495e18541627758736a1f5b9c33d0333832","urls":["bzz-raw://15ab15cd5439147abed697bc9644b0175754845608bf3d57c81c2c864d31ce46","dweb:/ipfs/QmYcV88YsM1sZCmhP8LKUFHq3hJ6bsvd66W5axBnsBBoUM"],"license":"UNLICENSED"},"contracts/light-clients/sp1-ics07/msgs/ISP1Msgs.sol":{"keccak256":"0x8ede29b02182df4be9c075b3b48083277e5dd93bb3e7ce1eecfea9728db49aed","urls":["bzz-raw://48edb4bbcd440454fffceb4f85b83c08c3973a212239d7da07c079634fe413dd","dweb:/ipfs/Qmf7ma1nXWghfZKrJJvLpPuY5aN2xZZjHY7kWP3KVFCoDJ"],"license":"MIT"},"contracts/light-clients/sp1-ics07/msgs/IUcAndMembershipMsgs.sol":{"keccak256":"0xef3619c79de37cabe95d9d8af9d145fc2ea2dd55137407fb4b0082b0491170d0","urls":["bzz-raw://93646d813cdacf5e38e609e54baa67c60d749f0f111d00cd18b003c3cb328204","dweb:/ipfs/QmVcuwPujbiZ644a4jRw2V1LZxhhGsm2HKMXweVqndwUnZ"],"license":"UNLICENSED"},"contracts/light-clients/sp1-ics07/msgs/IUpdateClientMsgs.sol":{"keccak256":"0x370cdfca1c65f1898c7da80507e66972a6fe51a5d5c4e7aeabdd7060ce875aed","urls":["bzz-raw://ff34288a142b6f40d6843818b9ec0a542db016bc0d4a7be582eb2e255655c43e","dweb:/ipfs/QmXwbJHDbqZdw3JTT48Le1ZWGqmKAG7omf2ePvDfBHomzj"],"license":"UNLICENSED"},"contracts/light-clients/sp1-ics07/utils/Paths.sol":{"keccak256":"0x0e4eec6cd6cd0ef684383946419a2bf53c8f800033acfc298db81688e5aa178b","urls":["bzz-raw://824b58c13ff96b6ee38b5d25435fcc1305c45735f52df6347dba3dacdc144ffa","dweb:/ipfs/QmT7qomKM46ChfVVwfBQHwjpoHW9vrG9nXkGXsPeZRLJ7w"],"license":"MIT"},"contracts/msgs/IICS02ClientMsgs.sol":{"keccak256":"0xb5fdb25319d5c32b3f527982d45ec1e9bec5215515581aee034d853006ea01a0","urls":["bzz-raw://6b8939bf552c62c952a491008bcd9b59ab7dda20a9482bf93438e05793881542","dweb:/ipfs/QmfWmzNCp8bd4pP26Lfw5HHp4dSZnCDqZDjVN7SD8P7eDh"],"license":"MIT"},"contracts/msgs/ILightClientMsgs.sol":{"keccak256":"0xa0565e7748b8b3284d72e1aa6ef4cc8264fcaa5f4e0761d22d64686953db6378","urls":["bzz-raw://c4476010d7090bfaa74127526f2051c6bc82ff42a09b936e848880a1f15a6065","dweb:/ipfs/QmY87JgRsFQXSZdGXNjNHEJCkdTHzFXH4vZEJ3q7MnHuP8"],"license":"MIT"},"node_modules/@openzeppelin/contracts/access/AccessControl.sol":{"keccak256":"0x1a6b4f6b7798ab80929d491b89d5427a9b3338c0fd1acd0ba325f69c6f1646af","urls":["bzz-raw://7bb7f346c12a14dc622bc105ce3c47202fbc89f4b153a28a63bb68193297330c","dweb:/ipfs/QmagwF8P3bUBXwdo159ueEnY9dLSvEWwK24kk2op58egwG"],"license":"MIT"},"node_modules/@openzeppelin/contracts/access/IAccessControl.sol":{"keccak256":"0xbff9f59c84e5337689161ce7641c0ef8e872d6a7536fbc1f5133f128887aba3c","urls":["bzz-raw://b308f882e796f7b79c9502deacb0a62983035c6f6f4e962b319ba6a1f4a77d3d","dweb:/ipfs/QmaWCW7ahEQqFjwhSUhV7Ae7WhfNvzSpE7DQ58hvEooqPL"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/Address.sol":{"keccak256":"0x6d0ae6e206645341fd122d278c2cb643dea260c190531f2f3f6a0426e77b00c0","urls":["bzz-raw://032d1201d839435be2c85b72e33206b3ea980c569d6ebf7fa57d811ab580a82f","dweb:/ipfs/QmeqQjAtMvdZT2tG7zm39itcRJkuwu8AEReK6WRnLJ18DD"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/Context.sol":{"keccak256":"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2","urls":["bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12","dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/Errors.sol":{"keccak256":"0x6afa713bfd42cf0f7656efa91201007ac465e42049d7de1d50753a373648c123","urls":["bzz-raw://ba1d02f4847670a1b83dec9f7d37f0b0418d6043447b69f3a29a5f9efc547fcf","dweb:/ipfs/QmQ7iH2keLNUKgq2xSWcRmuBE5eZ3F5whYAkAGzCNNoEWB"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/Multicall.sol":{"keccak256":"0xa4c645387f16eae227cd378108e8e0a8acbcf2b600e393629bbd8422241c495f","urls":["bzz-raw://5e272440acfbec7f51afdb60a42597837b267de83ba7bd08d7e4dbc40171567c","dweb:/ipfs/QmPYzRwXpjUJEGRBWaSnzHz8gym1AKJ8yfoCgbo7gcwmEf"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/TransientSlot.sol":{"keccak256":"0xac673fa1e374d9e6107504af363333e3e5f6344d2e83faf57d9bfd41d77cc946","urls":["bzz-raw://5982478dbbb218e9dd5a6e83f5c0e8d1654ddf20178484b43ef21dd2246809de","dweb:/ipfs/QmaB1hS68n2kG8vTbt7EPEzmrGhkUbfiFyykGGLsAr9X22"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/introspection/ERC165.sol":{"keccak256":"0x2d9dc2fe26180f74c11c13663647d38e259e45f95eb88f57b61d2160b0109d3e","urls":["bzz-raw://81233d1f98060113d9922180bb0f14f8335856fe9f339134b09335e9f678c377","dweb:/ipfs/QmWh6R35SarhAn4z2wH8SU456jJSYL2FgucfTFgbHJJN4E"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol":{"keccak256":"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c","urls":["bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617","dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u"],"license":"MIT"},"node_modules/sp1-contracts/contracts/src/ISP1Verifier.sol":{"keccak256":"0x9e3ba64860bea920772dcf16be7946de2a2900d80bd51e9c0771184138f4f4d3","urls":["bzz-raw://0ec7230ca1fdd74edc6ab597d80bb345282aed3f0db4788ed96b4cc373ff46a3","dweb:/ipfs/QmXPuSS5gzxMhFKWr1gsxBVu6WHh53ZZEvWkGgzrkM6Y7Q"],"license":"MIT"}},"version":1},"id":38} \ No newline at end of file +{"abi":[{"type":"constructor","inputs":[{"name":"updateClientProgramVkey","type":"bytes32","internalType":"bytes32"},{"name":"membershipProgramVkey","type":"bytes32","internalType":"bytes32"},{"name":"updateClientAndMembershipProgramVkey","type":"bytes32","internalType":"bytes32"},{"name":"misbehaviourProgramVkey","type":"bytes32","internalType":"bytes32"},{"name":"sp1Verifier","type":"address","internalType":"address"},{"name":"_clientState","type":"bytes","internalType":"bytes"},{"name":"_consensusState","type":"bytes32","internalType":"bytes32"},{"name":"roleManager","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"ALLOWED_SP1_CLOCK_DRIFT","inputs":[],"outputs":[{"name":"","type":"uint16","internalType":"uint16"}],"stateMutability":"view"},{"type":"function","name":"DEFAULT_ADMIN_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"MEMBERSHIP_PROGRAM_VKEY","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"MISBEHAVIOUR_PROGRAM_VKEY","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"PROOF_SUBMITTER_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"UPDATE_CLIENT_PROGRAM_VKEY","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"VERIFIER","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract ISP1Verifier"}],"stateMutability":"view"},{"type":"function","name":"clientState","inputs":[],"outputs":[{"name":"chainId","type":"string","internalType":"string"},{"name":"trustLevel","type":"tuple","internalType":"struct IICS07TendermintMsgs.TrustThreshold","components":[{"name":"numerator","type":"uint8","internalType":"uint8"},{"name":"denominator","type":"uint8","internalType":"uint8"}]},{"name":"latestHeight","type":"tuple","internalType":"struct IICS02ClientMsgs.Height","components":[{"name":"revisionNumber","type":"uint64","internalType":"uint64"},{"name":"revisionHeight","type":"uint64","internalType":"uint64"}]},{"name":"trustingPeriod","type":"uint32","internalType":"uint32"},{"name":"unbondingPeriod","type":"uint32","internalType":"uint32"},{"name":"isFrozen","type":"bool","internalType":"bool"},{"name":"zkAlgorithm","type":"uint8","internalType":"enum IICS07TendermintMsgs.SupportedZkAlgorithm"}],"stateMutability":"view"},{"type":"function","name":"getClientState","inputs":[],"outputs":[{"name":"","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"getConsensusStateHash","inputs":[{"name":"revisionHeight","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"getRoleAdmin","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"grantRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"hasRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"misbehaviour","inputs":[{"name":"misbehaviourMsg","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"multicall","inputs":[{"name":"data","type":"bytes[]","internalType":"bytes[]"}],"outputs":[{"name":"results","type":"bytes[]","internalType":"bytes[]"}],"stateMutability":"nonpayable"},{"type":"function","name":"renounceRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"callerConfirmation","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"revokeRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"updateClient","inputs":[{"name":"updateMsg","type":"bytes","internalType":"bytes"}],"outputs":[{"name":"","type":"uint8","internalType":"enum ILightClientMsgs.UpdateResult"}],"stateMutability":"nonpayable"},{"type":"function","name":"verifyMembership","inputs":[{"name":"msg_","type":"tuple","internalType":"struct ILightClientMsgs.MsgVerifyMembership","components":[{"name":"proof","type":"bytes","internalType":"bytes"},{"name":"proofHeight","type":"tuple","internalType":"struct IICS02ClientMsgs.Height","components":[{"name":"revisionNumber","type":"uint64","internalType":"uint64"},{"name":"revisionHeight","type":"uint64","internalType":"uint64"}]},{"name":"path","type":"bytes[]","internalType":"bytes[]"},{"name":"value","type":"bytes","internalType":"bytes"}]}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"verifyNonMembership","inputs":[{"name":"msg_","type":"tuple","internalType":"struct ILightClientMsgs.MsgVerifyNonMembership","components":[{"name":"proof","type":"bytes","internalType":"bytes"},{"name":"proofHeight","type":"tuple","internalType":"struct IICS02ClientMsgs.Height","components":[{"name":"revisionNumber","type":"uint64","internalType":"uint64"},{"name":"revisionHeight","type":"uint64","internalType":"uint64"}]},{"name":"path","type":"bytes[]","internalType":"bytes[]"}]}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"event","name":"RoleAdminChanged","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"previousAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"newAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"AccessControlBadConfirmation","inputs":[]},{"type":"error","name":"AccessControlUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"neededRole","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"AddressEmptyCode","inputs":[{"name":"target","type":"address","internalType":"address"}]},{"type":"error","name":"CannotHandleMisbehavior","inputs":[]},{"type":"error","name":"ChainIdMismatch","inputs":[{"name":"expected","type":"string","internalType":"string"},{"name":"actual","type":"string","internalType":"string"}]},{"type":"error","name":"ClientStateMismatch","inputs":[{"name":"expected","type":"bytes","internalType":"bytes"},{"name":"actual","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"ConsensusStateHashMismatch","inputs":[{"name":"expected","type":"bytes32","internalType":"bytes32"},{"name":"actual","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"ConsensusStateNotFound","inputs":[]},{"type":"error","name":"ConsensusStateRootMismatch","inputs":[{"name":"expected","type":"bytes32","internalType":"bytes32"},{"name":"actual","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"EmptyValue","inputs":[]},{"type":"error","name":"FailedCall","inputs":[]},{"type":"error","name":"FeatureNotSupported","inputs":[]},{"type":"error","name":"FrozenClientState","inputs":[]},{"type":"error","name":"InvalidMembershipProof","inputs":[]},{"type":"error","name":"KeyValuePairNotInCache","inputs":[{"name":"path","type":"bytes[]","internalType":"bytes[]"},{"name":"value","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"LengthIsOutOfRange","inputs":[{"name":"length","type":"uint256","internalType":"uint256"},{"name":"min","type":"uint256","internalType":"uint256"},{"name":"max","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"MembershipProofKeyNotFound","inputs":[{"name":"path","type":"bytes[]","internalType":"bytes[]"}]},{"type":"error","name":"MembershipProofValueMismatch","inputs":[{"name":"expected","type":"bytes","internalType":"bytes"},{"name":"actual","type":"bytes","internalType":"bytes"}]},{"type":"error","name":"ProofHeightMismatch","inputs":[{"name":"expectedRevisionNumber","type":"uint64","internalType":"uint64"},{"name":"expectedRevisionHeight","type":"uint64","internalType":"uint64"},{"name":"actualRevisionNumber","type":"uint64","internalType":"uint64"},{"name":"actualRevisionHeight","type":"uint64","internalType":"uint64"}]},{"type":"error","name":"ProofIsInTheFuture","inputs":[{"name":"now","type":"uint256","internalType":"uint256"},{"name":"proofTimestamp","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"ProofIsTooOld","inputs":[{"name":"now","type":"uint256","internalType":"uint256"},{"name":"proofTimestamp","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"TrustThresholdMismatch","inputs":[{"name":"expectedNumerator","type":"uint256","internalType":"uint256"},{"name":"expectedDenominator","type":"uint256","internalType":"uint256"},{"name":"actualNumerator","type":"uint256","internalType":"uint256"},{"name":"actualDenominator","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"TrustingPeriodMismatch","inputs":[{"name":"expected","type":"uint256","internalType":"uint256"},{"name":"actual","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"TrustingPeriodTooLong","inputs":[{"name":"trustingPeriod","type":"uint256","internalType":"uint256"},{"name":"unbondingPeriod","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"UnbondingPeriodMismatch","inputs":[{"name":"expected","type":"uint256","internalType":"uint256"},{"name":"actual","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"UnknownMembershipProofType","inputs":[{"name":"proofType","type":"uint8","internalType":"uint8"}]},{"type":"error","name":"UnknownZkAlgorithm","inputs":[{"name":"algorithm","type":"uint8","internalType":"uint8"}]},{"type":"error","name":"VerificationKeyMismatch","inputs":[{"name":"expected","type":"bytes32","internalType":"bytes32"},{"name":"actual","type":"bytes32","internalType":"bytes32"}]}],"bytecode":{"object":"0x61012060405234610541576137358038038061001a81610564565b928339810161010082820312610541578151916020810151604082015160608301519061004960808501610589565b60a08501519095906001600160401b0381116105415785019080601f8301121561054157815161007b9260200161059d565b9261008d60e060c08701519601610589565b9660805260a05260c05260e05280518101906020820190602081840312610541576020810151906001600160401b038211610541570191829003601f1981019061012013610541576040519160e083016001600160401b0381118482101761052d5760405260208401516001600160401b038111610541576020908501019080601f830112156105415781516101259260200161059d565b825260408112610541576040610139610545565b916101458286016105dd565b8352610153606086016105dd565b602084015260208401928352603f19011261054157610170610545565b61017c608085016105eb565b815261018a60a085016105eb565b6020820152604083019081526101a260c085016105ff565b90606084019182526101b660e086016105ff565b92608085019384526101008601519586151587036105415760a0860196875261012001519460028610156105415760c08101958652518051906001600160401b03821161052d57600154600181811c91168015610523575b602082101461050f57601f81116104ac575b50602090601f831160011461043f5763ffffffff95949392915f9183610434575b50508160011b915f199060031b1c1916176001555b5160ff81511661ff00602060025493015160081b169161ffff191617176002555160018060401b0381511660035491602068010000000000000000600160801b0391015160401b169160018060801b031916171760035551169267ffffffff00000000600454925160201b169051151560401b92519160028310156104205769ff00000000000000000068ff00000000000000009360481b169469ff000000000000000000199160018060481b0319161716179116171760045560018060401b0360035460401c165f52600560205260405f205560018060a01b03166101005260045463ffffffff8116610708810163ffffffff811161040c5763ffffffff809360201c1692839116116103f757826001600160a01b0381166103de575061037c610706565b505b604051612f0c908161078982396080518181816105570152611482015260a0518181816101f90152611e3e015260c051818181610cf001526121c2015260e0518181816102a00152610afe015261010051818181610cb50152611a570152f35b806103eb6103f192610610565b50610686565b5061037e565b6333fae18560e21b5f5260045260245260445ffd5b634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52602160045260245ffd5b015190505f80610241565b90601f1983169160015f52815f20925f5b818110610494575091600193918563ffffffff99989796941061047c575b505050811b01600155610256565b01515f1960f88460031b161c191690555f808061046e565b92936020600181928786015181550195019301610450565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f840160051c81019160208510610505575b601f0160051c01905b8181106104fa5750610220565b5f81556001016104ed565b90915081906104e4565b634e487b7160e01b5f52602260045260245ffd5b90607f169061020e565b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b60408051919082016001600160401b0381118382101761052d57604052565b6040519190601f01601f191682016001600160401b0381118382101761052d57604052565b51906001600160a01b038216820361054157565b9192916001600160401b03821161052d576105c1601f8301601f1916602001610564565b938285528282011161054157815f926020928387015e84010152565b519060ff8216820361054157565b51906001600160401b038216820361054157565b519063ffffffff8216820361054157565b6001600160a01b0381165f9081525f5160206137155f395f51905f52602052604090205460ff16610681576001600160a01b03165f8181525f5160206137155f395f51905f5260205260408120805460ff191660011790553391905f5160206136955f395f51905f528180a4600190565b505f90565b6001600160a01b0381165f9081525f5160206136b55f395f51905f52602052604090205460ff16610681576001600160a01b03165f8181525f5160206136b55f395f51905f5260205260408120805460ff191660011790553391905f5160206136f55f395f51905f52905f5160206136955f395f51905f529080a4600190565b5f80525f5160206136b55f395f51905f526020525f5160206136d55f395f51905f525460ff16610784575f8080525f5160206136b55f395f51905f526020525f5160206136d55f395f51905f52805460ff1916600117905533905f5160206136f55f395f51905f525f5160206136955f395f51905f528280a4600190565b5f9056fe6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a714610d13575080630225293e14610cd957806308c84e7014610c895780630bece35614610bc657806323842fb814610b96578063248a9ca314610b6c5780632c3ee47414610b505780632f2ff15d14610b21578063314d4dff14610ae757806336568abe14610a8b5780634d6d9ffb146109a95780635972185a1461096f578063682ed5f01461084b57806391d1485414610802578063a217fddf146107e8578063ac9650d81461066a578063bd3ce6b01461057a578063ca7242f914610540578063d547741f1461050a578063ddba65371461021c578063e45a6d0d146101e25763ef913a4b1461010e575f80fd5b346101de575f6003193601126101de576101da60405160208082015261012060408201526101c6816101436101608201610f03565b60ff600254818116606085015260081c16608083015267ffffffffffffffff60035481811660a085015260401c1660c083015260ff60045463ffffffff811660e085015263ffffffff8160201c16610100850152818160401c16151561012085015260481c166101b28161109c565b61014083015203601f198101835282611079565b604051918291602083526020830190610e35565b0390f35b5f80fd5b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5761022a36610db1565b6004549160ff8360401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754610298929060ff16156104d5575b810190611116565b6102c78151517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b6020815101518051810160208101916020818303126101de57602081015167ffffffffffffffff81116101de57019061018090829003126101de576040519061030f82611009565b602081015167ffffffffffffffff81116101de5783908201602001906103349161129d565b825261034260408201611385565b60208301908152906103578460608301611256565b604084019081529161036c8560a08401611256565b60608501908152946103818160e085016113a2565b92608086019384526101400190610397916113a2565b9360a081019485525190516fffffffffffffffffffffffffffffffff166103bd9161265e565b516040805182516fffffffffffffffffffffffffffffffff166020808301918252840151828401529190920151606080840191909152825290610401608082611079565b51902090516020015167ffffffffffffffff1661041d90611686565b61042a91908181146118b9565b516040805182516fffffffffffffffffffffffffffffffff16602080830191825284015182840152919092015160608084019190915282529061046e608082611079565b51902090516020015167ffffffffffffffff1661048a90611686565b61049791908181146118b9565b516104a190611a40565b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff1668010000000000000000176004555f80f35b6104dd6117cb565b610290565b7f928b1233000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101de5761053e61051b36610e02565b90610539610534825f525f602052600160405f20015490565b611853565b611be2565b005b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de575f6003193601126101de576040516105a18161059a81610f03565b0382611079565b6101206040516105b081610fc0565b60025460ff8116825260ff602083019160081c16815260ff6040516105d481610fc0565b67ffffffffffffffff600354818116835281602084019160401c168152816004549385808660481c1697816106146040519d8d8f9e8f9081520190610e35565b9a511660208c0152511660408a0152511660608801525116608086015263ffffffff811660a086015263ffffffff8160201c1660c086015260401c16151560e08401526106608161109c565b6101008301520390f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de57366023820112156101de57806004013567ffffffffffffffff81116101de57602482013660248360051b850101116101de576020906040516106cf8382611079565b5f815282810191601f1984013684376106e785611772565b956106f56040519788611079565b858752601f1961070487611772565b01855f5b8281106107d8575050505f5b868110156107c5576001906107a15f8061076e8861073a60248760051b8a01018a6116cd565b8d8d6040959395519483869484860198893784019083820190898252519283915e010185815203601f198101835282611079565b5190305af43d156107bd573d90610784826110a6565b916107926040519384611079565b82523d5f8b84013e5b30612d1f565b6107ab828b61178a565b526107b6818a61178a565b5001610714565b60609061079b565b604051868152806101da8189018b610e5a565b606082828c010152018690610708565b346101de575f6003193601126101de5760206040515f8152f35b346101de5761081036610e02565b905f525f60205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060ff60405f2054166040519015158152f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de578060040160a060031983360301126101de5760ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475460ff1615610962575b60848201916108ee83836116cd565b90501561093a576020928261090681610932956116cd565b602461092a61092361091b606489018761171e565b9790966116cd565b36916110c2565b950191611d2b565b604051908152f35b7f1208b21b000000000000000000000000000000000000000000000000000000005f5260045ffd5b61096a6117cb565b6108df565b346101de575f6003193601126101de5760206040517fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b8152f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de5780600401608060031983360301126101de5760ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754909261093292909160ff1615610a7e575b610a61610a5482806116cd565b919092606485019061171e565b929091602460405195610a748988611079565b5f87520191611d2b565b610a866117cb565b610a47565b346101de57610a9936610e02565b3373ffffffffffffffffffffffffffffffffffffffff821603610abf5761053e91611be2565b7f6697b232000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5761053e610b3236610e02565b90610b4b610534825f525f602052600160405f20015490565b611b10565b346101de575f6003193601126101de5760206040516107088152f35b346101de5760206003193601126101de5760206109326004355f525f602052600160405f20015490565b346101de5760206003193601126101de5760043567ffffffffffffffff811681036101de57610932602091611686565b346101de57610bd436610db1565b60ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754610c3c929060ff1615610c7c5761146e565b6040516003821015610c4f576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b610c846117cb565b61146e565b346101de575f6003193601126101de57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5760206003193601126101de57600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036101de57817f7965db0b0000000000000000000000000000000000000000000000000000000060209314908115610d87575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483610d80565b9060206003198301126101de5760043567ffffffffffffffff81116101de57826023820112156101de5780600401359267ffffffffffffffff84116101de57602484830101116101de576024019190565b60031960409101126101de576004359060243573ffffffffffffffffffffffffffffffffffffffff811681036101de5790565b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b9080602083519182815201916020808360051b8301019401925f915b838310610e8557505050505090565b9091929394602080610ea383601f1986600196030187528951610e35565b97019301930191939290610e76565b90600182811c92168015610ef9575b6020831014610ecc57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610ec1565b6001545f9291610f1282610eb2565b8082529160018116908115610f865750600114610f2d575050565b60015f9081529293509091907fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b838310610f6c575060209250010190565b600181602092949394548385870101520191019190610f5b565b60209495507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091509291921683830152151560051b010190565b6040810190811067ffffffffffffffff821117610fdc57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60c0810190811067ffffffffffffffff821117610fdc57604052565b6020810190811067ffffffffffffffff821117610fdc57604052565b6060810190811067ffffffffffffffff821117610fdc57604052565b60e0810190811067ffffffffffffffff821117610fdc57604052565b90601f601f19910116810190811067ffffffffffffffff821117610fdc57604052565b60021115610c4f57565b67ffffffffffffffff8111610fdc57601f01601f191660200190565b9291926110ce826110a6565b916110dc6040519384611079565b8294818452818301116101de578281602093845f960137010152565b9080601f830112156101de57816020611113933591016110c2565b90565b6020818303126101de5780359067ffffffffffffffff82116101de5701906020828203126101de576040519161114b83611025565b80359067ffffffffffffffff82116101de57016060818303126101de576040519161117583611041565b81358352602082013567ffffffffffffffff81116101de57816111999184016110f8565b6020840152604082013567ffffffffffffffff81116101de576111bc92016110f8565b6040820152815290565b156111cf575050565b7fd56bdc26000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b929192611209826110a6565b916112176040519384611079565b8294818452818301116101de578281602093845f96015e010152565b519060ff821682036101de57565b519067ffffffffffffffff821682036101de57565b91908260409103126101de5760405161126e81610fc0565b602061128781839561127f81611241565b855201611241565b910152565b519063ffffffff821682036101de57565b91908281039261012084126101de57604051916112b98361105d565b8294825167ffffffffffffffff81116101de57830182601f820112156101de576040916112ef84836020601f19955191016111fd565b865201126101de576113349060405161130781610fc0565b61131360208501611233565b815261132160408501611233565b6020820152602085015260608301611256565b604083015261134560a0820161128c565b606083015261135660c0820161128c565b608083015260e08101519081151582036101de576101009160a084015201519060028210156101de5760c00152565b51906fffffffffffffffffffffffffffffffff821682036101de57565b91908260609103126101de576040516113ba81611041565b60408082946113c881611385565b8452602081015160208501520151910152565b9190610180838203126101de57604051906113f582611009565b819380519167ffffffffffffffff83116101de576101408261141e8360a096611287960161129d565b865261142d83602083016113a2565b602087015261143f83608083016113a2565b604087015261145060e08201611385565b6060870152611463836101008301611256565b608087015201611256565b61147a91810190611116565b6114a98151517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b60208151015180518101906020818303126101de5760208101519167ffffffffffffffff83116101de576114e49260208092019201016113db565b906114ee826118f0565b6114f782611983565b916003831015610c4f578261162957908167ffffffffffffffff6020604060a0611113960193845184848201511685600354851c1681116115a1575b5050015160405161158681611578858201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b03601f198101835282611079565b51902092510151165f52600560205260405f20555b51611a40565b6115e08661162293511667ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006003541617600355565b7fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff00000000000000006003549260401b16911617600355565b5f80611533565b506001820361166e5761111390680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff600454161760045561159b565b6002821461167f576111139061159b565b5050600290565b67ffffffffffffffff165f52600560205260405f205480156116a55790565b7f5b48b457000000000000000000000000000000000000000000000000000000005f5260045ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101de570180359067ffffffffffffffff82116101de576020019181360383136101de57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101de570180359067ffffffffffffffff82116101de57602001918160051b360383136101de57565b67ffffffffffffffff8111610fdc5760051b60200190565b805182101561179e5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b335f9081527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e0602052604090205460ff161561180357565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004527fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b60245260445ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260ff60405f2054161561188a5750565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b156118c2575050565b7f6d23e8a3000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6119819061191681516fffffffffffffffffffffffffffffffff6060840151169061265e565b61197967ffffffffffffffff602060808185015160405161196b81611578868201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b519020940151015116611686565b8082146118b9565b565b67ffffffffffffffff602060a08301510151165f52600560205260405f205480155f146119b05750505f90565b604082019081516040516119f98161157860208201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b5190201491821592611a17575b505015611a1257600190565b600290565b6fffffffffffffffffffffffffffffffff91925060208291015151169151511611155f80611a06565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169080516040602083015192015192803b156101de575f92611ad892611aea604051968795869485947f41493c600000000000000000000000000000000000000000000000000000000086526004860152606060248601526064850190610e35565b90600319848303016044850152610e35565b03915afa8015611b0557611afb5750565b5f61198191611079565b6040513d5f823e3d90fd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f205416155f14611bdc57805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f2054165f14611bdc57805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b3567ffffffffffffffff811681036101de5790565b929190611ccb81611772565b93611cd96040519586611079565b602085838152019160051b8101918383116101de5781905b838210611cff575050505050565b813567ffffffffffffffff81116101de57602091611d2087849387016110f8565b815201910190611cf1565b90949291939480156125a5578101906020818303126101de5780359067ffffffffffffffff82116101de57016040818303126101de5760405191611d6e83610fc0565b813560028110156101de578352602082013567ffffffffffffffff81116101de57611d9992016110f8565b90602081019182528051611dac8161109c565b611db58161109c565b612022575051908151820160208101926020818303126101de57602081015167ffffffffffffffff81116101de570190608090829003126101de5760405192611dfd84610fc0565b602082015167ffffffffffffffff81116101de5782611e26836020604094611e2e9701016129d0565b8652016113a2565b9460208301958652611e658351517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b6020835101519182518301926020818503126101de5760208101519067ffffffffffffffff82116101de5701926040848203126101de5760405193611ea985610fc0565b6020810151855260408101519167ffffffffffffffff83116101de57611ed6926020809201920101612a36565b94611ef06020850196808852518015158061201657612b83565b5f805b87518051831015611fe357611f0b83611f1e9261178a565b5151611f18368888611cbf565b90612db8565b15611fd957508694611f7d611f92956fffffffffffffffffffffffffffffffff9a9895611f7661159b966020633b9aca009f9d8198611f5d915161178a565b51015190815181518082149182611fc5575b5050612bc1565b6001612c0f565b51950194611f8a86611caa565b875191612e2a565b516001815111611fa6575b50505151160490565b611fb2611fbe92611caa565b90848451511691612eba565b5f80611f9d565b909150898401209089830120145f80611f6f565b9060010190611ef3565b5061159b92506020915094611f7d611f92956fffffffffffffffffffffffffffffffff9a9895633b9aca009c9a98612c0f565b5061ffff811115612b83565b949060018695939495516120358161109c565b61203e8161109c565b146120855760ff86516120508161109c565b6120598161109c565b7fd3a2e6c9000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90919293945051926060602060405161209d81610fc0565b6040516120a981611009565b6040516120b58161105d565b8481526040516120c481610fc0565b5f81525f85820152848201526040516120dc81610fc0565b5f81525f8582015260408201525f858201525f60808201525f60a08201525f60c0820152815261210a612998565b83820152612116612998565b60408201525f8482015260405161212c81610fc0565b5f81525f84820152608082015260405161214581610fc0565b5f81525f8482015260a08201528152015283518401936020818603126101de5760208101519067ffffffffffffffff82116101de5701936020858203126101de576040519461219386611025565b60208101519067ffffffffffffffff82116101de5760206121bc92816121e995019201016129d0565b808652517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b602084510151948551860160208101966020818303126101de57602081015167ffffffffffffffff81116101de570190604090829003126101de576040519661223188610fc0565b602082015167ffffffffffffffff81116101de57816020612254928501016113db565b885260408201519167ffffffffffffffff83116101de576122789201602001612a36565b936122926020880195808752518015158061201657612b83565b60208101956122a087611caa565b67ffffffffffffffff60a08a5101519181602084015116918291161480612586575b6122cb85611caa565b9267ffffffffffffffff6122de8c611caa565b915116911561253857505050506122f99061159b89516118f0565b6123038751611983565b6003811015610c4f5780612507575061231b86611caa565b67ffffffffffffffff8060035460401c169116116124b4575b5060408651015160405161237d8161157860208201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b51902067ffffffffffffffff61239287611caa565b165f52600560205260405f20555b5f805b8551805183101561248957611f0b836123bb9261178a565b1561247f5750633b9aca0096936fffffffffffffffffffffffffffffffff9693611f76879460206123f261240a9660409b5161178a565b51015190815181518082149182612469575050612bc1565b6124318351858101519067ffffffffffffffff602060a08185015193015101511690612e2a565b516001815111612447575b505051015151160490565b61245361246292611caa565b90858585510151511691612eba565b5f8061243c565b9091506020840120906020830120145f80611f6f565b90600101906123a3565b50604095925061240a9150936fffffffffffffffffffffffffffffffff9693633b9aca009895612c0f565b6124c06124f591611caa565b67ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006003541617600355565b6125016115e086611caa565b5f612334565b60019150036123a0577f59bc8fd3000000000000000000000000000000000000000000000000000000005f5260045ffd5b6084945067ffffffffffffffff9081604051957fb8d8167e00000000000000000000000000000000000000000000000000000000875216600486015216602484015260448301526064820152fd5b5061259084611caa565b67ffffffffffffffff808451169116146122c2565b505090916125b860206125cd9201611caa565b91604051946125c686610fc0565b3691611cbf565b8352602083019182526040516125ec81611578866020830195866128ca565b5190205c915190518215612606575050633b9aca00900490565b9061265a612648926040519384937ff06fc33b000000000000000000000000000000000000000000000000000000008552604060048601526044850190610e5a565b90600319848303016024850152610e35565b0390fd5b906fffffffffffffffffffffffffffffffff633b9aca0091160442811161289b5780420342811161286e576107081061283f57508051516126a0600154610eb2565b1480612818575b815190156127d85750602081015160ff815116906002549160ff83169283821492836127bf575b6020015160ff169215612777575050505063ffffffff606082015116906004549163ffffffff831680820361274957505063ffffffff608081920151169160201c1680820361271b575050565b7f1eb5c1eb000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b7f73b1bde3000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6084945060ff90604051947fd382033a000000000000000000000000000000000000000000000000000000008652600486015260081c16602484015260448301526064820152fd5b6020810151600883901c60ff90811691161493506126ce565b61265a906040519182917ff6b6676b0000000000000000000000000000000000000000000000000000000083526040600484015261264860448401610f03565b508051602081519101206040516128328161059a81610f03565b60208151910120146126a7565b7f12e74add000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f20daedb6000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b9067ffffffffffffffff90939293168152604060208201526080810190835191604080830152825180915260a0820190602060a08260051b8501019401915f905b82821061294f57505050506020611113939401519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc082850301910152610e35565b9091929460208061298a837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6089600196030186528951610e35565b97019201920190929161290b565b604051906129a582611041565b5f6040838281528260208201520152565b9080601f830112156101de578151611113926020016111fd565b91906060838203126101de57604051906129e982611041565b819380518352602081015167ffffffffffffffff81116101de5782612a0f9183016129b6565b602084015260408101519167ffffffffffffffff83116101de5760409261128792016129b6565b9080601f830112156101de57815191612a4e83611772565b92612a5c6040519485611079565b80845260208085019160051b830101918383116101de5760208101915b838310612a8857505050505090565b825167ffffffffffffffff81116101de578201906040601f1983880301126101de5760405190612ab782610fc0565b602083015167ffffffffffffffff81116101de5760209084010187601f820112156101de578051612ae781611772565b91612af56040519384611079565b81835260208084019260051b820101918a83116101de5760208201905b838210612b555750505050825260408301519167ffffffffffffffff83116101de57612b46886020809695819601016129b6565b83820152815201920191612a79565b815167ffffffffffffffff81116101de57602091612b788e8480948801016129b6565b815201910190612b12565b15612b8b5750565b7fb0369c31000000000000000000000000000000000000000000000000000000005f52600452600160245261ffff60445260645ffd5b91909115612bcd575050565b9061265a612648926040519384937f5f1ca381000000000000000000000000000000000000000000000000000000008552604060048601526044850190610e35565b91909115612c1b575050565b604051907ffef760c700000000000000000000000000000000000000000000000000000000825280602483016020600485015252604482019260448260051b84010191815f907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603015b838310612c945786860387fd5b90919293947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8782030188528535828112156101de578301906020823592019167ffffffffffffffff81116101de5780360383136101de57602082601f19601f8480600198869897879852868601375f85828601015201160101970198019301919096939296612c87565b90612d5c5750805115612d3457602081519101fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580612daf575b612d6d575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15612d65565b908151815103611bdc575f5b8251811015612e2257612dd7818461178a565b5151612de3828461178a565b515103612e1b57612df4818461178a565b5160208151910120612e06828461178a565b516020815191012003612e1b57600101612dc4565b5050505f90565b505050600190565b91612e7f60209261197960405185810190612e7681611578888591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b51902091611686565b0151808203612e8c575050565b7f4b2dfe98000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b929190925f5b8451811015612ef8578083612ed76001938861178a565b51604051612eee81611578602082019489866128ca565b5190205d01612ec0565b505050905056fea164736f6c634300081c000a2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac47bd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10bad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5","sourceMap":"1355:25496:38:-:0;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;3774:52;1355:25496;3774:52;1355:25496;3836:47;1355:25496;3893:80;1355:25496;3983:51;1355:25496;;4059:60;;;1355:25496;4059:60;;1355:25496;;;;;;;;;4059:60;;1355:25496;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;-1:-1:-1;1355:25496:38;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;4129:21;1355:25496;;;-1:-1:-1;1355:25496:38;;;;;;;;;4220:36;1355:25496;;;;;2472:10;;;1355:25496;2472:10;;;;1355:25496;2472:10;;1355:25496;2472:10;1355:25496;;;;;4288:83;2472:10;;1355:25496;-1:-1:-1;;;;;1355:25496:38;;;;4529:44;;;:::i;:::-;;4484:351;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4484:351;4637:43;;4736:45;4637:43;;:::i;:::-;;4736:45;:::i;:::-;;4484:351;;2472:10;;;;-1:-1:-1;2472:10:38;1355:25496;2472:10;;;;-1:-1:-1;2472:10:38;;1355:25496;;;-1:-1:-1;2472:10:38;;1355:25496;2472:10;;-1:-1:-1;2472:10:38;1355:25496;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;1355:25496:38;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;1355:25496:38;;;;;;:::o;:::-;;;;-1:-1:-1;;;;;1355:25496:38;;;;;;;;-1:-1:-1;;1355:25496:38;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;1355:25496:38;;;;;;:::o;:::-;;;;;;;;;;:::o;6155:316:85:-;-1:-1:-1;;;;;1355:25496:38;;;;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;;;;;;-1:-1:-1;;1355:25496:38;;;;;735:10:113;;1355:25496:38;-1:-1:-1;;;;;;;;;;;1355:25496:38;;6346:40:85;6323:4;6400:11;:::o;6248:217::-;6442:12;1355:25496:38;6442:12:85;:::o;6155:316::-;-1:-1:-1;;;;;1355:25496:38;;;;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;;;;;;;;;-1:-1:-1;;;;;1355:25496:38;;;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;;;;;;-1:-1:-1;;1355:25496:38;;;;;735:10:113;;1355:25496:38;-1:-1:-1;;;;;;;;;;;2576:33:38;-1:-1:-1;;;;;;;;;;;6346:40:85;1355:25496:38;6346:40:85;6323:4;6400:11;:::o;6155:316::-;1355:25496:38;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;;;;;;;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;-1:-1:-1;;;;;;;;;;;1355:25496:38;;-1:-1:-1;;1355:25496:38;6323:4:85;1355:25496:38;;;735:10:113;;-1:-1:-1;;;;;;;;;;;;;;;;;;;;;;1355:25496:38;;6346:40:85;6323:4;6400:11;:::o;6248:217::-;1355:25496:38;6442:12:85;:::o","linkReferences":{}},"deployedBytecode":{"object":"0x6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a714610d13575080630225293e14610cd957806308c84e7014610c895780630bece35614610bc657806323842fb814610b96578063248a9ca314610b6c5780632c3ee47414610b505780632f2ff15d14610b21578063314d4dff14610ae757806336568abe14610a8b5780634d6d9ffb146109a95780635972185a1461096f578063682ed5f01461084b57806391d1485414610802578063a217fddf146107e8578063ac9650d81461066a578063bd3ce6b01461057a578063ca7242f914610540578063d547741f1461050a578063ddba65371461021c578063e45a6d0d146101e25763ef913a4b1461010e575f80fd5b346101de575f6003193601126101de576101da60405160208082015261012060408201526101c6816101436101608201610f03565b60ff600254818116606085015260081c16608083015267ffffffffffffffff60035481811660a085015260401c1660c083015260ff60045463ffffffff811660e085015263ffffffff8160201c16610100850152818160401c16151561012085015260481c166101b28161109c565b61014083015203601f198101835282611079565b604051918291602083526020830190610e35565b0390f35b5f80fd5b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5761022a36610db1565b6004549160ff8360401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754610298929060ff16156104d5575b810190611116565b6102c78151517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b6020815101518051810160208101916020818303126101de57602081015167ffffffffffffffff81116101de57019061018090829003126101de576040519061030f82611009565b602081015167ffffffffffffffff81116101de5783908201602001906103349161129d565b825261034260408201611385565b60208301908152906103578460608301611256565b604084019081529161036c8560a08401611256565b60608501908152946103818160e085016113a2565b92608086019384526101400190610397916113a2565b9360a081019485525190516fffffffffffffffffffffffffffffffff166103bd9161265e565b516040805182516fffffffffffffffffffffffffffffffff166020808301918252840151828401529190920151606080840191909152825290610401608082611079565b51902090516020015167ffffffffffffffff1661041d90611686565b61042a91908181146118b9565b516040805182516fffffffffffffffffffffffffffffffff16602080830191825284015182840152919092015160608084019190915282529061046e608082611079565b51902090516020015167ffffffffffffffff1661048a90611686565b61049791908181146118b9565b516104a190611a40565b7fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff1668010000000000000000176004555f80f35b6104dd6117cb565b610290565b7f928b1233000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101de5761053e61051b36610e02565b90610539610534825f525f602052600160405f20015490565b611853565b611be2565b005b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de575f6003193601126101de576040516105a18161059a81610f03565b0382611079565b6101206040516105b081610fc0565b60025460ff8116825260ff602083019160081c16815260ff6040516105d481610fc0565b67ffffffffffffffff600354818116835281602084019160401c168152816004549385808660481c1697816106146040519d8d8f9e8f9081520190610e35565b9a511660208c0152511660408a0152511660608801525116608086015263ffffffff811660a086015263ffffffff8160201c1660c086015260401c16151560e08401526106608161109c565b6101008301520390f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de57366023820112156101de57806004013567ffffffffffffffff81116101de57602482013660248360051b850101116101de576020906040516106cf8382611079565b5f815282810191601f1984013684376106e785611772565b956106f56040519788611079565b858752601f1961070487611772565b01855f5b8281106107d8575050505f5b868110156107c5576001906107a15f8061076e8861073a60248760051b8a01018a6116cd565b8d8d6040959395519483869484860198893784019083820190898252519283915e010185815203601f198101835282611079565b5190305af43d156107bd573d90610784826110a6565b916107926040519384611079565b82523d5f8b84013e5b30612d1f565b6107ab828b61178a565b526107b6818a61178a565b5001610714565b60609061079b565b604051868152806101da8189018b610e5a565b606082828c010152018690610708565b346101de575f6003193601126101de5760206040515f8152f35b346101de5761081036610e02565b905f525f60205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052602060ff60405f2054166040519015158152f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de578060040160a060031983360301126101de5760ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac475460ff1615610962575b60848201916108ee83836116cd565b90501561093a576020928261090681610932956116cd565b602461092a61092361091b606489018761171e565b9790966116cd565b36916110c2565b950191611d2b565b604051908152f35b7f1208b21b000000000000000000000000000000000000000000000000000000005f5260045ffd5b61096a6117cb565b6108df565b346101de575f6003193601126101de5760206040517fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b8152f35b346101de5760206003193601126101de5760043567ffffffffffffffff81116101de5780600401608060031983360301126101de5760ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e060209081527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754909261093292909160ff1615610a7e575b610a61610a5482806116cd565b919092606485019061171e565b929091602460405195610a748988611079565b5f87520191611d2b565b610a866117cb565b610a47565b346101de57610a9936610e02565b3373ffffffffffffffffffffffffffffffffffffffff821603610abf5761053e91611be2565b7f6697b232000000000000000000000000000000000000000000000000000000005f5260045ffd5b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5761053e610b3236610e02565b90610b4b610534825f525f602052600160405f20015490565b611b10565b346101de575f6003193601126101de5760206040516107088152f35b346101de5760206003193601126101de5760206109326004355f525f602052600160405f20015490565b346101de5760206003193601126101de5760043567ffffffffffffffff811681036101de57610932602091611686565b346101de57610bd436610db1565b60ff60045460401c166104e2575f80527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e06020527f6e0c24a6e293ff9b755263dbaa15ba3796b0b8d3fe17cfb4ddf8143b268eac4754610c3c929060ff1615610c7c5761146e565b6040516003821015610c4f576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b610c846117cb565b61146e565b346101de575f6003193601126101de57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101de575f6003193601126101de5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346101de5760206003193601126101de57600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036101de57817f7965db0b0000000000000000000000000000000000000000000000000000000060209314908115610d87575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483610d80565b9060206003198301126101de5760043567ffffffffffffffff81116101de57826023820112156101de5780600401359267ffffffffffffffff84116101de57602484830101116101de576024019190565b60031960409101126101de576004359060243573ffffffffffffffffffffffffffffffffffffffff811681036101de5790565b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b9080602083519182815201916020808360051b8301019401925f915b838310610e8557505050505090565b9091929394602080610ea383601f1986600196030187528951610e35565b97019301930191939290610e76565b90600182811c92168015610ef9575b6020831014610ecc57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610ec1565b6001545f9291610f1282610eb2565b8082529160018116908115610f865750600114610f2d575050565b60015f9081529293509091907fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b838310610f6c575060209250010190565b600181602092949394548385870101520191019190610f5b565b60209495507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091509291921683830152151560051b010190565b6040810190811067ffffffffffffffff821117610fdc57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60c0810190811067ffffffffffffffff821117610fdc57604052565b6020810190811067ffffffffffffffff821117610fdc57604052565b6060810190811067ffffffffffffffff821117610fdc57604052565b60e0810190811067ffffffffffffffff821117610fdc57604052565b90601f601f19910116810190811067ffffffffffffffff821117610fdc57604052565b60021115610c4f57565b67ffffffffffffffff8111610fdc57601f01601f191660200190565b9291926110ce826110a6565b916110dc6040519384611079565b8294818452818301116101de578281602093845f960137010152565b9080601f830112156101de57816020611113933591016110c2565b90565b6020818303126101de5780359067ffffffffffffffff82116101de5701906020828203126101de576040519161114b83611025565b80359067ffffffffffffffff82116101de57016060818303126101de576040519161117583611041565b81358352602082013567ffffffffffffffff81116101de57816111999184016110f8565b6020840152604082013567ffffffffffffffff81116101de576111bc92016110f8565b6040820152815290565b156111cf575050565b7fd56bdc26000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b929192611209826110a6565b916112176040519384611079565b8294818452818301116101de578281602093845f96015e010152565b519060ff821682036101de57565b519067ffffffffffffffff821682036101de57565b91908260409103126101de5760405161126e81610fc0565b602061128781839561127f81611241565b855201611241565b910152565b519063ffffffff821682036101de57565b91908281039261012084126101de57604051916112b98361105d565b8294825167ffffffffffffffff81116101de57830182601f820112156101de576040916112ef84836020601f19955191016111fd565b865201126101de576113349060405161130781610fc0565b61131360208501611233565b815261132160408501611233565b6020820152602085015260608301611256565b604083015261134560a0820161128c565b606083015261135660c0820161128c565b608083015260e08101519081151582036101de576101009160a084015201519060028210156101de5760c00152565b51906fffffffffffffffffffffffffffffffff821682036101de57565b91908260609103126101de576040516113ba81611041565b60408082946113c881611385565b8452602081015160208501520151910152565b9190610180838203126101de57604051906113f582611009565b819380519167ffffffffffffffff83116101de576101408261141e8360a096611287960161129d565b865261142d83602083016113a2565b602087015261143f83608083016113a2565b604087015261145060e08201611385565b6060870152611463836101008301611256565b608087015201611256565b61147a91810190611116565b6114a98151517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b60208151015180518101906020818303126101de5760208101519167ffffffffffffffff83116101de576114e49260208092019201016113db565b906114ee826118f0565b6114f782611983565b916003831015610c4f578261162957908167ffffffffffffffff6020604060a0611113960193845184848201511685600354851c1681116115a1575b5050015160405161158681611578858201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b03601f198101835282611079565b51902092510151165f52600560205260405f20555b51611a40565b6115e08661162293511667ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006003541617600355565b7fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff6fffffffffffffffff00000000000000006003549260401b16911617600355565b5f80611533565b506001820361166e5761111390680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff600454161760045561159b565b6002821461167f576111139061159b565b5050600290565b67ffffffffffffffff165f52600560205260405f205480156116a55790565b7f5b48b457000000000000000000000000000000000000000000000000000000005f5260045ffd5b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101de570180359067ffffffffffffffff82116101de576020019181360383136101de57565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156101de570180359067ffffffffffffffff82116101de57602001918160051b360383136101de57565b67ffffffffffffffff8111610fdc5760051b60200190565b805182101561179e5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b335f9081527f4c48d1d2b0f4f7485fc28e3db22341d96a20aa29e6efa8149da9751603abd4e0602052604090205460ff161561180357565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f52336004527fbd893629a699470e4ec82a5715bb4981fdaacc5d0a728bf5f55b801d8f4ef10b60245260445ffd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260ff60405f2054161561188a5750565b7fe2517d3f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b156118c2575050565b7f6d23e8a3000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6119819061191681516fffffffffffffffffffffffffffffffff6060840151169061265e565b61197967ffffffffffffffff602060808185015160405161196b81611578868201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b519020940151015116611686565b8082146118b9565b565b67ffffffffffffffff602060a08301510151165f52600560205260405f205480155f146119b05750505f90565b604082019081516040516119f98161157860208201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b5190201491821592611a17575b505015611a1257600190565b600290565b6fffffffffffffffffffffffffffffffff91925060208291015151169151511611155f80611a06565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169080516040602083015192015192803b156101de575f92611ad892611aea604051968795869485947f41493c600000000000000000000000000000000000000000000000000000000086526004860152606060248601526064850190610e35565b90600319848303016044850152610e35565b03915afa8015611b0557611afb5750565b5f61198191611079565b6040513d5f823e3d90fd5b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f205416155f14611bdc57805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0082541617905573ffffffffffffffffffffffffffffffffffffffff339216907f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d5f80a4600190565b50505f90565b805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260ff60405f2054165f14611bdc57805f525f60205260405f2073ffffffffffffffffffffffffffffffffffffffff83165f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00815416905573ffffffffffffffffffffffffffffffffffffffff339216907ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b5f80a4600190565b3567ffffffffffffffff811681036101de5790565b929190611ccb81611772565b93611cd96040519586611079565b602085838152019160051b8101918383116101de5781905b838210611cff575050505050565b813567ffffffffffffffff81116101de57602091611d2087849387016110f8565b815201910190611cf1565b90949291939480156125a5578101906020818303126101de5780359067ffffffffffffffff82116101de57016040818303126101de5760405191611d6e83610fc0565b813560028110156101de578352602082013567ffffffffffffffff81116101de57611d9992016110f8565b90602081019182528051611dac8161109c565b611db58161109c565b612022575051908151820160208101926020818303126101de57602081015167ffffffffffffffff81116101de570190608090829003126101de5760405192611dfd84610fc0565b602082015167ffffffffffffffff81116101de5782611e26836020604094611e2e9701016129d0565b8652016113a2565b9460208301958652611e658351517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b6020835101519182518301926020818503126101de5760208101519067ffffffffffffffff82116101de5701926040848203126101de5760405193611ea985610fc0565b6020810151855260408101519167ffffffffffffffff83116101de57611ed6926020809201920101612a36565b94611ef06020850196808852518015158061201657612b83565b5f805b87518051831015611fe357611f0b83611f1e9261178a565b5151611f18368888611cbf565b90612db8565b15611fd957508694611f7d611f92956fffffffffffffffffffffffffffffffff9a9895611f7661159b966020633b9aca009f9d8198611f5d915161178a565b51015190815181518082149182611fc5575b5050612bc1565b6001612c0f565b51950194611f8a86611caa565b875191612e2a565b516001815111611fa6575b50505151160490565b611fb2611fbe92611caa565b90848451511691612eba565b5f80611f9d565b909150898401209089830120145f80611f6f565b9060010190611ef3565b5061159b92506020915094611f7d611f92956fffffffffffffffffffffffffffffffff9a9895633b9aca009c9a98612c0f565b5061ffff811115612b83565b949060018695939495516120358161109c565b61203e8161109c565b146120855760ff86516120508161109c565b6120598161109c565b7fd3a2e6c9000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b90919293945051926060602060405161209d81610fc0565b6040516120a981611009565b6040516120b58161105d565b8481526040516120c481610fc0565b5f81525f85820152848201526040516120dc81610fc0565b5f81525f8582015260408201525f858201525f60808201525f60a08201525f60c0820152815261210a612998565b83820152612116612998565b60408201525f8482015260405161212c81610fc0565b5f81525f84820152608082015260405161214581610fc0565b5f81525f8482015260a08201528152015283518401936020818603126101de5760208101519067ffffffffffffffff82116101de5701936020858203126101de576040519461219386611025565b60208101519067ffffffffffffffff82116101de5760206121bc92816121e995019201016129d0565b808652517f00000000000000000000000000000000000000000000000000000000000000008082146111c6565b602084510151948551860160208101966020818303126101de57602081015167ffffffffffffffff81116101de570190604090829003126101de576040519661223188610fc0565b602082015167ffffffffffffffff81116101de57816020612254928501016113db565b885260408201519167ffffffffffffffff83116101de576122789201602001612a36565b936122926020880195808752518015158061201657612b83565b60208101956122a087611caa565b67ffffffffffffffff60a08a5101519181602084015116918291161480612586575b6122cb85611caa565b9267ffffffffffffffff6122de8c611caa565b915116911561253857505050506122f99061159b89516118f0565b6123038751611983565b6003811015610c4f5780612507575061231b86611caa565b67ffffffffffffffff8060035460401c169116116124b4575b5060408651015160405161237d8161157860208201948591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b51902067ffffffffffffffff61239287611caa565b165f52600560205260405f20555b5f805b8551805183101561248957611f0b836123bb9261178a565b1561247f5750633b9aca0096936fffffffffffffffffffffffffffffffff9693611f76879460206123f261240a9660409b5161178a565b51015190815181518082149182612469575050612bc1565b6124318351858101519067ffffffffffffffff602060a08185015193015101511690612e2a565b516001815111612447575b505051015151160490565b61245361246292611caa565b90858585510151511691612eba565b5f8061243c565b9091506020840120906020830120145f80611f6f565b90600101906123a3565b50604095925061240a9150936fffffffffffffffffffffffffffffffff9693633b9aca009895612c0f565b6124c06124f591611caa565b67ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006003541617600355565b6125016115e086611caa565b5f612334565b60019150036123a0577f59bc8fd3000000000000000000000000000000000000000000000000000000005f5260045ffd5b6084945067ffffffffffffffff9081604051957fb8d8167e00000000000000000000000000000000000000000000000000000000875216600486015216602484015260448301526064820152fd5b5061259084611caa565b67ffffffffffffffff808451169116146122c2565b505090916125b860206125cd9201611caa565b91604051946125c686610fc0565b3691611cbf565b8352602083019182526040516125ec81611578866020830195866128ca565b5190205c915190518215612606575050633b9aca00900490565b9061265a612648926040519384937ff06fc33b000000000000000000000000000000000000000000000000000000008552604060048601526044850190610e5a565b90600319848303016024850152610e35565b0390fd5b906fffffffffffffffffffffffffffffffff633b9aca0091160442811161289b5780420342811161286e576107081061283f57508051516126a0600154610eb2565b1480612818575b815190156127d85750602081015160ff815116906002549160ff83169283821492836127bf575b6020015160ff169215612777575050505063ffffffff606082015116906004549163ffffffff831680820361274957505063ffffffff608081920151169160201c1680820361271b575050565b7f1eb5c1eb000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b7f73b1bde3000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b6084945060ff90604051947fd382033a000000000000000000000000000000000000000000000000000000008652600486015260081c16602484015260448301526064820152fd5b6020810151600883901c60ff90811691161493506126ce565b61265a906040519182917ff6b6676b0000000000000000000000000000000000000000000000000000000083526040600484015261264860448401610f03565b508051602081519101206040516128328161059a81610f03565b60208151910120146126a7565b7f12e74add000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f20daedb6000000000000000000000000000000000000000000000000000000005f524260045260245260445ffd5b9067ffffffffffffffff90939293168152604060208201526080810190835191604080830152825180915260a0820190602060a08260051b8501019401915f905b82821061294f57505050506020611113939401519060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc082850301910152610e35565b9091929460208061298a837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6089600196030186528951610e35565b97019201920190929161290b565b604051906129a582611041565b5f6040838281528260208201520152565b9080601f830112156101de578151611113926020016111fd565b91906060838203126101de57604051906129e982611041565b819380518352602081015167ffffffffffffffff81116101de5782612a0f9183016129b6565b602084015260408101519167ffffffffffffffff83116101de5760409261128792016129b6565b9080601f830112156101de57815191612a4e83611772565b92612a5c6040519485611079565b80845260208085019160051b830101918383116101de5760208101915b838310612a8857505050505090565b825167ffffffffffffffff81116101de578201906040601f1983880301126101de5760405190612ab782610fc0565b602083015167ffffffffffffffff81116101de5760209084010187601f820112156101de578051612ae781611772565b91612af56040519384611079565b81835260208084019260051b820101918a83116101de5760208201905b838210612b555750505050825260408301519167ffffffffffffffff83116101de57612b46886020809695819601016129b6565b83820152815201920191612a79565b815167ffffffffffffffff81116101de57602091612b788e8480948801016129b6565b815201910190612b12565b15612b8b5750565b7fb0369c31000000000000000000000000000000000000000000000000000000005f52600452600160245261ffff60445260645ffd5b91909115612bcd575050565b9061265a612648926040519384937f5f1ca381000000000000000000000000000000000000000000000000000000008552604060048601526044850190610e35565b91909115612c1b575050565b604051907ffef760c700000000000000000000000000000000000000000000000000000000825280602483016020600485015252604482019260448260051b84010191815f907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603015b838310612c945786860387fd5b90919293947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc8782030188528535828112156101de578301906020823592019167ffffffffffffffff81116101de5780360383136101de57602082601f19601f8480600198869897879852868601375f85828601015201160101970198019301919096939296612c87565b90612d5c5750805115612d3457602081519101fd5b7fd6bda275000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580612daf575b612d6d575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15612d65565b908151815103611bdc575f5b8251811015612e2257612dd7818461178a565b5151612de3828461178a565b515103612e1b57612df4818461178a565b5160208151910120612e06828461178a565b516020815191012003612e1b57600101612dc4565b5050505f90565b505050600190565b91612e7f60209261197960405185810190612e7681611578888591909160408060608301946fffffffffffffffffffffffffffffffff8151168452602081015160208501520151910152565b51902091611686565b0151808203612e8c575050565b7f4b2dfe98000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b929190925f5b8451811015612ef8578083612ed76001938861178a565b51604051612eee81611578602082019489866128ca565b5190205d01612ec0565b505050905056fea164736f6c634300081c000a","sourceMap":"1355:25496:38:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;4960:23;;;;1355:25496;;;;;;4960:23;1355:25496;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4960:23;1355:25496;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;4960:23;-1:-1:-1;;4960:23:38;;;;;;:::i;:::-;1355:25496;;;;;4960:23;1355:25496;;4960:23;1355:25496;;;;:::i;:::-;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;1648:48;1355:25496;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;9619:70;;1355:25496;;;26731:42;26727:105;;1355:25496;9619:70;;;;:::i;:::-;9699:196;9720:30;;1355:25496;9759:25;9720:64;;;9699:196;:::i;:::-;1355:25496;9982:30;;:43;;1355:25496;;9971:95;;1355:25496;9971:95;;1355:25496;;;;;;;;;9971:95;;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;19046:18;1355:25496;;;;19066:11;;;:::i;:::-;19282:29;1355:25496;;;;;;;;19271:41;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;19271:41;;;;1355:25496;;19271:41;:::i;:::-;1355:25496;19261:52;;19381:21;;1355:25496;19381:36;1355:25496;;;19359:59;;;:::i;:::-;19428:179;;19449:54;;;;19428:179;:::i;:::-;19811:29;1355:25496;;;;;;;;19800:41;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;19800:41;;;;1355:25496;;19800:41;:::i;:::-;1355:25496;19790:52;;19910:21;;1355:25496;19910:36;1355:25496;;;19888:59;;;:::i;:::-;19957:179;;19978:54;;;;19957:179;:::i;:::-;10139:30;;;;:::i;:::-;1355:25496;;;;;;;;;26727:105;26800:20;;:::i;:::-;26727:105;;1355:25496;;;;;;;;;;;4723:26:85;1355:25496:38;;;:::i;:::-;4693:18:85;2484:4;4693:18;;3877:6;1355:25496:38;3877:6:85;1355:25496:38;;3877:22:85;1355:25496:38;3877:6:85;1355:25496:38;3877:22:85;1355:25496:38;3786:120:85;;4693:18;2484:4;:::i;:::-;4723:26;:::i;:::-;1355:25496:38;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;1551:51;1355:25496;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;:::i;:::-;2095:51;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;2095:51;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;;1355:25496:38;;;:::i;:::-;;;;;;;;;;1528:13:116;;;1355:25496:38;1560:3:116;1543:15;;;;;;1349:26;1355:25496:38;4107:55:111;1355:25496:38;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;:::i;:::-;4065:25:111;1629:4:116;;4065:25:111;;1355:25496:38;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;1629:4:116;4107:55:111;:::i;:::-;1579:88:116;;;;:::i;:::-;;;;;;:::i;:::-;;1355:25496:38;1528:13:116;;1355:25496:38;;;;;1543:15:116;1355:25496:38;;;;;;;;;;1543:15:116;1355:25496:38;:::i;:::-;;;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;2930:29:85;1355:25496:38;-1:-1:-1;1355:25496:38;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;26731:42;26727:105;;1355:25496;7188:10;;;;;;;;:::i;:::-;:21;;;1355:25496;;;7253:10;;;;7241:64;7253:10;;:::i;:::-;7265:16;1355:25496;7294:10;7283:9;;;;;;:::i;:::-;7294:10;;;;:::i;:::-;1355:25496;;;:::i;:::-;7265:16;;7241:64;;:::i;:::-;1355:25496;;;;;;;;;;;;;26727:105;26800:20;;:::i;:::-;26727:105;;1355:25496;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;2576:33;1355:25496;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;7544:63;;1355:25496;;;;26731:42;26727:105;;1355:25496;7586:9;7556:10;;;;:::i;:::-;7586:9;;;;;;;;:::i;:::-;1355:25496;;;7568:16;1355:25496;;;;;;;:::i;:::-;;;;7568:16;7544:63;;:::i;26727:105::-;26800:20;;:::i;:::-;26727:105;;1355:25496;;;;;;;:::i;:::-;735:10:113;1355:25496:38;;;5397:34:85;5393:102;;5505:37;;;:::i;5393:102::-;5454:30;1355:25496:38;5454:30:85;1355:25496:38;;5454:30:85;1355:25496:38;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;1854:50;1355:25496;;;;;;;4306:25:85;1355:25496:38;;;:::i;:::-;4276:18:85;2484:4;4276:18;;3877:6;1355:25496:38;3877:6:85;1355:25496:38;;3877:22:85;1355:25496:38;3877:6:85;1355:25496:38;3877:22:85;1355:25496:38;3786:120:85;;2484:4;4306:25;:::i;1355:25496:38:-;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;2472:10;1355:25496;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;3877:6:85;1355:25496:38;3877:6:85;1355:25496:38;;3877:22:85;1355:25496:38;3877:6:85;1355:25496:38;3877:22:85;1355:25496:38;3786:120:85;;1355:25496:38;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;5401:1560;;1355:25496;;;26731:42;26727:105;;5401:1560;:::i;:::-;1355:25496;;;;;;;;;;;;;;;;;;;;;;;26727:105;26800:20;;:::i;:::-;5401:1560;:::i;1355:25496::-;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;1950:38;1355:25496;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;1742:66;1355:25496;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;2649:47:85;2664:32;1355:25496:38;2649:47:85;;:87;;;;;1355:25496:38;;;;;;;2649:87:85;844:25:122;829:40;;;2649:87:85;;;1355:25496:38;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;2095:51;1355:25496;;;;;;;:::i;:::-;;;;;2095:51;1355:25496;;;2095:51;;;;1355:25496;;;;;;;:::o;:::-;2095:51;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;:::o;:::-;2095:51;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;1355:25496:38;;;:::o;:::-;;;;;;;;-1:-1:-1;;1355:25496:38;;;;:::o;:::-;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::o;:::-;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;;;;;;:::i;5401:1560::-;5644:58;5401:1560;5644:58;;;;:::i;:::-;5712:186;5733:24;;1355:25496;5766:26;5733:59;;;5712:186;:::i;:::-;5985:37;:24;;:37;;1355:25496;;5974:89;;1355:25496;5985:37;1355:25496;;;;;;5985:37;5974:89;;1355:25496;;;;;;;;5974:89;5985:37;5974:89;;;1355:25496;;;;:::i;:::-;6108:6;;;;:::i;:::-;6171:26;;;:::i;:::-;1355:25496;;;;;;;6211:52;;;6344:16;;1355:25496;5985:37;1355:25496;6344:16;6899:24;6344:16;;;;;:31;;;;1355:25496;;;;;;;;6344:73;;6340:155;;6207:666;6586:24;;;;1355:25496;;6575:36;;;;;;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6575:36;;-1:-1:-1;;6575:36:38;;;;;;:::i;:::-;1355:25496;6565:47;;6530:16;;:31;1355:25496;;-1:-1:-1;1355:25496:38;6508:21;5985:37;1355:25496;;-1:-1:-1;1355:25496:38;;6207:666;6899:24;;:::i;6340:155::-;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6340:155;;;;6207:666;-1:-1:-1;1355:25496:38;6633:58;;1355:25496;;6899:24;1355:25496;;;6707:20;1355:25496;;;6707:20;1355:25496;6207:666;;6629:244;6771:34;6755:50;;6751:122;;6899:24;6629:244;6207:666;;6751:122;6821:41;;6771:34;6821:41;:::o;5036:228::-;1355:25496;;-1:-1:-1;1355:25496:38;5145:21;1355:25496;;;-1:-1:-1;1355:25496:38;;5200:9;;1355:25496;;5036:228;:::o;1355:25496::-;;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;3175:103:85;735:10:113;2930:6:85;1355:25496:38;;;;;;;;;;;;3495:23:85;3491:108;;3175:103::o;3491:108::-;3541:47;2930:6;3541:47;735:10:113;3541:47:85;1355:25496:38;2576:33;1355:25496;;;2930:6:85;3541:47;3175:103;1355:25496:38;2930:6:85;1355:25496:38;2930:6:85;1355:25496:38;;;2930:6:85;1355:25496:38;;735:10:113;1355:25496:38;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;3495:23:85;3491:108;;3175:103;:::o;3491:108::-;3541:47;2930:6;3541:47;735:10:113;3541:47:85;1355:25496:38;;;;2930:6:85;3541:47;1355:25496:38;;;;;;:::o;:::-;;;;;;;;;;;18217:570;18605:175;18217:570;18383:11;18363:18;;1355:25496;18383:11;;;1355:25496;;18383:11;;:::i;:::-;18537:58;1355:25496;18462:28;18559:20;18462:28;;;;1355:25496;;18451:40;;;;;;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;18451:40;1355:25496;18441:51;;18559:20;;;:35;1355:25496;;18537:58;:::i;:::-;18626:52;;;18605:175;:::i;:::-;18217:570::o;22962:1028::-;1355:25496;23184:31;:16;;;;:31;1355:25496;;;;23162:21;23184:31;1355:25496;;;;;23230:32;;23226:758;23230:32;;;23350:43;;1355:25496;23350:43;:::o;23226:758::-;1355:25496;23470:24;;;;;1355:25496;;23459:36;;;23184:31;23459:36;;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23459:36;1355:25496;23449:47;;23427:69;;;;:165;;;23226:758;-1:-1:-1;;23410:574:38;;;23768:42;23761:49;:::o;23410:574::-;23939:34;23932:41;:::o;23427:165::-;1355:25496;23516:28;;;23184:31;23516:28;;;;1355:25496;;23558:24;;1355:25496;;-1:-1:-1;23516:76:38;23427:165;;;;24151:152;1355:25496;24231:8;1355:25496;;;;24284:11;24264:18;;;;24284:11;;;24231:65;;;;;;-1:-1:-1;1355:25496:38;;;;24284:11;1355:25496;24231:65;;;;;;;1355:25496;24231:65;;;;;1355:25496;;;;;;;;;;;:::i;:::-;;-1:-1:-1;;1355:25496:38;;;;;;;;;:::i;:::-;24231:65;;;;;;;;;;24151:152;:::o;24231:65::-;-1:-1:-1;24231:65:38;;;:::i;:::-;24284:11;1355:25496;;-1:-1:-1;1355:25496:38;;;;;6155:316:85;1355:25496:38;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;6252:23:85;6248:217;1355:25496:38;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;-1:-1:-1;1355:25496:38;6323:4:85;1355:25496:38;;;;;;;;735:10:113;1355:25496:38;;6346:40:85;;1355:25496:38;6346:40:85;;6323:4;6400:11;:::o;6248:217::-;6442:12;;1355:25496:38;6442:12:85;:::o;6708:317::-;1355:25496:38;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;6802:217:85;1355:25496:38;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;-1:-1:-1;1355:25496:38;;;;;;;;735:10:113;1355:25496:38;;6900:40:85;;1355:25496:38;6900:40:85;;1355:25496:38;6954:11:85;:::o;1355:25496:38:-;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;8191:1064;;;;;;;8421:17;;8417:181;;8665:52;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;8731:83;;8876:21;;1355:25496;;;10929:60;;1355:25496;10929:60;;1355:25496;;;;;;;;;10929:60;;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;10999:160;11020:14;;1355:25496;11043:23;11020:46;;;10999:160;:::i;:::-;1355:25496;11242:14;;:27;;1355:25496;;;11231:75;;1355:25496;;;;;;;;;11231:75;;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;11231:75;1355:25496;11231:75;;;1355:25496;;;;:::i;:::-;;11316:177;1355:25496;;;;;;;;11337:25;;;:70;;;11316:177;:::i;:::-;1355:25496;;11663:3;11640:14;;1355:25496;;11636:25;;;;;11703:17;;11691:43;11703:17;;:::i;:::-;;:22;1355:25496;;;;;:::i;:::-;11691:43;;:::i;:::-;11690:44;11686:99;;11824:14;;;12132:50;12335:14;11824;1355:25496;11824:14;;;11865:185;12280:27;11824:14;1355:25496;26385:3;11824:14;;;;:17;:14;;:17;:::i;:::-;;:23;;1355:25496;;;;;11894:30;;;:72;;;;11663:3;11865:185;;;:::i;:::-;1355:25496;12132:50;:::i;:::-;1355:25496;12252:26;;;;;;:::i;:::-;12280:27;;;;:::i;12335:14::-;12464;1355:25496;;;12464:25;12460:152;;11616:503;12651:27;;;1355:25496;;;8830:81;:::o;12460:152::-;12519:26;12505:96;12519:26;;:::i;:::-;12563:27;;;;1355:25496;;12505:96;;:::i;:::-;12460:152;;;;11894:72;1355:25496;;;;;;11928:16;1355:25496;;;;11948:18;11928:38;11894:72;;;;11686:99;11758:8;1355:25496;;11621:13;;;11636:25;;12280:27;11636:25;;1355:25496;11636:25;;;12132:50;12335:14;11636:25;1355:25496;11636:25;;;26385:3;11636:25;;;12132:50;:::i;11337:70::-;11366:41;1355:25496;11366:41;;;11316:177;:::i;8727:421::-;1355:25496;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;8932:98;8928:220;;1355:25496;8727:421;1355:25496;;;;:::i;:::-;;;;:::i;:::-;9188:60;1355:25496;9188:60;1355:25496;9188:60;1355:25496;;;9188:60;8928:220;9102:21;;;;;;;1355:25496;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;1355:25496:38;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;13696:75;;1355:25496;;;;;;;;;13696:75;;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;13696:75;;13785:208;13696:75;;1355:25496;;;;:::i;:::-;;;;;13833:41;13810:64;;;13785:208;:::i;:::-;1355:25496;14028:14;;:27;;1355:25496;;;14017:95;;1355:25496;14017:95;;1355:25496;;;;;;;;;14017:95;;1355:25496;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;;14126:189;1355:25496;;;;;;;;14151:25;;;:70;;;14126:189;:::i;:::-;1355:25496;14355:26;;;;;;:::i;:::-;1355:25496;;14385:25;;:35;;:50;;1355:25496;14385:50;;1355:25496;;;;;;14355:80;:184;;;8928:220;14598:26;;;:::i;:::-;14646;1355:25496;14646:26;;;:::i;:::-;1355:25496;;;;;;;14897:25;;;;14954:14;14897:25;;;;;:::i;14954:14::-;15080:45;15099:25;;15080:45;:::i;:::-;1355:25496;;;;;;15143:52;;;15284:26;;;;:::i;:::-;1355:25496;;;;;;;;;15284:68;15280:153;;15139:599;15543:25;1355:25496;15543:25;;:43;;1355:25496;;15532:55;;;1355:25496;15532:55;;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;15532:55;1355:25496;15522:66;;1355:25496;15472:26;;;:::i;:::-;1355:25496;;;15450:21;1355:25496;;;;;;15139:599;1355:25496;;15927:3;15904:14;;1355:25496;;15900:25;;;;;15967:17;;15955:43;15967:17;;:::i;15955:43::-;15954:44;15950:99;;16088:14;26385:3;16088:14;;1355:25496;16088:14;;16129:185;16088:14;;1355:25496;16088:17;16396:50;16088:14;1355:25496;16088:14;;:17;:::i;:::-;;:23;;1355:25496;;;;;16158:30;;;:72;;;;16129:185;;;:::i;16396:50::-;16632:43;16506:25;;:43;;;;:48;1355:25496;;;16506:48;;;1355:25496;16568:35;;;:50;1355:25496;;16632:43;;:::i;:::-;16799:14;1355:25496;;;16799:25;16795:198;;15880:503;17032:25;;;:43;;1355:25496;;;9046:91;:::o;16795:198::-;16871:26;16840:142;16871:26;;:::i;:::-;16915:25;;;;;:43;;1355:25496;;16840:142;;:::i;:::-;16795:198;;;;16158:72;1355:25496;;;;;;16192:16;1355:25496;;;;16212:18;16192:38;16158:72;;;;15950:99;16022:8;1355:25496;;15885:13;;;15900:25;;1355:25496;15900:25;;;16396:50;15900:25;;;1355:25496;15900:25;;26385:3;15900:25;;16396:50;:::i;15280:153::-;1355:25496;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;15280:153;;;15139:599;1355:25496;;;15613:58;15139:599;15609:129;15698:25;1355:25496;15698:25;;1355:25496;15698:25;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14355:184;14459:26;;;;:::i;:::-;1355:25496;;;;;;;14459:80;14355:184;;8417:181;8522:26;;;;;;1355:25496;8522:26;;;:::i;:::-;1355:25496;;;;;;;:::i;:::-;;;;:::i;:::-;;;8522:26;8550:35;;1355:25496;;;;;25522:31;;;;8522:26;25522:31;;;;;:::i;:::-;1355:25496;25512:42;;4288:69:120;25671:11:38;;25684:12;;25632:14;;1355:25496;;8489:98;;26385:3;8489:98;1355:25496;8482:105;:::o;1355:25496::-;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;-1:-1:-1;;1355:25496:38;;;;;;;;;:::i;:::-;;;;20400:2140;;1355:25496;26385:3;20400:2140;1355:25496;;20608:15;20583:40;;1355:25496;;20608:15;;1355:25496;20608:15;1355:25496;;;;2472:10;-1:-1:-1;1355:25496:38;;21386:25;;;1355:25496;;21429:11;1355:25496;;:::i;:::-;21380:76;:180;;;20400:2140;21611:25;;1355:25496;;;;21678:28;;;;;1355:25496;;;;;21720:22;1355:25496;;;;;21678:74;;;;:172;;;;20400:2140;21678:28;22062:40;1355:25496;;;;;;;22157:32;;;;1355:25496;22157:32;;;1355:25496;;;22193:26;1355:25496;;;;;22157:62;;;1355:25496;;22358:33;;1355:25496;22358:33;;;;1355:25496;;;21678:28;1355:25496;;22358:64;;;1355:25496;;20400:2140;;:::o;1355:25496::-;;-1:-1:-1;1355:25496:38;22193:26;1355:25496;;;;-1:-1:-1;1355:25496:38;;;-1:-1:-1;1355:25496:38;22193:26;1355:25496;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;21678:172;:28;21772:40;;1355:25496;;;;;;;;;;;21772:78;;-1:-1:-1;21678:172:38;;1355:25496;;;;;;;;;;;;;;;;;;;;;:::i;21380:180::-;21492:25;;;1355:25496;;;;;21476:43;1355:25496;;;;;;;:::i;:::-;;;;;;21523:37;21476:84;21380:180;;1355:25496;;-1:-1:-1;1355:25496:38;20608:15;1355:25496;;;;;-1:-1:-1;1355:25496:38;;;-1:-1:-1;1355:25496:38;;;;;-1:-1:-1;1355:25496:38;;;-1:-1:-1;1355:25496:38;20608:15;1355:25496;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;1355:25496:38;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::o;:::-;;;;;;8961:69;1355:25496;;14284:16;1355:25496;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1355:25496:38;;;;;;;;;;;;;;;-1:-1:-1;1355:25496:38;;;;;;;;;;;;;;;;;;;;;;;;4437:582:111;;4609:8;;-1:-1:-1;1355:25496:38;;5690:21:111;:17;;5815:105;;;;;;5686:301;5957:19;5710:1;5957:19;;5710:1;5957:19;4605:408;1355:25496:38;;4857:22:111;:49;;;4605:408;4853:119;;4985:17;;:::o;4853:119::-;1355:25496:38;4933:24:111;;4878:1;4933:24;1355:25496:38;4933:24:111;1355:25496:38;;4878:1:111;4933:24;4857:49;4883:18;;;:23;4857:49;;358:427:47;;1355:25496:38;;;;452:20:47;448:63;;1355:25496:38;554:3:47;1355:25496:38;;540:12:47;;;;;577:4;;;;:::i;:::-;;1355:25496:38;592:4:47;;;;:::i;:::-;;1355:25496:38;577:26:47;573:77;;677:4;;;;:::i;:::-;;1355:25496:38;;;;;667:15:47;696:4;;;;:::i;:::-;;1355:25496:38;;;;;686:15:47;667:34;663:85;;1355:25496:38;;525:13:47;;663:85;721:12;;;1355:25496:38;721:12:47;:::o;540:::-;;;;1355:25496:38;358:427:47;:::o;17339:759:38:-;;17732:177;17609:33;17339:759;17688:34;1355:25496;;17609:33;;;;;;;;;1355:25496;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17609:33;1355:25496;17599:44;;17688:34;;:::i;17732:177::-;17965:26;1355:25496;17941:50;;;1355:25496;;17339:759;;:::o;1355:25496::-;;-1:-1:-1;1355:25496:38;;;;;;-1:-1:-1;1355:25496:38;24776:318;;;;;24914:1;24937:3;1355:25496;;24917:18;;;;;25011:10;;;1355:25496;25011:10;;;:::i;:::-;;1355:25496;;24987:35;;;;;;;;;;:::i;:::-;1355:25496;24977:46;;4520:68:120;1355:25496:38;24902:13;;24917:18;;;;;;24776:318::o","linkReferences":{},"immutableReferences":{"6367":[{"start":1367,"length":32},{"start":5250,"length":32}],"6370":[{"start":505,"length":32},{"start":7742,"length":32}],"6373":[{"start":3312,"length":32},{"start":8642,"length":32}],"6376":[{"start":672,"length":32},{"start":2814,"length":32}],"6380":[{"start":3253,"length":32},{"start":6743,"length":32}]}},"methodIdentifiers":{"ALLOWED_SP1_CLOCK_DRIFT()":"2c3ee474","DEFAULT_ADMIN_ROLE()":"a217fddf","MEMBERSHIP_PROGRAM_VKEY()":"e45a6d0d","MISBEHAVIOUR_PROGRAM_VKEY()":"314d4dff","PROOF_SUBMITTER_ROLE()":"5972185a","UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY()":"0225293e","UPDATE_CLIENT_PROGRAM_VKEY()":"ca7242f9","VERIFIER()":"08c84e70","clientState()":"bd3ce6b0","getClientState()":"ef913a4b","getConsensusStateHash(uint64)":"23842fb8","getRoleAdmin(bytes32)":"248a9ca3","grantRole(bytes32,address)":"2f2ff15d","hasRole(bytes32,address)":"91d14854","misbehaviour(bytes)":"ddba6537","multicall(bytes[])":"ac9650d8","renounceRole(bytes32,address)":"36568abe","revokeRole(bytes32,address)":"d547741f","supportsInterface(bytes4)":"01ffc9a7","updateClient(bytes)":"0bece356","verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":"682ed5f0","verifyNonMembership((bytes,(uint64,uint64),bytes[]))":"4d6d9ffb"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"updateClientProgramVkey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"membershipProgramVkey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"updateClientAndMembershipProgramVkey\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"misbehaviourProgramVkey\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"sp1Verifier\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"_clientState\",\"type\":\"bytes\"},{\"internalType\":\"bytes32\",\"name\":\"_consensusState\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"roleManager\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AccessControlBadConfirmation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"neededRole\",\"type\":\"bytes32\"}],\"name\":\"AccessControlUnauthorizedAccount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"}],\"name\":\"AddressEmptyCode\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CannotHandleMisbehavior\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"expected\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"actual\",\"type\":\"string\"}],\"name\":\"ChainIdMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"expected\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"actual\",\"type\":\"bytes\"}],\"name\":\"ClientStateMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConsensusStateHashMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"ConsensusStateNotFound\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"ConsensusStateRootMismatch\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"EmptyValue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FailedCall\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FeatureNotSupported\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"FrozenClientState\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidMembershipProof\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"value\",\"type\":\"bytes\"}],\"name\":\"KeyValuePairNotInCache\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"min\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"max\",\"type\":\"uint256\"}],\"name\":\"LengthIsOutOfRange\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"}],\"name\":\"MembershipProofKeyNotFound\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"expected\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"actual\",\"type\":\"bytes\"}],\"name\":\"MembershipProofValueMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"expectedRevisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"expectedRevisionHeight\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"actualRevisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"actualRevisionHeight\",\"type\":\"uint64\"}],\"name\":\"ProofHeightMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"now\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofTimestamp\",\"type\":\"uint256\"}],\"name\":\"ProofIsInTheFuture\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"now\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"proofTimestamp\",\"type\":\"uint256\"}],\"name\":\"ProofIsTooOld\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expectedNumerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expectedDenominator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualNumerator\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actualDenominator\",\"type\":\"uint256\"}],\"name\":\"TrustThresholdMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"TrustingPeriodMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"trustingPeriod\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unbondingPeriod\",\"type\":\"uint256\"}],\"name\":\"TrustingPeriodTooLong\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"UnbondingPeriodMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"proofType\",\"type\":\"uint8\"}],\"name\":\"UnknownMembershipProofType\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"algorithm\",\"type\":\"uint8\"}],\"name\":\"UnknownZkAlgorithm\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"expected\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"actual\",\"type\":\"bytes32\"}],\"name\":\"VerificationKeyMismatch\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ALLOWED_SP1_CLOCK_DRIFT\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MEMBERSHIP_PROGRAM_VKEY\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MISBEHAVIOUR_PROGRAM_VKEY\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PROOF_SUBMITTER_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"UPDATE_CLIENT_PROGRAM_VKEY\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"VERIFIER\",\"outputs\":[{\"internalType\":\"contract ISP1Verifier\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"clientState\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"chainId\",\"type\":\"string\"},{\"components\":[{\"internalType\":\"uint8\",\"name\":\"numerator\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"denominator\",\"type\":\"uint8\"}],\"internalType\":\"struct IICS07TendermintMsgs.TrustThreshold\",\"name\":\"trustLevel\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"revisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"internalType\":\"struct IICS02ClientMsgs.Height\",\"name\":\"latestHeight\",\"type\":\"tuple\"},{\"internalType\":\"uint32\",\"name\":\"trustingPeriod\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"unbondingPeriod\",\"type\":\"uint32\"},{\"internalType\":\"bool\",\"name\":\"isFrozen\",\"type\":\"bool\"},{\"internalType\":\"enum IICS07TendermintMsgs.SupportedZkAlgorithm\",\"name\":\"zkAlgorithm\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getClientState\",\"outputs\":[{\"internalType\":\"bytes\",\"name\":\"\",\"type\":\"bytes\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"name\":\"getConsensusStateHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"misbehaviourMsg\",\"type\":\"bytes\"}],\"name\":\"misbehaviour\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes[]\",\"name\":\"data\",\"type\":\"bytes[]\"}],\"name\":\"multicall\",\"outputs\":[{\"internalType\":\"bytes[]\",\"name\":\"results\",\"type\":\"bytes[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"callerConfirmation\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"updateMsg\",\"type\":\"bytes\"}],\"name\":\"updateClient\",\"outputs\":[{\"internalType\":\"enum ILightClientMsgs.UpdateResult\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"revisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"internalType\":\"struct IICS02ClientMsgs.Height\",\"name\":\"proofHeight\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"},{\"internalType\":\"bytes\",\"name\":\"value\",\"type\":\"bytes\"}],\"internalType\":\"struct ILightClientMsgs.MsgVerifyMembership\",\"name\":\"msg_\",\"type\":\"tuple\"}],\"name\":\"verifyMembership\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"bytes\",\"name\":\"proof\",\"type\":\"bytes\"},{\"components\":[{\"internalType\":\"uint64\",\"name\":\"revisionNumber\",\"type\":\"uint64\"},{\"internalType\":\"uint64\",\"name\":\"revisionHeight\",\"type\":\"uint64\"}],\"internalType\":\"struct IICS02ClientMsgs.Height\",\"name\":\"proofHeight\",\"type\":\"tuple\"},{\"internalType\":\"bytes[]\",\"name\":\"path\",\"type\":\"bytes[]\"}],\"internalType\":\"struct ILightClientMsgs.MsgVerifyNonMembership\",\"name\":\"msg_\",\"type\":\"tuple\"}],\"name\":\"verifyNonMembership\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"author\":\"srdtrk\",\"errors\":{\"AccessControlBadConfirmation()\":[{\"details\":\"The caller of a function is not the expected one. NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.\"}],\"AccessControlUnauthorizedAccount(address,bytes32)\":[{\"details\":\"The `account` is missing a role.\"}],\"AddressEmptyCode(address)\":[{\"details\":\"There's no code at `target` (it is not a contract).\"}],\"CannotHandleMisbehavior()\":[{\"details\":\"Misbehavior cannot be handled in membership handler, so it is returned as an error.\"}],\"ChainIdMismatch(string,string)\":[{\"params\":{\"actual\":\"The actual chain ID.\",\"expected\":\"The expected chain ID.\"}}],\"ClientStateMismatch(bytes,bytes)\":[{\"params\":{\"actual\":\"The actual client state.\",\"expected\":\"The expected client state.\"}}],\"ConsensusStateHashMismatch(bytes32,bytes32)\":[{\"params\":{\"actual\":\"The actual consensus state hash.\",\"expected\":\"The expected consensus state hash.\"}}],\"ConsensusStateRootMismatch(bytes32,bytes32)\":[{\"params\":{\"actual\":\"The actual consensus state root.\",\"expected\":\"The expected consensus state root.\"}}],\"FailedCall()\":[{\"details\":\"A call to an address target failed. The target may have reverted.\"}],\"KeyValuePairNotInCache(bytes[],bytes)\":[{\"params\":{\"path\":\"The path of the key-value pair.\",\"value\":\"The value of the key-value pair.\"}}],\"LengthIsOutOfRange(uint256,uint256,uint256)\":[{\"params\":{\"length\":\"The length of the value.\",\"max\":\"The maximum length of the value.\",\"min\":\"The minimum length of the value.\"}}],\"MembershipProofKeyNotFound(bytes[])\":[{\"params\":{\"path\":\"The path of the key-value pair.\"}}],\"MembershipProofValueMismatch(bytes,bytes)\":[{\"params\":{\"actual\":\"The actual value.\",\"expected\":\"The expected value.\"}}],\"ProofHeightMismatch(uint64,uint64,uint64,uint64)\":[{\"params\":{\"actualRevisionHeight\":\"The actual revision height.\",\"actualRevisionNumber\":\"The actual revision number.\",\"expectedRevisionHeight\":\"The expected revision height.\",\"expectedRevisionNumber\":\"The expected revision number.\"}}],\"ProofIsInTheFuture(uint256,uint256)\":[{\"params\":{\"now\":\"The current timestamp in seconds.\",\"proofTimestamp\":\"The timestamp in the proof in seconds.\"}}],\"ProofIsTooOld(uint256,uint256)\":[{\"params\":{\"now\":\"The current timestamp in seconds.\",\"proofTimestamp\":\"The timestamp in the proof in seconds.\"}}],\"TrustThresholdMismatch(uint256,uint256,uint256,uint256)\":[{\"params\":{\"actualDenominator\":\"The actual denominator of the trust threshold.\",\"actualNumerator\":\"The actual numerator of the trust threshold.\",\"expectedDenominator\":\"The expected denominator of the trust threshold.\",\"expectedNumerator\":\"The expected numerator of the trust threshold.\"}}],\"TrustingPeriodMismatch(uint256,uint256)\":[{\"params\":{\"actual\":\"The actual trusting period in seconds.\",\"expected\":\"The expected trusting period in seconds.\"}}],\"TrustingPeriodTooLong(uint256,uint256)\":[{\"params\":{\"trustingPeriod\":\"The trusting period in seconds.\",\"unbondingPeriod\":\"The unbonding period in seconds.\"}}],\"UnbondingPeriodMismatch(uint256,uint256)\":[{\"params\":{\"actual\":\"The actual unbonding period in seconds.\",\"expected\":\"The expected unbonding period in seconds.\"}}],\"UnknownMembershipProofType(uint8)\":[{\"params\":{\"proofType\":\"The unknown membership proof type.\"}}],\"UnknownZkAlgorithm(uint8)\":[{\"params\":{\"algorithm\":\"The unknown zk algorithm.\"}}],\"VerificationKeyMismatch(bytes32,bytes32)\":[{\"params\":{\"actual\":\"The actual verification key.\",\"expected\":\"The expected verification key.\"}}]},\"events\":{\"RoleAdminChanged(bytes32,bytes32,bytes32)\":{\"details\":\"Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite {RoleAdminChanged} not being emitted to signal this.\"},\"RoleGranted(bytes32,address,address)\":{\"details\":\"Emitted when `account` is granted `role`. `sender` is the account that originated the contract call. This account bears the admin role (for the granted role). Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.\"},\"RoleRevoked(bytes32,address,address)\":{\"details\":\"Emitted when `account` is revoked `role`. `sender` is the account that originated the contract call: - if using `revokeRole`, it is the admin role bearer - if using `renounceRole`, it is the role bearer (i.e. `account`)\"}},\"kind\":\"dev\",\"methods\":{\"constructor\":{\"params\":{\"_clientState\":\"The encoded initial client state.\",\"_consensusState\":\"The encoded initial consensus state.\",\"membershipProgramVkey\":\"The verification key for the verify (non)membership program.\",\"misbehaviourProgramVkey\":\"The verification key for the misbehaviour program.\",\"roleManager\":\"Manages the proof submitters and can submit proofs. Should be the ICS26Router if used in IBC.\",\"sp1Verifier\":\"The address of the SP1 verifier contract.\",\"updateClientAndMembershipProgramVkey\":\"The verification key for the update client and membership program.\",\"updateClientProgramVkey\":\"The verification key for the update client program.\"}},\"getClientState()\":{\"returns\":{\"_0\":\"The client state.\"}},\"getConsensusStateHash(uint64)\":{\"params\":{\"revisionHeight\":\"The revision height.\"},\"returns\":{\"_0\":\"The consensus state at the given revision height.\"}},\"getRoleAdmin(bytes32)\":{\"details\":\"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}.\"},\"grantRole(bytes32,address)\":{\"details\":\"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event.\"},\"hasRole(bytes32,address)\":{\"details\":\"Returns `true` if `account` has been granted `role`.\"},\"misbehaviour(bytes)\":{\"details\":\"The misbehavior is verfied in the sp1 program. Here we only check the public values which contain the trusted headers.\",\"params\":{\"misbehaviourMsg\":\"The misbehaviour message\"}},\"multicall(bytes[])\":{\"custom:oz-upgrades-unsafe-allow-reachable\":\"delegatecall\",\"details\":\"Receives and executes a batch of function calls on this contract.\"},\"renounceRole(bytes32,address)\":{\"details\":\"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event.\"},\"revokeRole(bytes32,address)\":{\"details\":\"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event.\"},\"supportsInterface(bytes4)\":{\"details\":\"Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] to learn more about how these ids are created. This function call must use less than 30 000 gas.\"},\"updateClient(bytes)\":{\"details\":\"This function verifies the public values and forwards the proof to the SP1 verifier.\",\"params\":{\"updateMsg\":\"The encoded update message e.g., an SP1 proof.\"},\"returns\":{\"_0\":\"The result of the update operation\"}},\"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))\":{\"details\":\"Notice that this message is not view, as it may update the client state for caching purposes.\",\"params\":{\"msg_\":\"The membership message\"},\"returns\":{\"_0\":\"The unix timestamp of the verification height in the counterparty chain in seconds.\"}},\"verifyNonMembership((bytes,(uint64,uint64),bytes[]))\":{\"details\":\"Notice that this message is not view, as it may update the client state for caching purposes.\",\"params\":{\"msg_\":\"The membership message\"},\"returns\":{\"_0\":\"The unix timestamp of the verification height in the counterparty chain in seconds.\"}}},\"stateVariables\":{\"ALLOWED_SP1_CLOCK_DRIFT\":{\"return\":\"The allowed prover clock drift in seconds.\",\"returns\":{\"_0\":\"The allowed prover clock drift in seconds.\"}},\"MEMBERSHIP_PROGRAM_VKEY\":{\"return\":\"The verification key for the membership program.\",\"returns\":{\"_0\":\"The verification key for the membership program.\"}},\"MISBEHAVIOUR_PROGRAM_VKEY\":{\"return\":\"The verification key for the misbehaviour program.\",\"returns\":{\"_0\":\"The verification key for the misbehaviour program.\"}},\"PROOF_SUBMITTER_ROLE\":{\"details\":\"The proof submitter role is used to whitelist addresses that can submit proofsIf `address(0)` has this role, then anyone can submit proofsIf this client is hooked up to ICS26Router, the router must be given this role\",\"return\":\"The role identifier\",\"returns\":{\"_0\":\"The role identifier\"}},\"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY\":{\"return\":\"The verification key for the update client and membership program.\",\"returns\":{\"_0\":\"The verification key for the update client and membership program.\"}},\"UPDATE_CLIENT_PROGRAM_VKEY\":{\"return\":\"The verification key for the update client program.\",\"returns\":{\"_0\":\"The verification key for the update client program.\"}},\"VERIFIER\":{\"return\":\"The SP1 verifier contract.\",\"returns\":{\"_0\":\"The SP1 verifier contract.\"}},\"_consensusStateHashes\":{\"details\":\"Revision number need not be keyed as it is not allowed to change.\"}},\"title\":\"SP1 ICS07 Tendermint Light Client\",\"version\":1},\"userdoc\":{\"errors\":{\"CannotHandleMisbehavior()\":[{\"notice\":\"The error that is returned when the update client and membership program contains misbehavior.\"}],\"ChainIdMismatch(string,string)\":[{\"notice\":\"The error that is returned when the chain ID does not match the expected value.\"}],\"ClientStateMismatch(bytes,bytes)\":[{\"notice\":\"The error that is returned when the client state does not match the expected value.\"}],\"ConsensusStateHashMismatch(bytes32,bytes32)\":[{\"notice\":\"The error that is returned when the consensus state hash does not match the expected value.\"}],\"ConsensusStateNotFound()\":[{\"notice\":\"The error that is returned when the consensus state is not found.\"}],\"ConsensusStateRootMismatch(bytes32,bytes32)\":[{\"notice\":\"The error that is returned when the consensus state root does not match the expected value.\"}],\"EmptyValue()\":[{\"notice\":\"Returned when the membership value is empty.\"}],\"FeatureNotSupported()\":[{\"notice\":\"Returned when the feature is not supported.\"}],\"FrozenClientState()\":[{\"notice\":\"The error that is returned when the client state is frozen.\"}],\"InvalidMembershipProof()\":[{\"notice\":\"Returned when the membership proof is invalid.\"}],\"KeyValuePairNotInCache(bytes[],bytes)\":[{\"notice\":\"Returned when a key-value pair is not in the cache.\"}],\"LengthIsOutOfRange(uint256,uint256,uint256)\":[{\"notice\":\"The error that is returned when the length of a value is out of range.\"}],\"MembershipProofKeyNotFound(bytes[])\":[{\"notice\":\"The error that is returned when the key-value pair's path is not contained in the proof.\"}],\"MembershipProofValueMismatch(bytes,bytes)\":[{\"notice\":\"The error that is returned when the key-value pair's value does not match the expected value.\"}],\"ProofHeightMismatch(uint64,uint64,uint64,uint64)\":[{\"notice\":\"The error that is returned when the proof height does not match the expected value.\"}],\"ProofIsInTheFuture(uint256,uint256)\":[{\"notice\":\"The error that is returned when a proof is in the future.\"}],\"ProofIsTooOld(uint256,uint256)\":[{\"notice\":\"The error that is returned when a proof is too old.\"}],\"TrustThresholdMismatch(uint256,uint256,uint256,uint256)\":[{\"notice\":\"The error that is returned when the trust threshold does not match the expected value.\"}],\"TrustingPeriodMismatch(uint256,uint256)\":[{\"notice\":\"The error that is returned when the trusting period does not match the expected value.\"}],\"TrustingPeriodTooLong(uint256,uint256)\":[{\"notice\":\"The error that is returned when the trusting period is longer than the unbonding period.\"}],\"UnbondingPeriodMismatch(uint256,uint256)\":[{\"notice\":\"The error that is returned when the unbonding period does not match the expected value.\"}],\"UnknownMembershipProofType(uint8)\":[{\"notice\":\"The error that is returned when the membership proof type is unknown.\"}],\"UnknownZkAlgorithm(uint8)\":[{\"notice\":\"The error that is returned when the zk algorithm is unknown.\"}],\"VerificationKeyMismatch(bytes32,bytes32)\":[{\"notice\":\"The error that is returned when the verification key does not match the expected value.\"}]},\"kind\":\"user\",\"methods\":{\"ALLOWED_SP1_CLOCK_DRIFT()\":{\"notice\":\"Constant allowed prover clock drift in seconds.\"},\"MEMBERSHIP_PROGRAM_VKEY()\":{\"notice\":\"Immutable membership program verification key.\"},\"MISBEHAVIOUR_PROGRAM_VKEY()\":{\"notice\":\"Immutable misbehaviour program verification key.\"},\"PROOF_SUBMITTER_ROLE()\":{\"notice\":\"The role identifier for the proof submitter role\"},\"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY()\":{\"notice\":\"Immutable update client and membership program verification key.\"},\"UPDATE_CLIENT_PROGRAM_VKEY()\":{\"notice\":\"Immutable update client program verification key.\"},\"VERIFIER()\":{\"notice\":\"Immutable SP1 verifier contract address.\"},\"clientState()\":{\"notice\":\"The ICS07Tendermint client state\"},\"constructor\":{\"notice\":\"The constructor sets the program verification key and the initial client and consensus states.\"},\"getClientState()\":{\"notice\":\"Returns the client state.\"},\"getConsensusStateHash(uint64)\":{\"notice\":\"Returns the consensus state keccak256 hash at the given revision height.\"},\"misbehaviour(bytes)\":{\"notice\":\"Misbehaviour handling, moves the light client to the frozen state if misbehaviour is detected\"},\"updateClient(bytes)\":{\"notice\":\"Updating the client and consensus state\"},\"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))\":{\"notice\":\"Querying the membership of a key-value pair\"},\"verifyNonMembership((bytes,(uint64,uint64),bytes[]))\":{\"notice\":\"Querying the non-membership of a key\"}},\"notice\":\"This contract implements an ICS07 IBC tendermint light client using SP1.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/light-clients/sp1-ics07/SP1ICS07Tendermint.sol\":\"SP1ICS07Tendermint\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"none\"},\"optimizer\":{\"enabled\":true,\"runs\":10000},\"remappings\":[\":@openzeppelin-contracts/=node_modules/@openzeppelin/contracts/\",\":@openzeppelin-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/\",\":@openzeppelin/=node_modules/@openzeppelin/\",\":@sp1-contracts/=node_modules/sp1-contracts/contracts/src/\",\":@uniswap/permit2/=node_modules/@uniswap/permit2/\",\":forge-std/=node_modules/forge-std/src/\",\":sp1-contracts/=node_modules/sp1-contracts/\"],\"viaIR\":true},\"sources\":{\"contracts/interfaces/ILightClient.sol\":{\"keccak256\":\"0x1caae9e4f741a86c0056d5f7713a35f3bd96db7a9fd52fc9f1cf79aad76a442f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4a536db3b926560f69547c17e4f451a2631ddea0f42bd224c7fd8382d74429f6\",\"dweb:/ipfs/QmcQ33kxcmmowXgMbQGTBgiKBjF7v8UiP4y73bB9hhDizC\"]},\"contracts/light-clients/sp1-ics07/SP1ICS07Tendermint.sol\":{\"keccak256\":\"0x2e03f0f1e058e7184ea70dc2a423a3d32a609620b6a1e8b764ec9c287b0b6187\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9e3c0b2f3b3af9cfc8a3ab085e0decffcd40230a27530142293f0b79a4aefe51\",\"dweb:/ipfs/Qmf3qEAMWdiGAMEXSae6mzZ7Sncsbr4QzDC4JzMeiNwgei\"]},\"contracts/light-clients/sp1-ics07/errors/ISP1ICS07TendermintErrors.sol\":{\"keccak256\":\"0xe1a0526f9913308f761af480ef7d87091d6038659b9058f89c50083d49768db1\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a31d6d4a059182b3dfd3e99595d82a134963c448c9152ef35b42c62ccca87635\",\"dweb:/ipfs/QmfQ2Cfa4kxWem3f88o9UnFQgL8oRU6vKkDFWSGmLsVLqN\"]},\"contracts/light-clients/sp1-ics07/interfaces/ISP1ICS07Tendermint.sol\":{\"keccak256\":\"0x0f00aea96b194d1a9db14f708f0de1fe7723fedc7b72bb8ea182e768ffc0a731\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://3111a76ebabf4b57917e93669892df895a54a93a4963e384d8c13a1c47877b44\",\"dweb:/ipfs/QmPCZ7Z4dJpowkzMhizUAW1j8QdJLD3DDjMbYq8G5VeXc1\"]},\"contracts/light-clients/sp1-ics07/msgs/IICS07TendermintMsgs.sol\":{\"keccak256\":\"0xae0ae6ff2742192ebaaee297c7d7473a1e1b4dc73393d45a944b0b7a8a647703\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://43b2f6492a1f727416bbcf55876d9c08ac1f66f4724efc75b935c5022555be20\",\"dweb:/ipfs/QmfTaPFNSFuUEsEBgJjnF2Jr2knayS7YwatfjP7dgdcAyH\"]},\"contracts/light-clients/sp1-ics07/msgs/IMembershipMsgs.sol\":{\"keccak256\":\"0x9dff6833fddbc11caf27387c9f8cba5dca9d1e430c9643d4e4a2e8eb09924d96\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://efa6cfc654510dfcef6f4286e3e66446082bcd5671662ae6e1c6fd0f893a5a4a\",\"dweb:/ipfs/QmRWAg1kHvmRtXgDm4ajVC7tEWQnzTWmHeHMYQJfyFuDGq\"]},\"contracts/light-clients/sp1-ics07/msgs/IMisbehaviourMsgs.sol\":{\"keccak256\":\"0x165b37d890b06eccd47558bf1efd6495e18541627758736a1f5b9c33d0333832\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://15ab15cd5439147abed697bc9644b0175754845608bf3d57c81c2c864d31ce46\",\"dweb:/ipfs/QmYcV88YsM1sZCmhP8LKUFHq3hJ6bsvd66W5axBnsBBoUM\"]},\"contracts/light-clients/sp1-ics07/msgs/ISP1Msgs.sol\":{\"keccak256\":\"0x8ede29b02182df4be9c075b3b48083277e5dd93bb3e7ce1eecfea9728db49aed\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://48edb4bbcd440454fffceb4f85b83c08c3973a212239d7da07c079634fe413dd\",\"dweb:/ipfs/Qmf7ma1nXWghfZKrJJvLpPuY5aN2xZZjHY7kWP3KVFCoDJ\"]},\"contracts/light-clients/sp1-ics07/msgs/IUcAndMembershipMsgs.sol\":{\"keccak256\":\"0xef3619c79de37cabe95d9d8af9d145fc2ea2dd55137407fb4b0082b0491170d0\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://93646d813cdacf5e38e609e54baa67c60d749f0f111d00cd18b003c3cb328204\",\"dweb:/ipfs/QmVcuwPujbiZ644a4jRw2V1LZxhhGsm2HKMXweVqndwUnZ\"]},\"contracts/light-clients/sp1-ics07/msgs/IUpdateClientMsgs.sol\":{\"keccak256\":\"0x370cdfca1c65f1898c7da80507e66972a6fe51a5d5c4e7aeabdd7060ce875aed\",\"license\":\"UNLICENSED\",\"urls\":[\"bzz-raw://ff34288a142b6f40d6843818b9ec0a542db016bc0d4a7be582eb2e255655c43e\",\"dweb:/ipfs/QmXwbJHDbqZdw3JTT48Le1ZWGqmKAG7omf2ePvDfBHomzj\"]},\"contracts/light-clients/sp1-ics07/utils/Paths.sol\":{\"keccak256\":\"0x0e4eec6cd6cd0ef684383946419a2bf53c8f800033acfc298db81688e5aa178b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://824b58c13ff96b6ee38b5d25435fcc1305c45735f52df6347dba3dacdc144ffa\",\"dweb:/ipfs/QmT7qomKM46ChfVVwfBQHwjpoHW9vrG9nXkGXsPeZRLJ7w\"]},\"contracts/msgs/IICS02ClientMsgs.sol\":{\"keccak256\":\"0xb5fdb25319d5c32b3f527982d45ec1e9bec5215515581aee034d853006ea01a0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6b8939bf552c62c952a491008bcd9b59ab7dda20a9482bf93438e05793881542\",\"dweb:/ipfs/QmfWmzNCp8bd4pP26Lfw5HHp4dSZnCDqZDjVN7SD8P7eDh\"]},\"contracts/msgs/ILightClientMsgs.sol\":{\"keccak256\":\"0xa0565e7748b8b3284d72e1aa6ef4cc8264fcaa5f4e0761d22d64686953db6378\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c4476010d7090bfaa74127526f2051c6bc82ff42a09b936e848880a1f15a6065\",\"dweb:/ipfs/QmY87JgRsFQXSZdGXNjNHEJCkdTHzFXH4vZEJ3q7MnHuP8\"]},\"node_modules/@openzeppelin/contracts/access/AccessControl.sol\":{\"keccak256\":\"0x1a6b4f6b7798ab80929d491b89d5427a9b3338c0fd1acd0ba325f69c6f1646af\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7bb7f346c12a14dc622bc105ce3c47202fbc89f4b153a28a63bb68193297330c\",\"dweb:/ipfs/QmagwF8P3bUBXwdo159ueEnY9dLSvEWwK24kk2op58egwG\"]},\"node_modules/@openzeppelin/contracts/access/IAccessControl.sol\":{\"keccak256\":\"0xbff9f59c84e5337689161ce7641c0ef8e872d6a7536fbc1f5133f128887aba3c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b308f882e796f7b79c9502deacb0a62983035c6f6f4e962b319ba6a1f4a77d3d\",\"dweb:/ipfs/QmaWCW7ahEQqFjwhSUhV7Ae7WhfNvzSpE7DQ58hvEooqPL\"]},\"node_modules/@openzeppelin/contracts/utils/Address.sol\":{\"keccak256\":\"0x6d0ae6e206645341fd122d278c2cb643dea260c190531f2f3f6a0426e77b00c0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://032d1201d839435be2c85b72e33206b3ea980c569d6ebf7fa57d811ab580a82f\",\"dweb:/ipfs/QmeqQjAtMvdZT2tG7zm39itcRJkuwu8AEReK6WRnLJ18DD\"]},\"node_modules/@openzeppelin/contracts/utils/Context.sol\":{\"keccak256\":\"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12\",\"dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF\"]},\"node_modules/@openzeppelin/contracts/utils/Errors.sol\":{\"keccak256\":\"0x6afa713bfd42cf0f7656efa91201007ac465e42049d7de1d50753a373648c123\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://ba1d02f4847670a1b83dec9f7d37f0b0418d6043447b69f3a29a5f9efc547fcf\",\"dweb:/ipfs/QmQ7iH2keLNUKgq2xSWcRmuBE5eZ3F5whYAkAGzCNNoEWB\"]},\"node_modules/@openzeppelin/contracts/utils/Multicall.sol\":{\"keccak256\":\"0xa4c645387f16eae227cd378108e8e0a8acbcf2b600e393629bbd8422241c495f\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5e272440acfbec7f51afdb60a42597837b267de83ba7bd08d7e4dbc40171567c\",\"dweb:/ipfs/QmPYzRwXpjUJEGRBWaSnzHz8gym1AKJ8yfoCgbo7gcwmEf\"]},\"node_modules/@openzeppelin/contracts/utils/TransientSlot.sol\":{\"keccak256\":\"0xac673fa1e374d9e6107504af363333e3e5f6344d2e83faf57d9bfd41d77cc946\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5982478dbbb218e9dd5a6e83f5c0e8d1654ddf20178484b43ef21dd2246809de\",\"dweb:/ipfs/QmaB1hS68n2kG8vTbt7EPEzmrGhkUbfiFyykGGLsAr9X22\"]},\"node_modules/@openzeppelin/contracts/utils/introspection/ERC165.sol\":{\"keccak256\":\"0x2d9dc2fe26180f74c11c13663647d38e259e45f95eb88f57b61d2160b0109d3e\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://81233d1f98060113d9922180bb0f14f8335856fe9f339134b09335e9f678c377\",\"dweb:/ipfs/QmWh6R35SarhAn4z2wH8SU456jJSYL2FgucfTFgbHJJN4E\"]},\"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617\",\"dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u\"]},\"node_modules/sp1-contracts/contracts/src/ISP1Verifier.sol\":{\"keccak256\":\"0x9e3ba64860bea920772dcf16be7946de2a2900d80bd51e9c0771184138f4f4d3\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0ec7230ca1fdd74edc6ab597d80bb345282aed3f0db4788ed96b4cc373ff46a3\",\"dweb:/ipfs/QmXPuSS5gzxMhFKWr1gsxBVu6WHh53ZZEvWkGgzrkM6Y7Q\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"bytes32","name":"updateClientProgramVkey","type":"bytes32"},{"internalType":"bytes32","name":"membershipProgramVkey","type":"bytes32"},{"internalType":"bytes32","name":"updateClientAndMembershipProgramVkey","type":"bytes32"},{"internalType":"bytes32","name":"misbehaviourProgramVkey","type":"bytes32"},{"internalType":"address","name":"sp1Verifier","type":"address"},{"internalType":"bytes","name":"_clientState","type":"bytes"},{"internalType":"bytes32","name":"_consensusState","type":"bytes32"},{"internalType":"address","name":"roleManager","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"type":"error","name":"AccessControlBadConfirmation"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"type":"error","name":"AccessControlUnauthorizedAccount"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"type":"error","name":"AddressEmptyCode"},{"inputs":[],"type":"error","name":"CannotHandleMisbehavior"},{"inputs":[{"internalType":"string","name":"expected","type":"string"},{"internalType":"string","name":"actual","type":"string"}],"type":"error","name":"ChainIdMismatch"},{"inputs":[{"internalType":"bytes","name":"expected","type":"bytes"},{"internalType":"bytes","name":"actual","type":"bytes"}],"type":"error","name":"ClientStateMismatch"},{"inputs":[{"internalType":"bytes32","name":"expected","type":"bytes32"},{"internalType":"bytes32","name":"actual","type":"bytes32"}],"type":"error","name":"ConsensusStateHashMismatch"},{"inputs":[],"type":"error","name":"ConsensusStateNotFound"},{"inputs":[{"internalType":"bytes32","name":"expected","type":"bytes32"},{"internalType":"bytes32","name":"actual","type":"bytes32"}],"type":"error","name":"ConsensusStateRootMismatch"},{"inputs":[],"type":"error","name":"EmptyValue"},{"inputs":[],"type":"error","name":"FailedCall"},{"inputs":[],"type":"error","name":"FeatureNotSupported"},{"inputs":[],"type":"error","name":"FrozenClientState"},{"inputs":[],"type":"error","name":"InvalidMembershipProof"},{"inputs":[{"internalType":"bytes[]","name":"path","type":"bytes[]"},{"internalType":"bytes","name":"value","type":"bytes"}],"type":"error","name":"KeyValuePairNotInCache"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"},{"internalType":"uint256","name":"min","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"type":"error","name":"LengthIsOutOfRange"},{"inputs":[{"internalType":"bytes[]","name":"path","type":"bytes[]"}],"type":"error","name":"MembershipProofKeyNotFound"},{"inputs":[{"internalType":"bytes","name":"expected","type":"bytes"},{"internalType":"bytes","name":"actual","type":"bytes"}],"type":"error","name":"MembershipProofValueMismatch"},{"inputs":[{"internalType":"uint64","name":"expectedRevisionNumber","type":"uint64"},{"internalType":"uint64","name":"expectedRevisionHeight","type":"uint64"},{"internalType":"uint64","name":"actualRevisionNumber","type":"uint64"},{"internalType":"uint64","name":"actualRevisionHeight","type":"uint64"}],"type":"error","name":"ProofHeightMismatch"},{"inputs":[{"internalType":"uint256","name":"now","type":"uint256"},{"internalType":"uint256","name":"proofTimestamp","type":"uint256"}],"type":"error","name":"ProofIsInTheFuture"},{"inputs":[{"internalType":"uint256","name":"now","type":"uint256"},{"internalType":"uint256","name":"proofTimestamp","type":"uint256"}],"type":"error","name":"ProofIsTooOld"},{"inputs":[{"internalType":"uint256","name":"expectedNumerator","type":"uint256"},{"internalType":"uint256","name":"expectedDenominator","type":"uint256"},{"internalType":"uint256","name":"actualNumerator","type":"uint256"},{"internalType":"uint256","name":"actualDenominator","type":"uint256"}],"type":"error","name":"TrustThresholdMismatch"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"type":"error","name":"TrustingPeriodMismatch"},{"inputs":[{"internalType":"uint256","name":"trustingPeriod","type":"uint256"},{"internalType":"uint256","name":"unbondingPeriod","type":"uint256"}],"type":"error","name":"TrustingPeriodTooLong"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"type":"error","name":"UnbondingPeriodMismatch"},{"inputs":[{"internalType":"uint8","name":"proofType","type":"uint8"}],"type":"error","name":"UnknownMembershipProofType"},{"inputs":[{"internalType":"uint8","name":"algorithm","type":"uint8"}],"type":"error","name":"UnknownZkAlgorithm"},{"inputs":[{"internalType":"bytes32","name":"expected","type":"bytes32"},{"internalType":"bytes32","name":"actual","type":"bytes32"}],"type":"error","name":"VerificationKeyMismatch"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"previousAdminRole","type":"bytes32","indexed":true},{"internalType":"bytes32","name":"newAdminRole","type":"bytes32","indexed":true}],"type":"event","name":"RoleAdminChanged","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleGranted","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32","indexed":true},{"internalType":"address","name":"account","type":"address","indexed":true},{"internalType":"address","name":"sender","type":"address","indexed":true}],"type":"event","name":"RoleRevoked","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"ALLOWED_SP1_CLOCK_DRIFT","outputs":[{"internalType":"uint16","name":"","type":"uint16"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"MEMBERSHIP_PROGRAM_VKEY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"MISBEHAVIOUR_PROGRAM_VKEY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"PROOF_SUBMITTER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"UPDATE_CLIENT_PROGRAM_VKEY","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"VERIFIER","outputs":[{"internalType":"contract ISP1Verifier","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"clientState","outputs":[{"internalType":"string","name":"chainId","type":"string"},{"internalType":"struct IICS07TendermintMsgs.TrustThreshold","name":"trustLevel","type":"tuple","components":[{"internalType":"uint8","name":"numerator","type":"uint8"},{"internalType":"uint8","name":"denominator","type":"uint8"}]},{"internalType":"struct IICS02ClientMsgs.Height","name":"latestHeight","type":"tuple","components":[{"internalType":"uint64","name":"revisionNumber","type":"uint64"},{"internalType":"uint64","name":"revisionHeight","type":"uint64"}]},{"internalType":"uint32","name":"trustingPeriod","type":"uint32"},{"internalType":"uint32","name":"unbondingPeriod","type":"uint32"},{"internalType":"bool","name":"isFrozen","type":"bool"},{"internalType":"enum IICS07TendermintMsgs.SupportedZkAlgorithm","name":"zkAlgorithm","type":"uint8"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"getClientState","outputs":[{"internalType":"bytes","name":"","type":"bytes"}]},{"inputs":[{"internalType":"uint64","name":"revisionHeight","type":"uint64"}],"stateMutability":"view","type":"function","name":"getConsensusStateHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"grantRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"view","type":"function","name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes","name":"misbehaviourMsg","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"misbehaviour"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function","name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}]},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"renounceRole"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"stateMutability":"nonpayable","type":"function","name":"revokeRole"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"stateMutability":"view","type":"function","name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}]},{"inputs":[{"internalType":"bytes","name":"updateMsg","type":"bytes"}],"stateMutability":"nonpayable","type":"function","name":"updateClient","outputs":[{"internalType":"enum ILightClientMsgs.UpdateResult","name":"","type":"uint8"}]},{"inputs":[{"internalType":"struct ILightClientMsgs.MsgVerifyMembership","name":"msg_","type":"tuple","components":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"struct IICS02ClientMsgs.Height","name":"proofHeight","type":"tuple","components":[{"internalType":"uint64","name":"revisionNumber","type":"uint64"},{"internalType":"uint64","name":"revisionHeight","type":"uint64"}]},{"internalType":"bytes[]","name":"path","type":"bytes[]"},{"internalType":"bytes","name":"value","type":"bytes"}]}],"stateMutability":"nonpayable","type":"function","name":"verifyMembership","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"struct ILightClientMsgs.MsgVerifyNonMembership","name":"msg_","type":"tuple","components":[{"internalType":"bytes","name":"proof","type":"bytes"},{"internalType":"struct IICS02ClientMsgs.Height","name":"proofHeight","type":"tuple","components":[{"internalType":"uint64","name":"revisionNumber","type":"uint64"},{"internalType":"uint64","name":"revisionHeight","type":"uint64"}]},{"internalType":"bytes[]","name":"path","type":"bytes[]"}]}],"stateMutability":"nonpayable","type":"function","name":"verifyNonMembership","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{"constructor":{"params":{"_clientState":"The encoded initial client state.","_consensusState":"The encoded initial consensus state.","membershipProgramVkey":"The verification key for the verify (non)membership program.","misbehaviourProgramVkey":"The verification key for the misbehaviour program.","roleManager":"Manages the proof submitters and can submit proofs. Should be the ICS26Router if used in IBC.","sp1Verifier":"The address of the SP1 verifier contract.","updateClientAndMembershipProgramVkey":"The verification key for the update client and membership program.","updateClientProgramVkey":"The verification key for the update client program."}},"getClientState()":{"returns":{"_0":"The client state."}},"getConsensusStateHash(uint64)":{"params":{"revisionHeight":"The revision height."},"returns":{"_0":"The consensus state at the given revision height."}},"getRoleAdmin(bytes32)":{"details":"Returns the admin role that controls `role`. See {grantRole} and {revokeRole}. To change a role's admin, use {_setRoleAdmin}."},"grantRole(bytes32,address)":{"details":"Grants `role` to `account`. If `account` had not been already granted `role`, emits a {RoleGranted} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleGranted} event."},"hasRole(bytes32,address)":{"details":"Returns `true` if `account` has been granted `role`."},"misbehaviour(bytes)":{"details":"The misbehavior is verfied in the sp1 program. Here we only check the public values which contain the trusted headers.","params":{"misbehaviourMsg":"The misbehaviour message"}},"multicall(bytes[])":{"custom:oz-upgrades-unsafe-allow-reachable":"delegatecall","details":"Receives and executes a batch of function calls on this contract."},"renounceRole(bytes32,address)":{"details":"Revokes `role` from the calling account. Roles are often managed via {grantRole} and {revokeRole}: this function's purpose is to provide a mechanism for accounts to lose their privileges if they are compromised (such as when a trusted device is misplaced). If the calling account had been revoked `role`, emits a {RoleRevoked} event. Requirements: - the caller must be `callerConfirmation`. May emit a {RoleRevoked} event."},"revokeRole(bytes32,address)":{"details":"Revokes `role` from `account`. If `account` had been granted `role`, emits a {RoleRevoked} event. Requirements: - the caller must have ``role``'s admin role. May emit a {RoleRevoked} event."},"supportsInterface(bytes4)":{"details":"Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] to learn more about how these ids are created. This function call must use less than 30 000 gas."},"updateClient(bytes)":{"details":"This function verifies the public values and forwards the proof to the SP1 verifier.","params":{"updateMsg":"The encoded update message e.g., an SP1 proof."},"returns":{"_0":"The result of the update operation"}},"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":{"details":"Notice that this message is not view, as it may update the client state for caching purposes.","params":{"msg_":"The membership message"},"returns":{"_0":"The unix timestamp of the verification height in the counterparty chain in seconds."}},"verifyNonMembership((bytes,(uint64,uint64),bytes[]))":{"details":"Notice that this message is not view, as it may update the client state for caching purposes.","params":{"msg_":"The membership message"},"returns":{"_0":"The unix timestamp of the verification height in the counterparty chain in seconds."}}},"version":1},"userdoc":{"kind":"user","methods":{"ALLOWED_SP1_CLOCK_DRIFT()":{"notice":"Constant allowed prover clock drift in seconds."},"MEMBERSHIP_PROGRAM_VKEY()":{"notice":"Immutable membership program verification key."},"MISBEHAVIOUR_PROGRAM_VKEY()":{"notice":"Immutable misbehaviour program verification key."},"PROOF_SUBMITTER_ROLE()":{"notice":"The role identifier for the proof submitter role"},"UPDATE_CLIENT_AND_MEMBERSHIP_PROGRAM_VKEY()":{"notice":"Immutable update client and membership program verification key."},"UPDATE_CLIENT_PROGRAM_VKEY()":{"notice":"Immutable update client program verification key."},"VERIFIER()":{"notice":"Immutable SP1 verifier contract address."},"clientState()":{"notice":"The ICS07Tendermint client state"},"constructor":{"notice":"The constructor sets the program verification key and the initial client and consensus states."},"getClientState()":{"notice":"Returns the client state."},"getConsensusStateHash(uint64)":{"notice":"Returns the consensus state keccak256 hash at the given revision height."},"misbehaviour(bytes)":{"notice":"Misbehaviour handling, moves the light client to the frozen state if misbehaviour is detected"},"updateClient(bytes)":{"notice":"Updating the client and consensus state"},"verifyMembership((bytes,(uint64,uint64),bytes[],bytes))":{"notice":"Querying the membership of a key-value pair"},"verifyNonMembership((bytes,(uint64,uint64),bytes[]))":{"notice":"Querying the non-membership of a key"}},"version":1}},"settings":{"remappings":["@openzeppelin-contracts/=node_modules/@openzeppelin/contracts/","@openzeppelin-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/","@openzeppelin/=node_modules/@openzeppelin/","@sp1-contracts/=node_modules/sp1-contracts/contracts/src/","@uniswap/permit2/=node_modules/@uniswap/permit2/","forge-std/=node_modules/forge-std/src/","sp1-contracts/=node_modules/sp1-contracts/"],"optimizer":{"enabled":true,"runs":10000},"metadata":{"bytecodeHash":"none"},"compilationTarget":{"contracts/light-clients/sp1-ics07/SP1ICS07Tendermint.sol":"SP1ICS07Tendermint"},"evmVersion":"cancun","libraries":{},"viaIR":true},"sources":{"contracts/interfaces/ILightClient.sol":{"keccak256":"0x1caae9e4f741a86c0056d5f7713a35f3bd96db7a9fd52fc9f1cf79aad76a442f","urls":["bzz-raw://4a536db3b926560f69547c17e4f451a2631ddea0f42bd224c7fd8382d74429f6","dweb:/ipfs/QmcQ33kxcmmowXgMbQGTBgiKBjF7v8UiP4y73bB9hhDizC"],"license":"MIT"},"contracts/light-clients/sp1-ics07/SP1ICS07Tendermint.sol":{"keccak256":"0x2e03f0f1e058e7184ea70dc2a423a3d32a609620b6a1e8b764ec9c287b0b6187","urls":["bzz-raw://9e3c0b2f3b3af9cfc8a3ab085e0decffcd40230a27530142293f0b79a4aefe51","dweb:/ipfs/Qmf3qEAMWdiGAMEXSae6mzZ7Sncsbr4QzDC4JzMeiNwgei"],"license":"MIT"},"contracts/light-clients/sp1-ics07/errors/ISP1ICS07TendermintErrors.sol":{"keccak256":"0xe1a0526f9913308f761af480ef7d87091d6038659b9058f89c50083d49768db1","urls":["bzz-raw://a31d6d4a059182b3dfd3e99595d82a134963c448c9152ef35b42c62ccca87635","dweb:/ipfs/QmfQ2Cfa4kxWem3f88o9UnFQgL8oRU6vKkDFWSGmLsVLqN"],"license":"MIT"},"contracts/light-clients/sp1-ics07/interfaces/ISP1ICS07Tendermint.sol":{"keccak256":"0x0f00aea96b194d1a9db14f708f0de1fe7723fedc7b72bb8ea182e768ffc0a731","urls":["bzz-raw://3111a76ebabf4b57917e93669892df895a54a93a4963e384d8c13a1c47877b44","dweb:/ipfs/QmPCZ7Z4dJpowkzMhizUAW1j8QdJLD3DDjMbYq8G5VeXc1"],"license":"MIT"},"contracts/light-clients/sp1-ics07/msgs/IICS07TendermintMsgs.sol":{"keccak256":"0xae0ae6ff2742192ebaaee297c7d7473a1e1b4dc73393d45a944b0b7a8a647703","urls":["bzz-raw://43b2f6492a1f727416bbcf55876d9c08ac1f66f4724efc75b935c5022555be20","dweb:/ipfs/QmfTaPFNSFuUEsEBgJjnF2Jr2knayS7YwatfjP7dgdcAyH"],"license":"UNLICENSED"},"contracts/light-clients/sp1-ics07/msgs/IMembershipMsgs.sol":{"keccak256":"0x9dff6833fddbc11caf27387c9f8cba5dca9d1e430c9643d4e4a2e8eb09924d96","urls":["bzz-raw://efa6cfc654510dfcef6f4286e3e66446082bcd5671662ae6e1c6fd0f893a5a4a","dweb:/ipfs/QmRWAg1kHvmRtXgDm4ajVC7tEWQnzTWmHeHMYQJfyFuDGq"],"license":"UNLICENSED"},"contracts/light-clients/sp1-ics07/msgs/IMisbehaviourMsgs.sol":{"keccak256":"0x165b37d890b06eccd47558bf1efd6495e18541627758736a1f5b9c33d0333832","urls":["bzz-raw://15ab15cd5439147abed697bc9644b0175754845608bf3d57c81c2c864d31ce46","dweb:/ipfs/QmYcV88YsM1sZCmhP8LKUFHq3hJ6bsvd66W5axBnsBBoUM"],"license":"UNLICENSED"},"contracts/light-clients/sp1-ics07/msgs/ISP1Msgs.sol":{"keccak256":"0x8ede29b02182df4be9c075b3b48083277e5dd93bb3e7ce1eecfea9728db49aed","urls":["bzz-raw://48edb4bbcd440454fffceb4f85b83c08c3973a212239d7da07c079634fe413dd","dweb:/ipfs/Qmf7ma1nXWghfZKrJJvLpPuY5aN2xZZjHY7kWP3KVFCoDJ"],"license":"MIT"},"contracts/light-clients/sp1-ics07/msgs/IUcAndMembershipMsgs.sol":{"keccak256":"0xef3619c79de37cabe95d9d8af9d145fc2ea2dd55137407fb4b0082b0491170d0","urls":["bzz-raw://93646d813cdacf5e38e609e54baa67c60d749f0f111d00cd18b003c3cb328204","dweb:/ipfs/QmVcuwPujbiZ644a4jRw2V1LZxhhGsm2HKMXweVqndwUnZ"],"license":"UNLICENSED"},"contracts/light-clients/sp1-ics07/msgs/IUpdateClientMsgs.sol":{"keccak256":"0x370cdfca1c65f1898c7da80507e66972a6fe51a5d5c4e7aeabdd7060ce875aed","urls":["bzz-raw://ff34288a142b6f40d6843818b9ec0a542db016bc0d4a7be582eb2e255655c43e","dweb:/ipfs/QmXwbJHDbqZdw3JTT48Le1ZWGqmKAG7omf2ePvDfBHomzj"],"license":"UNLICENSED"},"contracts/light-clients/sp1-ics07/utils/Paths.sol":{"keccak256":"0x0e4eec6cd6cd0ef684383946419a2bf53c8f800033acfc298db81688e5aa178b","urls":["bzz-raw://824b58c13ff96b6ee38b5d25435fcc1305c45735f52df6347dba3dacdc144ffa","dweb:/ipfs/QmT7qomKM46ChfVVwfBQHwjpoHW9vrG9nXkGXsPeZRLJ7w"],"license":"MIT"},"contracts/msgs/IICS02ClientMsgs.sol":{"keccak256":"0xb5fdb25319d5c32b3f527982d45ec1e9bec5215515581aee034d853006ea01a0","urls":["bzz-raw://6b8939bf552c62c952a491008bcd9b59ab7dda20a9482bf93438e05793881542","dweb:/ipfs/QmfWmzNCp8bd4pP26Lfw5HHp4dSZnCDqZDjVN7SD8P7eDh"],"license":"MIT"},"contracts/msgs/ILightClientMsgs.sol":{"keccak256":"0xa0565e7748b8b3284d72e1aa6ef4cc8264fcaa5f4e0761d22d64686953db6378","urls":["bzz-raw://c4476010d7090bfaa74127526f2051c6bc82ff42a09b936e848880a1f15a6065","dweb:/ipfs/QmY87JgRsFQXSZdGXNjNHEJCkdTHzFXH4vZEJ3q7MnHuP8"],"license":"MIT"},"node_modules/@openzeppelin/contracts/access/AccessControl.sol":{"keccak256":"0x1a6b4f6b7798ab80929d491b89d5427a9b3338c0fd1acd0ba325f69c6f1646af","urls":["bzz-raw://7bb7f346c12a14dc622bc105ce3c47202fbc89f4b153a28a63bb68193297330c","dweb:/ipfs/QmagwF8P3bUBXwdo159ueEnY9dLSvEWwK24kk2op58egwG"],"license":"MIT"},"node_modules/@openzeppelin/contracts/access/IAccessControl.sol":{"keccak256":"0xbff9f59c84e5337689161ce7641c0ef8e872d6a7536fbc1f5133f128887aba3c","urls":["bzz-raw://b308f882e796f7b79c9502deacb0a62983035c6f6f4e962b319ba6a1f4a77d3d","dweb:/ipfs/QmaWCW7ahEQqFjwhSUhV7Ae7WhfNvzSpE7DQ58hvEooqPL"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/Address.sol":{"keccak256":"0x6d0ae6e206645341fd122d278c2cb643dea260c190531f2f3f6a0426e77b00c0","urls":["bzz-raw://032d1201d839435be2c85b72e33206b3ea980c569d6ebf7fa57d811ab580a82f","dweb:/ipfs/QmeqQjAtMvdZT2tG7zm39itcRJkuwu8AEReK6WRnLJ18DD"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/Context.sol":{"keccak256":"0x493033a8d1b176a037b2cc6a04dad01a5c157722049bbecf632ca876224dd4b2","urls":["bzz-raw://6a708e8a5bdb1011c2c381c9a5cfd8a9a956d7d0a9dc1bd8bcdaf52f76ef2f12","dweb:/ipfs/Qmax9WHBnVsZP46ZxEMNRQpLQnrdE4dK8LehML1Py8FowF"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/Errors.sol":{"keccak256":"0x6afa713bfd42cf0f7656efa91201007ac465e42049d7de1d50753a373648c123","urls":["bzz-raw://ba1d02f4847670a1b83dec9f7d37f0b0418d6043447b69f3a29a5f9efc547fcf","dweb:/ipfs/QmQ7iH2keLNUKgq2xSWcRmuBE5eZ3F5whYAkAGzCNNoEWB"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/Multicall.sol":{"keccak256":"0xa4c645387f16eae227cd378108e8e0a8acbcf2b600e393629bbd8422241c495f","urls":["bzz-raw://5e272440acfbec7f51afdb60a42597837b267de83ba7bd08d7e4dbc40171567c","dweb:/ipfs/QmPYzRwXpjUJEGRBWaSnzHz8gym1AKJ8yfoCgbo7gcwmEf"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/TransientSlot.sol":{"keccak256":"0xac673fa1e374d9e6107504af363333e3e5f6344d2e83faf57d9bfd41d77cc946","urls":["bzz-raw://5982478dbbb218e9dd5a6e83f5c0e8d1654ddf20178484b43ef21dd2246809de","dweb:/ipfs/QmaB1hS68n2kG8vTbt7EPEzmrGhkUbfiFyykGGLsAr9X22"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/introspection/ERC165.sol":{"keccak256":"0x2d9dc2fe26180f74c11c13663647d38e259e45f95eb88f57b61d2160b0109d3e","urls":["bzz-raw://81233d1f98060113d9922180bb0f14f8335856fe9f339134b09335e9f678c377","dweb:/ipfs/QmWh6R35SarhAn4z2wH8SU456jJSYL2FgucfTFgbHJJN4E"],"license":"MIT"},"node_modules/@openzeppelin/contracts/utils/introspection/IERC165.sol":{"keccak256":"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c","urls":["bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617","dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u"],"license":"MIT"},"node_modules/sp1-contracts/contracts/src/ISP1Verifier.sol":{"keccak256":"0x9e3ba64860bea920772dcf16be7946de2a2900d80bd51e9c0771184138f4f4d3","urls":["bzz-raw://0ec7230ca1fdd74edc6ab597d80bb345282aed3f0db4788ed96b4cc373ff46a3","dweb:/ipfs/QmXPuSS5gzxMhFKWr1gsxBVu6WHh53ZZEvWkGgzrkM6Y7Q"],"license":"MIT"}},"version":1},"id":38} \ No newline at end of file diff --git a/contracts/utils/SolanaIFTSendCallConstructor.sol b/contracts/utils/SolanaIFTSendCallConstructor.sol new file mode 100644 index 000000000..d9665f103 --- /dev/null +++ b/contracts/utils/SolanaIFTSendCallConstructor.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +import { IIFTSendCallConstructor } from "../interfaces/IIFTSendCallConstructor.sol"; + +import { Strings } from "@openzeppelin-contracts/utils/Strings.sol"; +import { ERC165 } from "@openzeppelin-contracts/utils/introspection/ERC165.sol"; +import { IERC165 } from "@openzeppelin-contracts/utils/introspection/IERC165.sol"; + +/// @title Solana IFT Send Call Constructor +/// @notice Constructs ICS27-GMP call data for minting IFT tokens on Solana counterparty chains +contract SolanaIFTSendCallConstructor is IIFTSendCallConstructor, ERC165 { + /// @notice Expected length of a 0x-prefixed hex-encoded Solana public key (2 + 64) + uint256 private constant SOLANA_PUBKEY_HEX_LENGTH = 66; + + /// @notice Error thrown when the receiver address is invalid + /// @param receiver The invalid receiver string + error SolanaIFTInvalidReceiver(string receiver); + + /// @inheritdoc IIFTSendCallConstructor + function constructMintCall(string calldata receiver, uint256 amount) external pure returns (bytes memory) { + require(bytes(receiver).length == SOLANA_PUBKEY_HEX_LENGTH, SolanaIFTInvalidReceiver(receiver)); + (bool success, uint256 parsed) = Strings.tryParseHexUint(receiver); + require(success, SolanaIFTInvalidReceiver(receiver)); + + return abi.encode(bytes32(parsed), amount); + } + + /// @inheritdoc ERC165 + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return interfaceId == type(IIFTSendCallConstructor).interfaceId || super.supportsInterface(interfaceId); + } +} diff --git a/e2e/interchaintestv8/ethereum/utils.go b/e2e/interchaintestv8/ethereum/utils.go index 03fad2fe4..47022fe6d 100644 --- a/e2e/interchaintestv8/ethereum/utils.go +++ b/e2e/interchaintestv8/ethereum/utils.go @@ -37,6 +37,7 @@ type DeployedContracts struct { Ift string `json:"ift,omitempty"` // CosmosIFTSendCallConstructor (optional, deployed when IFT_ICA_ADDRESS is set) CosmosIftConstructor string `json:"cosmosIftConstructor,omitempty"` + SolanaIftConstructor string `json:"solanaIftConstructor,omitempty"` } // GetVerifierAddress returns the appropriate verifier address based on prover type and proof type. diff --git a/e2e/interchaintestv8/ethereum_solana_ift_test.go b/e2e/interchaintestv8/ethereum_solana_ift_test.go new file mode 100644 index 000000000..d7cfd27e1 --- /dev/null +++ b/e2e/interchaintestv8/ethereum_solana_ift_test.go @@ -0,0 +1,1476 @@ +package main + +import ( + "context" + "crypto/ecdsa" + "encoding/binary" + "encoding/hex" + "fmt" + "math/big" + "os" + "strconv" + "strings" + "testing" + "time" + + bin "github.com/gagliardetto/binary" + "github.com/stretchr/testify/suite" + "google.golang.org/protobuf/proto" + + ethcommon "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + + solanago "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/programs/token" + "github.com/gagliardetto/solana-go/rpc" + + "github.com/cosmos/solidity-ibc-eureka/packages/go-abigen/ics26router" + + access_manager "github.com/cosmos/solidity-ibc-eureka/packages/go-anchor/accessmanager" + attestation "github.com/cosmos/solidity-ibc-eureka/packages/go-anchor/attestation" + ics26_router "github.com/cosmos/solidity-ibc-eureka/packages/go-anchor/ics26router" + ics27_gmp "github.com/cosmos/solidity-ibc-eureka/packages/go-anchor/ics27gmp" + ift "github.com/cosmos/solidity-ibc-eureka/packages/go-anchor/ift" + + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/attestor" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/e2esuite" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/ethereum" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/relayer" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/solana" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/testvalues" + "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/types/evmift" + relayertypes "github.com/srdtrk/solidity-ibc-eureka/e2e/v8/types/relayer" +) + +const ( + EthSolanaIFTTokenDecimals = uint8(6) + EthSolanaIFTMintAmount = uint64(10_000_000) // 10 tokens with 6 decimals + EthSolanaIFTTransferAmount = uint64(1_000_000) // 1 token with 6 decimals + + EthClientIDOnSolana = testvalues.FirstAttestationsClientID // "attestations-0" + SolanaClientIDOnEth = testvalues.CustomClientID // "cosmoshub-1" + + numEthAttestors = 1 + numSolAttestors = 1 + ethSolGMPPortID = testvalues.SolanaGMPPortID + ethSolComputeUnits = uint32(400_000) + + ethAttestorKeystorePathTemplate = "/tmp/ethsol_eth_attestor_%d" + solanaAttestorKeystorePathTemplate = "/tmp/ethsol_sol_attestor_%d" +) + +type EthereumSolanaIFTTestSuite struct { + e2esuite.TestSuite + + SolanaRelayer *solanago.Wallet + + ethDeployer *ecdsa.PrivateKey + ethUser *ecdsa.PrivateKey + + contractAddresses ethereum.DeployedContracts + + RelayerClient relayertypes.RelayerServiceClient + RelayerProcess *os.Process + + SolanaAltAddress string + + ethAttestorAddresses []string + ethAttestorResult attestor.SetupResult + solanaAttestorResult attestor.SetupResult + + IFTMintWallet *solanago.Wallet + IFTAppState solanago.PublicKey + IFTAppMintState solanago.PublicKey + IFTMintAuthority solanago.PublicKey + IFTBridge solanago.PublicKey + SenderTokenAccount solanago.PublicKey + + GMPAppStatePDA solanago.PublicKey + RouterStatePDA solanago.PublicKey + IBCClientPDA solanago.PublicKey + GMPIBCAppPDA solanago.PublicKey + ClientSequencePDA solanago.PublicKey +} + +func (s *EthereumSolanaIFTTestSuite) IFTMint() solanago.PublicKey { + return s.IFTMintWallet.PublicKey() +} + +func (s *EthereumSolanaIFTTestSuite) IFTMintBytes() []byte { + pk := s.IFTMintWallet.PublicKey() + return pk[:] +} + +func TestWithEthereumSolanaIFTTestSuite(t *testing.T) { + suite.Run(t, new(EthereumSolanaIFTTestSuite)) +} + +func (s *EthereumSolanaIFTTestSuite) TearDownSuite() { + ctx := context.Background() + attestor.CleanupContainers(ctx, s.T(), s.ethAttestorResult.Containers) + attestor.CleanupContainers(ctx, s.T(), s.solanaAttestorResult.Containers) + + if s.RelayerProcess != nil { + s.T().Logf("Cleaning up relayer process (PID: %d)", s.RelayerProcess.Pid) + if err := s.RelayerProcess.Kill(); err != nil { + s.T().Logf("Failed to kill relayer process: %v", err) + } + } +} + +func (s *EthereumSolanaIFTTestSuite) SetupSuite(ctx context.Context) { + var err error + + err = os.Chdir("../..") + s.Require().NoError(err) + + os.Setenv(testvalues.EnvKeyEthTestnetType, testvalues.EthTestnetTypeAnvil) + os.Setenv(testvalues.EnvKeySolanaTestnetType, testvalues.SolanaTestnetType_Localnet) + s.TestSuite.SetupSuite(ctx) + + s.T().Log("Waiting for Solana cluster to be ready...") + err = s.Solana.Chain.WaitForClusterReady(ctx, 30*time.Second) + s.Require().NoError(err, "Solana cluster failed to initialize") + + eth := s.Eth.Chains[0] + + s.Require().True(s.Run("Set up environment", func() { + s.ethUser, err = eth.CreateAndFundUser() + s.Require().NoError(err) + + s.ethDeployer, err = eth.CreateAndFundUserFromKey(testvalues.E2EDeployerPrivateKeyHex) + s.Require().NoError(err) + + operatorKey, err := eth.CreateAndFundUser() + s.Require().NoError(err) + + prover := os.Getenv(testvalues.EnvKeySp1Prover) + if prover == "" { + prover = testvalues.EnvValueSp1Prover_Mock + } + os.Setenv(testvalues.EnvKeySp1Prover, prover) + os.Setenv(testvalues.EnvKeyVerifier, testvalues.EnvValueVerifier_Mock) + + if os.Getenv(testvalues.EnvKeyRustLog) == "" { + os.Setenv(testvalues.EnvKeyRustLog, testvalues.EnvValueRustLog_Info) + } + os.Setenv(testvalues.EnvKeyEthRPC, eth.RPC) + os.Setenv(testvalues.EnvKeyOperatorPrivateKey, hex.EncodeToString(crypto.FromECDSA(operatorKey))) + })) + + s.Require().True(s.Run("Deploy Solana programs", func() { + solanaUser := solanago.NewWallet() + s.T().Logf("Created SolanaRelayer wallet: %s", solanaUser.PublicKey()) + + s.Require().True(s.Run("Fund wallets", func() { + const deployerFunding = 100 * testvalues.InitialSolBalance + err := e2esuite.RunParallelTasks( + e2esuite.ParallelTask{ + Name: "Fund SolanaRelayer", + Run: func() error { + _, err := s.Solana.Chain.FundUserWithRetry(ctx, solanaUser.PublicKey(), testvalues.InitialSolBalance, 5) + return err + }, + }, + e2esuite.ParallelTask{ + Name: "Fund Deployer", + Run: func() error { + _, err := s.Solana.Chain.FundUserWithRetry(ctx, solana.DeployerPubkey, deployerFunding, 5) + return err + }, + }, + ) + s.Require().NoError(err) + s.SolanaRelayer = solanaUser + })) + + s.Require().True(s.Run("Deploy programs", func() { + const keypairDir = "solana-keypairs/localnet" + const deployerPath = keypairDir + "/deployer_wallet.json" + + deployProgram := func(displayName, programName string) e2esuite.ParallelTaskWithResult[solanago.PublicKey] { + return e2esuite.ParallelTaskWithResult[solanago.PublicKey]{ + Name: displayName, + Run: func() (solanago.PublicKey, error) { + s.T().Logf("Deploying %s...", displayName) + keypairPath := fmt.Sprintf("%s/%s-keypair.json", keypairDir, programName) + programID, err := s.Solana.Chain.DeploySolanaProgramAsync(ctx, programName, keypairPath, deployerPath) + if err == nil { + s.T().Logf("Deployed %s at: %s", displayName, programID) + } + return programID, err + }, + } + } + + deployResults, err := e2esuite.RunParallelTasksWithResults( + deployProgram("Access Manager", "access_manager"), + deployProgram("ICS26 Router", "ics26_router"), + deployProgram("ICS27 GMP", "ics27_gmp"), + deployProgram("IFT", "ift"), + deployProgram("Attestation", "attestation"), + ) + s.Require().NoError(err) + + access_manager.ProgramID = deployResults["Access Manager"] + ics26_router.ProgramID = deployResults["ICS26 Router"] + ics27_gmp.ProgramID = deployResults["ICS27 GMP"] + ift.ProgramID = deployResults["IFT"] + attestation.ProgramID = deployResults["Attestation"] + })) + })) + + s.Require().True(s.Run("Initialize Access Control", func() { + accessControlAccount, _ := solana.AccessManager.AccessManagerPDA(access_manager.ProgramID) + initInstruction, err := access_manager.NewInitializeInstruction( + s.SolanaRelayer.PublicKey(), + accessControlAccount, + s.SolanaRelayer.PublicKey(), + solanago.SystemProgramID, + solanago.SysVarInstructionsPubkey, + ) + s.Require().NoError(err) + + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), initInstruction) + s.Require().NoError(err) + _, err = s.Solana.Chain.SignAndBroadcastTxWithRetryAndTimeout(ctx, tx, rpc.CommitmentConfirmed, 30, s.SolanaRelayer) + s.Require().NoError(err) + })) + + s.Require().True(s.Run("Grant roles", func() { + accessControlAccount, _ := solana.AccessManager.AccessManagerPDA(access_manager.ProgramID) + const RELAYER_ROLE = uint64(1) + const ID_CUSTOMIZER_ROLE = uint64(6) + + grantRelayerRoleIx, err := access_manager.NewGrantRoleInstruction( + RELAYER_ROLE, s.SolanaRelayer.PublicKey(), accessControlAccount, + s.SolanaRelayer.PublicKey(), solanago.SysVarInstructionsPubkey, + ) + s.Require().NoError(err) + + grantIdCustomizerRoleIx, err := access_manager.NewGrantRoleInstruction( + ID_CUSTOMIZER_ROLE, s.SolanaRelayer.PublicKey(), accessControlAccount, + s.SolanaRelayer.PublicKey(), solanago.SysVarInstructionsPubkey, + ) + s.Require().NoError(err) + + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), grantRelayerRoleIx, grantIdCustomizerRoleIx) + s.Require().NoError(err) + _, err = s.Solana.Chain.SignAndBroadcastTxWithRetryAndTimeout(ctx, tx, rpc.CommitmentConfirmed, 30, s.SolanaRelayer) + s.Require().NoError(err) + })) + + s.Require().True(s.Run("Initialize ICS26 Router", func() { + routerStateAccount, _ := solana.Ics26Router.RouterStatePDA(ics26_router.ProgramID) + initInstruction, err := ics26_router.NewInitializeInstruction( + access_manager.ProgramID, routerStateAccount, + s.SolanaRelayer.PublicKey(), solanago.SystemProgramID, + ) + s.Require().NoError(err) + + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), initInstruction) + s.Require().NoError(err) + _, err = s.Solana.Chain.SignAndBroadcastTxWithRetryAndTimeout(ctx, tx, rpc.CommitmentConfirmed, 30, s.SolanaRelayer) + s.Require().NoError(err) + })) + + s.Require().True(s.Run("Initialize ICS27 GMP", func() { + gmpAppStatePDA, _ := solana.Ics27Gmp.AppStatePDA(ics27_gmp.ProgramID) + initInstruction, err := ics27_gmp.NewInitializeInstruction( + access_manager.ProgramID, gmpAppStatePDA, + s.SolanaRelayer.PublicKey(), solanago.SystemProgramID, + ) + s.Require().NoError(err) + + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), initInstruction) + s.Require().NoError(err) + _, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaRelayer) + s.Require().NoError(err) + })) + + s.Require().True(s.Run("Register ICS27 GMP with Router", func() { + routerStateAccount, _ := solana.Ics26Router.RouterStatePDA(ics26_router.ProgramID) + accessControlAccount, _ := solana.AccessManager.AccessManagerPDA(access_manager.ProgramID) + ibcAppAccount, _ := solana.Ics26Router.IbcAppWithArgSeedPDA(ics26_router.ProgramID, []byte(ethSolGMPPortID)) + + registerInstruction, err := ics26_router.NewAddIbcAppInstruction( + ethSolGMPPortID, + routerStateAccount, accessControlAccount, ibcAppAccount, + ics27_gmp.ProgramID, s.SolanaRelayer.PublicKey(), s.SolanaRelayer.PublicKey(), + solanago.SystemProgramID, solanago.SysVarInstructionsPubkey, + ) + s.Require().NoError(err) + + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), registerInstruction) + s.Require().NoError(err) + _, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaRelayer) + s.Require().NoError(err) + })) + + s.GMPAppStatePDA, _ = solana.Ics27Gmp.AppStatePDA(ics27_gmp.ProgramID) + s.RouterStatePDA, _ = solana.Ics26Router.RouterStatePDA(ics26_router.ProgramID) + s.IBCClientPDA, _ = solana.Ics26Router.ClientWithArgSeedPDA(ics26_router.ProgramID, []byte(EthClientIDOnSolana)) + s.GMPIBCAppPDA, _ = solana.Ics26Router.IbcAppWithArgSeedPDA(ics26_router.ProgramID, []byte(ethSolGMPPortID)) + s.ClientSequencePDA, _ = solana.Ics26Router.ClientSequenceWithArgSeedPDA(ics26_router.ProgramID, []byte(EthClientIDOnSolana)) + + s.Require().True(s.Run("Generate Eth attestor keys", func() { + var err error + s.ethAttestorAddresses, err = attestor.GenerateAttestorKeys(ctx, attestor.GenerateAttestorKeysParams{ + Client: s.GetDockerClient(), + NumKeys: numEthAttestors, + KeystorePathTemplate: ethAttestorKeystorePathTemplate, + }) + s.Require().NoError(err) + s.T().Logf("Generated %d Eth attestor keys: %v", len(s.ethAttestorAddresses), s.ethAttestorAddresses) + })) + + s.Require().True(s.Run("Deploy EVM contracts", func() { + stdout, err := eth.ForgeScript(s.ethDeployer, testvalues.E2EDeployScriptPath) + s.Require().NoError(err) + + s.contractAddresses, err = ethereum.GetEthContractsFromDeployOutput(string(stdout)) + s.Require().NoError(err) + s.T().Logf("ICS26Router: %s, IFT: %s", s.contractAddresses.Ics26Router, s.contractAddresses.Ift) + })) + + s.Require().True(s.Run("Verify SolanaIFTSendCallConstructor", func() { + s.Require().NotEmpty(s.contractAddresses.SolanaIftConstructor, "SolanaIFTSendCallConstructor should be deployed by the deploy script") + s.T().Logf("SolanaIFTSendCallConstructor at: %s", s.contractAddresses.SolanaIftConstructor) + })) + + // NOTE: SetupAttestors registers t.Cleanup to stop containers. Must be called outside + // s.Run() subtests so cleanup runs at end of test, not when subtest finishes. + s.T().Log("Starting Eth attestors...") + s.ethAttestorResult = attestor.SetupAttestors(ctx, s.T(), attestor.SetupParams{ + NumAttestors: numEthAttestors, + KeystorePathTemplate: ethAttestorKeystorePathTemplate, + ChainType: attestor.ChainTypeEvm, + AdapterURL: eth.DockerRPC, + RouterAddress: s.contractAddresses.Ics26Router, + DockerClient: s.GetDockerClient(), + NetworkID: s.GetNetworkID(), + }) + for i, endpoint := range s.ethAttestorResult.Endpoints { + err := attestor.CheckAttestorHealth(ctx, endpoint) + s.Require().NoError(err, "Eth attestor %d at %s is not healthy", i, endpoint) + } + + s.T().Log("Starting Solana attestors...") + s.solanaAttestorResult = attestor.SetupAttestors(ctx, s.T(), attestor.SetupParams{ + NumAttestors: numSolAttestors, + KeystorePathTemplate: solanaAttestorKeystorePathTemplate, + ChainType: attestor.ChainTypeSolana, + AdapterURL: attestor.TransformLocalhostToDockerHost(testvalues.SolanaLocalnetRPC), + RouterAddress: ics26_router.ProgramID.String(), + DockerClient: s.GetDockerClient(), + NetworkID: s.GetNetworkID(), + EnableHostAccess: true, + }) + + s.Require().True(s.Run("Create Address Lookup Table", func() { + altAddress := s.Solana.Chain.CreateIBCAddressLookupTableWithAttestation( + ctx, s.T(), s.Require(), s.SolanaRelayer, + eth.ChainID.String(), ethSolGMPPortID, EthClientIDOnSolana, EthClientIDOnSolana, + ) + s.SolanaAltAddress = altAddress.String() + s.T().Logf("Created ALT: %s", s.SolanaAltAddress) + })) + + s.Require().True(s.Run("Initialize Attestation Light Client on Solana", func() { + s.initializeAttestationLightClientOnSolana(ctx, EthClientIDOnSolana) + })) + + s.Require().True(s.Run("Start Relayer", func() { + config := relayer.NewConfigBuilder(). + EthToSolanaAttested(relayer.EthToSolanaAttestedParams{ + EthChainID: eth.ChainID.String(), + SolanaChainID: testvalues.SolanaChainID, + EthRPC: eth.RPC, + ICS26Address: s.contractAddresses.Ics26Router, + SolanaRPC: testvalues.SolanaLocalnetRPC, + ICS26ProgramID: ics26_router.ProgramID.String(), + FeePayer: s.SolanaRelayer.PublicKey().String(), + ALTAddress: s.SolanaAltAddress, + AttestorEndpoints: s.ethAttestorResult.Endpoints, + AttestorTimeout: 30000, + QuorumThreshold: numEthAttestors, + }). + SolanaToEthAttested(relayer.SolanaToEthAttestedParams{ + SolanaChainID: testvalues.SolanaChainID, + EthChainID: eth.ChainID.String(), + SolanaRPC: testvalues.SolanaLocalnetRPC, + ICS26ProgramID: ics26_router.ProgramID.String(), + EthRPC: eth.RPC, + ICS26Address: s.contractAddresses.Ics26Router, + AttestorEndpoints: s.solanaAttestorResult.Endpoints, + AttestorTimeout: 30000, + QuorumThreshold: numSolAttestors, + }). + Build() + + err := config.GenerateConfigFile(testvalues.RelayerConfigFilePath) + s.Require().NoError(err) + + s.RelayerProcess, err = relayer.StartRelayer(testvalues.RelayerConfigFilePath) + s.Require().NoError(err) + + s.T().Cleanup(func() { + os.Remove(testvalues.RelayerConfigFilePath) + }) + })) + + s.Require().True(s.Run("Create Relayer Client", func() { + s.RelayerClient, err = relayer.GetGRPCClient(relayer.DefaultRelayerGRPCAddress()) + s.Require().NoError(err) + })) + + s.Require().True(s.Run("Create Solana light client on Ethereum", func() { + currentFinalizedSlot, err := s.Solana.Chain.RPCClient.GetSlot(ctx, rpc.CommitmentFinalized) + s.Require().NoError(err) + solanaTimestamp, err := s.Solana.Chain.RPCClient.GetBlockTime(ctx, currentFinalizedSlot) + s.Require().NoError(err) + + // Convert attestor addresses to EIP-55 checksummed format (required by eth_attested.rs) + checksummedAddrs := make([]string, len(s.solanaAttestorResult.Addresses)) + for i, addr := range s.solanaAttestorResult.Addresses { + checksummedAddrs[i] = ethcommon.HexToAddress(addr).Hex() + } + + resp, err := s.RelayerClient.CreateClient(ctx, &relayertypes.CreateClientRequest{ + SrcChain: testvalues.SolanaChainID, + DstChain: eth.ChainID.String(), + Parameters: map[string]string{ + testvalues.ParameterKey_AttestorAddresses: strings.Join(checksummedAddrs, ","), + testvalues.ParameterKey_MinRequiredSigs: strconv.Itoa(numSolAttestors), + testvalues.ParameterKey_height: strconv.FormatUint(currentFinalizedSlot, 10), + testvalues.ParameterKey_timestamp: strconv.FormatInt(int64(*solanaTimestamp), 10), + }, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Tx) + + // BroadcastTx with nil address = contract deployment + ethRelayerSubmitter, err := eth.CreateAndFundUser() + s.Require().NoError(err) + receipt, err := eth.BroadcastTx(ctx, ethRelayerSubmitter, 15_000_000, nil, resp.Tx) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + sp1Ics07Address := receipt.ContractAddress + s.T().Logf("Solana light client deployed on Ethereum at: %s", sp1Ics07Address.Hex()) + + ics26Contract, err := ics26router.NewContract(ethcommon.HexToAddress(s.contractAddresses.Ics26Router), eth.RPCClient) + s.Require().NoError(err) + + counterpartyInfo := ics26router.IICS02ClientMsgsCounterpartyInfo{ + ClientId: EthClientIDOnSolana, + MerklePrefix: [][]byte{[]byte("")}, + } + + txOpts, err := eth.GetTransactOpts(s.ethDeployer) + s.Require().NoError(err) + + tx, err := ics26Contract.AddClient(txOpts, SolanaClientIDOnEth, counterpartyInfo, sp1Ics07Address) + s.Require().NoError(err) + + addClientReceipt, err := eth.GetTxReciept(ctx, tx.Hash()) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, addClientReceipt.Status) + })) + + s.Require().True(s.Run("Add attestation client to Router on Solana", func() { + routerStateAccount, _ := solana.Ics26Router.RouterStatePDA(ics26_router.ProgramID) + accessControlAccount, _ := solana.AccessManager.AccessManagerPDA(access_manager.ProgramID) + clientAccount, _ := solana.Ics26Router.ClientWithArgSeedPDA(ics26_router.ProgramID, []byte(EthClientIDOnSolana)) + clientSequenceAccount, _ := solana.Ics26Router.ClientSequenceWithArgSeedPDA(ics26_router.ProgramID, []byte(EthClientIDOnSolana)) + + counterpartyInfo := ics26_router.SolanaIbcTypesRouterCounterpartyInfo{ + ClientId: SolanaClientIDOnEth, + MerklePrefix: [][]byte{[]byte("")}, + } + + addClientInstruction, err := ics26_router.NewAddClientInstruction( + EthClientIDOnSolana, counterpartyInfo, + s.SolanaRelayer.PublicKey(), routerStateAccount, accessControlAccount, + clientAccount, clientSequenceAccount, + attestation.ProgramID, solanago.SystemProgramID, solanago.SysVarInstructionsPubkey, + ) + s.Require().NoError(err) + + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), addClientInstruction) + s.Require().NoError(err) + _, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaRelayer) + s.Require().NoError(err) + })) +} + +func (s *EthereumSolanaIFTTestSuite) initializeAttestationLightClientOnSolana(ctx context.Context, clientID string) { + var attestorAddresses [][20]uint8 + for _, addr := range s.ethAttestorAddresses { + addrHex := addr + if len(addrHex) >= 2 && addrHex[:2] == "0x" { + addrHex = addrHex[2:] + } + + addrBytes, err := hex.DecodeString(addrHex) + s.Require().NoError(err) + s.Require().Len(addrBytes, 20, "Attestor address must be 20 bytes") + + var addrArray [20]uint8 + copy(addrArray[:], addrBytes) + attestorAddresses = append(attestorAddresses, addrArray) + } + + minRequiredSigs := uint8(numEthAttestors) + + clientStatePDA, _ := solana.Attestation.ClientPDA(attestation.ProgramID) + appStatePDA, _ := solana.Attestation.AppStatePDA(attestation.ProgramID) + + initInstruction, err := attestation.NewInitializeInstruction( + attestorAddresses, minRequiredSigs, + access_manager.ProgramID, + clientStatePDA, appStatePDA, + s.SolanaRelayer.PublicKey(), solanago.SystemProgramID, + ) + s.Require().NoError(err) + + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), initInstruction) + s.Require().NoError(err) + + sig, err := s.Solana.Chain.SignAndBroadcastTxWithRetryAndTimeout(ctx, tx, rpc.CommitmentConfirmed, 30, s.SolanaRelayer) + s.Require().NoError(err) + s.T().Logf("Attestation Light Client initialized on Solana - tx: %s", sig) +} + +func (s *EthereumSolanaIFTTestSuite) createIFTSplToken(ctx context.Context, mintWallet *solanago.Wallet) { + mint := mintWallet.PublicKey() + appStatePDA, _ := solana.Ift.IftAppStatePDA(ift.ProgramID) + appMintStatePDA, _ := solana.Ift.IftAppMintStatePDA(ift.ProgramID, mint[:]) + mintAuthorityPDA, _ := solana.Ift.IftMintAuthorityPDA(ift.ProgramID, mint[:]) + + s.IFTAppState = appStatePDA + s.IFTAppMintState = appMintStatePDA + s.IFTMintAuthority = mintAuthorityPDA + + // Initialize global app state (idempotent - will fail silently if already initialized) + globalInitIx, err := ift.NewInitializeInstruction( + s.SolanaRelayer.PublicKey(), // admin + appStatePDA, + s.SolanaRelayer.PublicKey(), + solanago.SystemProgramID, + ) + s.Require().NoError(err) + + globalInitTx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), globalInitIx) + s.Require().NoError(err) + // Ignore error - may already be initialized + _, _ = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, globalInitTx, rpc.CommitmentConfirmed, s.SolanaRelayer) + + createTokenParams := &ift.IftStateCreateTokenParams_SplToken{ + Decimals: EthSolanaIFTTokenDecimals, + } + + createTokenIx, err := ift.NewCreateAndInitializeSplTokenInstruction( + createTokenParams, + appStatePDA, + appMintStatePDA, + mint, + mintAuthorityPDA, + s.SolanaRelayer.PublicKey(), + s.SolanaRelayer.PublicKey(), + token.ProgramID, + solanago.SystemProgramID, + solanago.SysVarInstructionsPubkey, + ) + s.Require().NoError(err) + + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), createTokenIx) + s.Require().NoError(err) + + _, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaRelayer, mintWallet) + s.Require().NoError(err) +} + +func (s *EthereumSolanaIFTTestSuite) registerSolanaIFTBridgeForEVM(ctx context.Context, clientID string, counterpartyIFTAddress string) { + bridgePDA, _ := solana.Ift.IftBridgePDA(ift.ProgramID, s.IFTMintBytes(), []byte(clientID)) + s.IFTBridge = bridgePDA + + // EVM counterparty uses the unit variant (no fields) + evmOpt := ift.IftStateChainOptions_Evm(0) + registerMsg := ift.IftStateRegisterIftBridgeMsg{ + ClientId: clientID, + CounterpartyIftAddress: counterpartyIFTAddress, + ChainOptions: &evmOpt, + } + + registerIx, err := ift.NewRegisterIftBridgeInstruction( + registerMsg, s.IFTAppState, s.IFTAppMintState, bridgePDA, + s.SolanaRelayer.PublicKey(), // admin + s.SolanaRelayer.PublicKey(), // payer + solanago.SystemProgramID, + solanago.SysVarInstructionsPubkey, + ) + s.Require().NoError(err) + + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), registerIx) + s.Require().NoError(err) + + _, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaRelayer) + s.Require().NoError(err) + + s.T().Logf("IFT Bridge registered for client %s (EVM counterparty)", clientID) + s.T().Logf(" Bridge PDA: %s, Counterparty IFT: %s", bridgePDA, counterpartyIFTAddress) +} + +func (s *EthereumSolanaIFTTestSuite) adminMintIFTTokens(ctx context.Context, receiver solanago.PublicKey, amount uint64) { + mint := s.IFTMint() + mintBytes := s.IFTMintBytes() + + receiverATA, err := solana.AssociatedTokenAccountAddress(receiver, mint) + s.Require().NoError(err) + + mintAuthorityPDA, _ := solana.Ift.IftMintAuthorityPDA(ift.ProgramID, mintBytes) + + adminMintMsg := ift.IftStateAdminMintMsg{ + Receiver: receiver, + Amount: amount, + } + + adminMintIx, err := ift.NewAdminMintInstruction( + adminMintMsg, + s.IFTAppState, + s.IFTAppMintState, + s.IFTMint(), + mintAuthorityPDA, + receiverATA, + receiver, + s.SolanaRelayer.PublicKey(), // admin + s.SolanaRelayer.PublicKey(), // payer + token.ProgramID, + solanago.SPLAssociatedTokenAccountProgramID, + solanago.SystemProgramID, + solanago.SysVarInstructionsPubkey, + ) + s.Require().NoError(err) + + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), adminMintIx) + s.Require().NoError(err) + + _, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaRelayer) + s.Require().NoError(err) +} + +func (s *EthereumSolanaIFTTestSuite) Test_Deploy() { + ctx := context.Background() + s.SetupSuite(ctx) + + eth := s.Eth.Chains[0] + + s.Require().True(s.Run("Verify Ethereum chain is running", func() { + blockNum, err := eth.RPCClient.BlockNumber(ctx) + s.Require().NoError(err) + s.Require().Greater(blockNum, uint64(0)) + s.T().Logf("Ethereum block: %d", blockNum) + })) + + s.Require().True(s.Run("Verify Solana chain is running", func() { + slot, err := s.Solana.Chain.RPCClient.GetSlot(ctx, rpc.CommitmentConfirmed) + s.Require().NoError(err) + s.Require().Greater(slot, uint64(0)) + s.T().Logf("Solana slot: %d", slot) + })) + + s.Require().True(s.Run("Verify Relayer Info Eth->Solana", func() { + info, err := s.RelayerClient.Info(ctx, &relayertypes.InfoRequest{ + SrcChain: eth.ChainID.String(), + DstChain: testvalues.SolanaChainID, + }) + s.Require().NoError(err) + s.Require().NotNil(info) + })) + + s.Require().True(s.Run("Verify Relayer Info Solana->Eth", func() { + info, err := s.RelayerClient.Info(ctx, &relayertypes.InfoRequest{ + SrcChain: testvalues.SolanaChainID, + DstChain: eth.ChainID.String(), + }) + s.Require().NoError(err) + s.Require().NotNil(info) + })) + + s.Require().True(s.Run("Verify IFT contract on Ethereum", func() { + s.Require().NotEmpty(s.contractAddresses.Ift) + s.T().Logf("IFT contract: %s", s.contractAddresses.Ift) + })) + + s.Require().True(s.Run("Verify SolanaIFTSendCallConstructor on Ethereum", func() { + s.Require().NotEmpty(s.contractAddresses.SolanaIftConstructor) + s.T().Logf("SolanaIFTSendCallConstructor: %s", s.contractAddresses.SolanaIftConstructor) + })) +} + +func (s *EthereumSolanaIFTTestSuite) Test_EthSolana_IFT_Roundtrip() { + ctx := context.Background() + s.SetupSuite(ctx) + + eth := s.Eth.Chains[0] + ethIFTAddress := ethcommon.HexToAddress(s.contractAddresses.Ift) + + s.Require().True(s.Run("Create IFT SPL token on Solana", func() { + s.IFTMintWallet = solanago.NewWallet() + s.createIFTSplToken(ctx, s.IFTMintWallet) + + mint := s.IFTMint() + tokenAccount, err := s.Solana.Chain.CreateOrGetAssociatedTokenAccount(ctx, s.SolanaRelayer, mint, s.SolanaRelayer.PublicKey()) + s.Require().NoError(err) + s.SenderTokenAccount = tokenAccount + s.T().Logf("SPL token mint: %s, token account: %s", mint, tokenAccount) + })) + + s.Require().True(s.Run("Register IFT bridges", func() { + s.registerSolanaIFTBridgeForEVM(ctx, EthClientIDOnSolana, ethIFTAddress.Hex()) + + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + txOpts, err := eth.GetTransactOpts(s.ethDeployer) + s.Require().NoError(err) + + // counterpartyIFTAddress for Solana is the IFT program ID + tx, err := iftContract.RegisterIFTBridge(txOpts, SolanaClientIDOnEth, ift.ProgramID.String(), ethcommon.HexToAddress(s.contractAddresses.SolanaIftConstructor)) + s.Require().NoError(err) + + receipt, err := eth.GetTxReciept(ctx, tx.Hash()) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + s.T().Logf("IFT bridge registered on Ethereum for Solana counterparty") + })) + + ethUserAddr := crypto.PubkeyToAddress(s.ethUser.PublicKey) + transferAmount := big.NewInt(int64(EthSolanaIFTTransferAmount)) + + s.Require().True(s.Run("Mint tokens on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + txOpts, err := eth.GetTransactOpts(s.ethDeployer) + s.Require().NoError(err) + + tx, err := iftContract.Mint(txOpts, ethUserAddr, transferAmount) + s.Require().NoError(err) + + receipt, err := eth.GetTxReciept(ctx, tx.Hash()) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + + balance, err := iftContract.BalanceOf(nil, ethUserAddr) + s.Require().NoError(err) + s.Require().Equal(transferAmount.String(), balance.String()) + })) + + s.Require().True(s.Run("Transfer: Ethereum -> Solana", func() { + var ethSendTxHash []byte + s.Require().True(s.Run("Execute IFT transfer", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + txOpts, err := eth.GetTransactOpts(s.ethUser) + s.Require().NoError(err) + + timeout := uint64(time.Now().Add(30 * time.Minute).Unix()) + solanaReceiverHex := "0x" + hex.EncodeToString(s.SolanaRelayer.PublicKey().Bytes()) + tx, err := iftContract.IftTransfer(txOpts, SolanaClientIDOnEth, solanaReceiverHex, transferAmount, timeout) + s.Require().NoError(err) + + receipt, err := eth.GetTxReciept(ctx, tx.Hash()) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + ethSendTxHash = receipt.TxHash.Bytes() + s.T().Logf("Ethereum -> Solana transfer tx: %s", receipt.TxHash.Hex()) + })) + + s.Require().True(s.Run("Verify tokens burned on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + balance, err := iftContract.BalanceOf(nil, ethUserAddr) + s.Require().NoError(err) + s.Require().Equal("0", balance.String()) + })) + + s.Require().True(s.Run("Verify pending transfer exists on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + pending, err := iftContract.GetPendingTransfer(nil, SolanaClientIDOnEth, 1) + s.Require().NoError(err) + s.Require().Equal(ethUserAddr, pending.Sender) + s.Require().Equal(transferAmount.String(), pending.Amount.String()) + })) + + var recvSig solanago.Signature + s.Require().True(s.Run("Relay packet to Solana", func() { + resp, err := s.RelayerClient.RelayByTx(ctx, &relayertypes.RelayByTxRequest{ + SrcChain: eth.ChainID.String(), + DstChain: testvalues.SolanaChainID, + SourceTxIds: [][]byte{ethSendTxHash}, + SrcClientId: SolanaClientIDOnEth, + DstClientId: EthClientIDOnSolana, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Tx) + + sig, err := s.Solana.Chain.SubmitChunkedRelayPackets(ctx, s.T(), resp, s.SolanaRelayer) + s.Require().NoError(err) + recvSig = sig + s.T().Logf("Solana recv tx: %s", sig) + })) + + s.Require().True(s.Run("Verify tokens minted on Solana", func() { + balance, err := s.Solana.Chain.GetTokenBalance(ctx, s.SenderTokenAccount) + s.Require().NoError(err) + s.Require().Equal(EthSolanaIFTTransferAmount, balance) + })) + + s.Require().True(s.Run("Relay ack to Ethereum", func() { + ics26Address := ethcommon.HexToAddress(s.contractAddresses.Ics26Router) + + ackResp, err := s.RelayerClient.RelayByTx(ctx, &relayertypes.RelayByTxRequest{ + SrcChain: testvalues.SolanaChainID, + DstChain: eth.ChainID.String(), + SourceTxIds: [][]byte{[]byte(recvSig.String())}, + SrcClientId: EthClientIDOnSolana, + DstClientId: SolanaClientIDOnEth, + }) + s.Require().NoError(err) + + receipt, err := eth.BroadcastTx(ctx, s.ethUser, 15_000_000, &ics26Address, ackResp.Tx) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + })) + + s.Require().True(s.Run("Verify pending transfer cleared on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + _, err = iftContract.GetPendingTransfer(nil, SolanaClientIDOnEth, 1) + s.Require().Error(err, "getPendingTransfer should revert when transfer is cleared") + })) + })) + + var solanaToEthSequence uint64 + var solanaBaseSeq uint64 + s.Require().True(s.Run("Transfer: Solana -> Ethereum", func() { + var solanaTransferTxSig solanago.Signature + + s.Require().True(s.Run("Execute IFT transfer", func() { + baseSeq, err := s.Solana.Chain.GetNextSequenceNumber(ctx, s.ClientSequencePDA) + s.Require().NoError(err) + + solanaToEthSequence = solana.CalculateNamespacedSequence(baseSeq, ics27_gmp.ProgramID, s.SolanaRelayer.PublicKey()) + solanaBaseSeq = baseSeq + seqBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(seqBytes, solanaToEthSequence) + + packetCommitmentPDA, _ := solana.Ics26Router.PacketCommitmentWithArgSeedPDA(ics26_router.ProgramID, []byte(EthClientIDOnSolana), seqBytes) + pendingTransferPDA, _ := solana.Ift.PendingTransferPDA(ift.ProgramID, s.IFTMintBytes(), []byte(EthClientIDOnSolana), seqBytes) + + solanaClockTime, err := s.Solana.Chain.GetSolanaClockTime(ctx) + s.Require().NoError(err) + + transferMsg := ift.IftStateIftTransferMsg{ + ClientId: EthClientIDOnSolana, + Receiver: ethUserAddr.Hex(), + Amount: EthSolanaIFTTransferAmount, + TimeoutTimestamp: solanaClockTime + 900, + } + + attestationClientStatePDA, _ := solana.Attestation.ClientPDA(attestation.ProgramID) + consensusStatePDA := s.deriveAttestationConsensusStatePDA(ctx, attestationClientStatePDA) + + transferIx, err := ift.NewIftTransferInstruction( + transferMsg, s.IFTAppState, s.IFTAppMintState, s.IFTBridge, s.IFTMint(), s.SenderTokenAccount, + s.SolanaRelayer.PublicKey(), s.SolanaRelayer.PublicKey(), + token.ProgramID, solanago.SystemProgramID, ics27_gmp.ProgramID, s.GMPAppStatePDA, + ics26_router.ProgramID, s.RouterStatePDA, s.ClientSequencePDA, packetCommitmentPDA, + s.GMPIBCAppPDA, s.IBCClientPDA, + attestation.ProgramID, attestationClientStatePDA, solanago.SysVarInstructionsPubkey, consensusStatePDA, pendingTransferPDA, + ) + s.Require().NoError(err) + + computeBudgetIx := solana.NewComputeBudgetInstruction(ethSolComputeUnits) + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), computeBudgetIx, transferIx) + s.Require().NoError(err) + + solanaTransferTxSig, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaRelayer) + s.Require().NoError(err) + s.T().Logf("Solana -> Ethereum transfer tx: %s", solanaTransferTxSig) + })) + + s.Require().True(s.Run("Verify tokens burned on Solana", func() { + balance, err := s.Solana.Chain.GetTokenBalance(ctx, s.SenderTokenAccount) + s.Require().NoError(err) + s.Require().Equal(uint64(0), balance) + })) + + s.Require().True(s.Run("Verify pending transfer exists on Solana", func() { + s.Solana.Chain.VerifyPendingTransferExists(ctx, s.T(), s.Require(), + ift.ProgramID, s.IFTMint(), EthClientIDOnSolana, solanaToEthSequence) + })) + + var ethRecvTxHash []byte + s.Require().True(s.Run("Relay packet to Ethereum", func() { + ics26Address := ethcommon.HexToAddress(s.contractAddresses.Ics26Router) + + resp, err := s.RelayerClient.RelayByTx(ctx, &relayertypes.RelayByTxRequest{ + SrcChain: testvalues.SolanaChainID, + DstChain: eth.ChainID.String(), + SourceTxIds: [][]byte{[]byte(solanaTransferTxSig.String())}, + SrcClientId: EthClientIDOnSolana, + DstClientId: SolanaClientIDOnEth, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Tx) + + receipt, err := eth.BroadcastTx(ctx, s.ethUser, 15_000_000, &ics26Address, resp.Tx) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + ethRecvTxHash = receipt.TxHash.Bytes() + s.T().Logf("Ethereum recv tx: %s", receipt.TxHash.Hex()) + })) + + s.Require().True(s.Run("Verify tokens received on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + balance, err := iftContract.BalanceOf(nil, ethUserAddr) + s.Require().NoError(err) + s.Require().Equal(transferAmount.String(), balance.String(), "Ethereum user should have tokens back after roundtrip") + })) + + s.Require().True(s.Run("Relay ack to Solana", func() { + ackResp, err := s.RelayerClient.RelayByTx(ctx, &relayertypes.RelayByTxRequest{ + SrcChain: eth.ChainID.String(), + DstChain: testvalues.SolanaChainID, + SourceTxIds: [][]byte{ethRecvTxHash}, + SrcClientId: SolanaClientIDOnEth, + DstClientId: EthClientIDOnSolana, + }) + s.Require().NoError(err) + + _, err = s.Solana.Chain.SubmitChunkedRelayPackets(ctx, s.T(), ackResp, s.SolanaRelayer) + s.Require().NoError(err) + })) + + s.Require().True(s.Run("Verify pending transfer closed on Solana", func() { + s.Solana.Chain.VerifyPendingTransferClosed(ctx, s.T(), s.Require(), + ift.ProgramID, s.IFTMint(), EthClientIDOnSolana, solanaToEthSequence) + })) + + s.Require().True(s.Run("Verify packet commitment deleted on Solana", func() { + s.Solana.Chain.VerifyPacketCommitmentDeleted(ctx, s.T(), s.Require(), + EthClientIDOnSolana, solanaBaseSeq, ics27_gmp.ProgramID, s.SolanaRelayer.PublicKey()) + })) + })) + + s.Require().True(s.Run("Verify final balances", func() { + s.Require().True(s.Run("Ethereum user has tokens back", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + balance, err := iftContract.BalanceOf(nil, ethUserAddr) + s.Require().NoError(err) + s.Require().Equal(transferAmount.String(), balance.String(), "Ethereum user should have tokens back after roundtrip") + })) + + s.Require().True(s.Run("Solana sender has no tokens", func() { + balance, err := s.Solana.Chain.GetTokenBalance(ctx, s.SenderTokenAccount) + s.Require().NoError(err) + s.Require().Equal(uint64(0), balance, "Solana should have no tokens after roundtrip back to Ethereum") + })) + })) +} + +func (s *EthereumSolanaIFTTestSuite) Test_EthSolana_IFT_TimeoutEthToSolana() { + ctx := context.Background() + s.SetupSuite(ctx) + + eth := s.Eth.Chains[0] + ethIFTAddress := ethcommon.HexToAddress(s.contractAddresses.Ift) + ics26Address := ethcommon.HexToAddress(s.contractAddresses.Ics26Router) + + s.Require().True(s.Run("Create IFT SPL token on Solana", func() { + s.IFTMintWallet = solanago.NewWallet() + s.createIFTSplToken(ctx, s.IFTMintWallet) + + mint := s.IFTMint() + tokenAccount, err := s.Solana.Chain.CreateOrGetAssociatedTokenAccount(ctx, s.SolanaRelayer, mint, s.SolanaRelayer.PublicKey()) + s.Require().NoError(err) + s.SenderTokenAccount = tokenAccount + })) + + s.Require().True(s.Run("Register IFT bridges", func() { + s.registerSolanaIFTBridgeForEVM(ctx, EthClientIDOnSolana, ethIFTAddress.Hex()) + + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + txOpts, err := eth.GetTransactOpts(s.ethDeployer) + s.Require().NoError(err) + + tx, err := iftContract.RegisterIFTBridge(txOpts, SolanaClientIDOnEth, ift.ProgramID.String(), ethcommon.HexToAddress(s.contractAddresses.SolanaIftConstructor)) + s.Require().NoError(err) + + receipt, err := eth.GetTxReciept(ctx, tx.Hash()) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + })) + + ethUserAddr := crypto.PubkeyToAddress(s.ethUser.PublicKey) + transferAmount := big.NewInt(int64(EthSolanaIFTTransferAmount)) + + s.Require().True(s.Run("Mint tokens on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + txOpts, err := eth.GetTransactOpts(s.ethDeployer) + s.Require().NoError(err) + + tx, err := iftContract.Mint(txOpts, ethUserAddr, transferAmount) + s.Require().NoError(err) + + receipt, err := eth.GetTxReciept(ctx, tx.Hash()) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + + balance, err := iftContract.BalanceOf(nil, ethUserAddr) + s.Require().NoError(err) + s.Require().Equal(transferAmount.String(), balance.String()) + })) + + var ethSendTxHash []byte + s.Require().True(s.Run("Send transfer with short timeout", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + txOpts, err := eth.GetTransactOpts(s.ethUser) + s.Require().NoError(err) + + timeout := uint64(time.Now().Add(30 * time.Second).Unix()) + solanaReceiverHex := "0x" + hex.EncodeToString(s.SolanaRelayer.PublicKey().Bytes()) + tx, err := iftContract.IftTransfer(txOpts, SolanaClientIDOnEth, solanaReceiverHex, transferAmount, timeout) + s.Require().NoError(err) + + receipt, err := eth.GetTxReciept(ctx, tx.Hash()) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + ethSendTxHash = receipt.TxHash.Bytes() + })) + + s.Require().True(s.Run("Verify tokens burned on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + balance, err := iftContract.BalanceOf(nil, ethUserAddr) + s.Require().NoError(err) + s.Require().Equal("0", balance.String(), "Tokens should be burned") + })) + + s.Require().True(s.Run("Verify pending transfer exists on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + pending, err := iftContract.GetPendingTransfer(nil, SolanaClientIDOnEth, 1) + s.Require().NoError(err) + s.Require().Equal(ethUserAddr, pending.Sender) + s.Require().Equal(transferAmount.String(), pending.Amount.String()) + })) + + s.Require().True(s.Run("Wait for timeout", func() { + s.T().Log("Waiting 60 seconds for timeout...") + time.Sleep(60 * time.Second) + })) + + s.Require().True(s.Run("Relay timeout packet to Ethereum", func() { + resp, err := s.RelayerClient.RelayByTx(ctx, &relayertypes.RelayByTxRequest{ + SrcChain: testvalues.SolanaChainID, + DstChain: eth.ChainID.String(), + TimeoutTxIds: [][]byte{ethSendTxHash}, + SrcClientId: EthClientIDOnSolana, + DstClientId: SolanaClientIDOnEth, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Tx) + + receipt, err := eth.BroadcastTx(ctx, s.ethUser, 15_000_000, &ics26Address, resp.Tx) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + })) + + s.Require().True(s.Run("Verify tokens refunded on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + balance, err := iftContract.BalanceOf(nil, ethUserAddr) + s.Require().NoError(err) + s.Require().Equal(transferAmount.String(), balance.String(), "tokens should be refunded") + })) + + s.Require().True(s.Run("Verify pending transfer cleared on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + _, err = iftContract.GetPendingTransfer(nil, SolanaClientIDOnEth, 1) + s.Require().Error(err, "getPendingTransfer should revert when transfer is cleared") + })) + + s.Require().True(s.Run("Verify no balance on Solana", func() { + balance, err := s.Solana.Chain.GetTokenBalance(ctx, s.SenderTokenAccount) + s.Require().NoError(err) + s.Require().Equal(uint64(0), balance, "Solana should have no tokens") + })) +} + +func (s *EthereumSolanaIFTTestSuite) Test_EthSolana_IFT_TimeoutSolanaToEth() { + ctx := context.Background() + s.SetupSuite(ctx) + + eth := s.Eth.Chains[0] + ethIFTAddress := ethcommon.HexToAddress(s.contractAddresses.Ift) + + s.Require().True(s.Run("Create IFT SPL token on Solana", func() { + s.IFTMintWallet = solanago.NewWallet() + s.createIFTSplToken(ctx, s.IFTMintWallet) + + mint := s.IFTMint() + tokenAccount, err := s.Solana.Chain.CreateOrGetAssociatedTokenAccount(ctx, s.SolanaRelayer, mint, s.SolanaRelayer.PublicKey()) + s.Require().NoError(err) + s.SenderTokenAccount = tokenAccount + })) + + s.Require().True(s.Run("Register IFT bridges", func() { + s.registerSolanaIFTBridgeForEVM(ctx, EthClientIDOnSolana, ethIFTAddress.Hex()) + + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + txOpts, err := eth.GetTransactOpts(s.ethDeployer) + s.Require().NoError(err) + + tx, err := iftContract.RegisterIFTBridge(txOpts, SolanaClientIDOnEth, ift.ProgramID.String(), ethcommon.HexToAddress(s.contractAddresses.SolanaIftConstructor)) + s.Require().NoError(err) + + receipt, err := eth.GetTxReciept(ctx, tx.Hash()) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + })) + + s.Require().True(s.Run("Admin mint tokens to sender on Solana", func() { + s.adminMintIFTTokens(ctx, s.SolanaRelayer.PublicKey(), EthSolanaIFTMintAmount) + })) + + s.Require().True(s.Run("Update attestation client on Solana", func() { + s.updateAttestationClientOnSolana(ctx, eth.ChainID.String()) + })) + + ethUserAddr := crypto.PubkeyToAddress(s.ethUser.PublicKey) + + var solanaPacketTxHash []byte + var baseSequence uint64 + var namespacedSequence uint64 + s.Require().True(s.Run("Execute transfer with short timeout", func() { + var err error + baseSequence, err = s.Solana.Chain.GetNextSequenceNumber(ctx, s.ClientSequencePDA) + s.Require().NoError(err) + + namespacedSequence = solana.CalculateNamespacedSequence(baseSequence, ics27_gmp.ProgramID, s.SolanaRelayer.PublicKey()) + seqBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(seqBytes, namespacedSequence) + + packetCommitmentPDA, _ := solana.Ics26Router.PacketCommitmentWithArgSeedPDA(ics26_router.ProgramID, []byte(EthClientIDOnSolana), seqBytes) + pendingTransferPDA, _ := solana.Ift.PendingTransferPDA(ift.ProgramID, s.IFTMintBytes(), []byte(EthClientIDOnSolana), seqBytes) + + solanaClockTime, err := s.Solana.Chain.GetSolanaClockTime(ctx) + s.Require().NoError(err) + + transferMsg := ift.IftStateIftTransferMsg{ + ClientId: EthClientIDOnSolana, + Receiver: ethUserAddr.Hex(), + Amount: EthSolanaIFTTransferAmount, + TimeoutTimestamp: solanaClockTime + 45, + } + + attestationClientStatePDA, _ := solana.Attestation.ClientPDA(attestation.ProgramID) + consensusStatePDA := s.deriveAttestationConsensusStatePDA(ctx, attestationClientStatePDA) + + transferIx, err := ift.NewIftTransferInstruction( + transferMsg, s.IFTAppState, s.IFTAppMintState, s.IFTBridge, s.IFTMint(), s.SenderTokenAccount, + s.SolanaRelayer.PublicKey(), s.SolanaRelayer.PublicKey(), + token.ProgramID, solanago.SystemProgramID, ics27_gmp.ProgramID, s.GMPAppStatePDA, + ics26_router.ProgramID, s.RouterStatePDA, s.ClientSequencePDA, packetCommitmentPDA, + s.GMPIBCAppPDA, s.IBCClientPDA, + attestation.ProgramID, attestationClientStatePDA, solanago.SysVarInstructionsPubkey, consensusStatePDA, pendingTransferPDA, + ) + s.Require().NoError(err) + + computeBudgetIx := solana.NewComputeBudgetInstruction(ethSolComputeUnits) + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), computeBudgetIx, transferIx) + s.Require().NoError(err) + + sig, err := s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaRelayer) + s.Require().NoError(err) + + solanaPacketTxHash = []byte(sig.String()) + s.T().Logf("IFT transfer transaction (will timeout): %s", sig) + })) + + s.Require().True(s.Run("Verify tokens burned on Solana", func() { + balance, err := s.Solana.Chain.GetTokenBalance(ctx, s.SenderTokenAccount) + s.Require().NoError(err) + s.Require().Equal(EthSolanaIFTMintAmount-EthSolanaIFTTransferAmount, balance) + })) + + s.Require().True(s.Run("Verify pending transfer exists on Solana", func() { + s.Solana.Chain.VerifyPendingTransferExists(ctx, s.T(), s.Require(), + ift.ProgramID, s.IFTMint(), EthClientIDOnSolana, namespacedSequence) + })) + + s.Require().True(s.Run("Wait for timeout", func() { + s.T().Log("Waiting 60 seconds for timeout...") + time.Sleep(60 * time.Second) + })) + + s.Require().True(s.Run("Relay timeout back to Solana", func() { + resp, err := s.RelayerClient.RelayByTx(ctx, &relayertypes.RelayByTxRequest{ + SrcChain: eth.ChainID.String(), + DstChain: testvalues.SolanaChainID, + TimeoutTxIds: [][]byte{solanaPacketTxHash}, + SrcClientId: SolanaClientIDOnEth, + DstClientId: EthClientIDOnSolana, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Tx, "Relay should return transaction") + + sig, err := s.Solana.Chain.SubmitChunkedRelayPackets(ctx, s.T(), resp, s.SolanaRelayer) + s.Require().NoError(err) + s.T().Logf("Timeout transaction: %s", sig) + })) + + s.Require().True(s.Run("Verify tokens refunded on Solana", func() { + balance, err := s.Solana.Chain.GetTokenBalance(ctx, s.SenderTokenAccount) + s.Require().NoError(err) + s.Require().Equal(EthSolanaIFTMintAmount, balance, "Tokens should be refunded after timeout") + })) + + s.Require().True(s.Run("Verify pending transfer closed on Solana", func() { + s.Solana.Chain.VerifyPendingTransferClosed(ctx, s.T(), s.Require(), + ift.ProgramID, s.IFTMint(), EthClientIDOnSolana, namespacedSequence) + })) + + s.Require().True(s.Run("Verify packet commitment deleted on Solana", func() { + s.Solana.Chain.VerifyPacketCommitmentDeleted(ctx, s.T(), s.Require(), + EthClientIDOnSolana, baseSequence, ics27_gmp.ProgramID, s.SolanaRelayer.PublicKey()) + })) + + s.Require().True(s.Run("Verify no balance on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + balance, err := iftContract.BalanceOf(nil, ethUserAddr) + s.Require().NoError(err) + s.Require().Equal("0", balance.String(), "Ethereum should have no tokens") + })) +} + +// Test_EthSolana_IFT_FailedReceiveOnEth tests error acknowledgment when Ethereum receive fails. +// The test registers the IFT bridge only on Solana (intentionally skipping Ethereum bridge registration). +// When Solana sends an IFT transfer, Ethereum's IFT contract fails because no bridge is registered +// for the client ID. The ICS26 router catches this error and generates an error ack, which is +// relayed back to Solana to refund the sender. +func (s *EthereumSolanaIFTTestSuite) Test_EthSolana_IFT_FailedReceiveOnEth() { + ctx := context.Background() + s.SetupSuite(ctx) + + eth := s.Eth.Chains[0] + ethIFTAddress := ethcommon.HexToAddress(s.contractAddresses.Ift) + ics26Address := ethcommon.HexToAddress(s.contractAddresses.Ics26Router) + + s.Require().True(s.Run("Create IFT SPL token on Solana", func() { + s.IFTMintWallet = solanago.NewWallet() + s.createIFTSplToken(ctx, s.IFTMintWallet) + + mint := s.IFTMint() + tokenAccount, err := s.Solana.Chain.CreateOrGetAssociatedTokenAccount(ctx, s.SolanaRelayer, mint, s.SolanaRelayer.PublicKey()) + s.Require().NoError(err) + s.SenderTokenAccount = tokenAccount + })) + + s.Require().True(s.Run("Register Solana IFT bridge only", func() { + // Register the Solana-side bridge so the send from Solana works + s.registerSolanaIFTBridgeForEVM(ctx, EthClientIDOnSolana, ethIFTAddress.Hex()) + // NOTE: intentionally NOT registering the Ethereum IFT bridge + })) + + s.Require().True(s.Run("Admin mint tokens to sender on Solana", func() { + s.adminMintIFTTokens(ctx, s.SolanaRelayer.PublicKey(), EthSolanaIFTMintAmount) + })) + + s.Require().True(s.Run("Update attestation client on Solana", func() { + s.updateAttestationClientOnSolana(ctx, eth.ChainID.String()) + })) + + ethUserAddr := crypto.PubkeyToAddress(s.ethUser.PublicKey) + + var solanaTransferTxSig solanago.Signature + var baseSequence uint64 + var namespacedSequence uint64 + s.Require().True(s.Run("Execute transfer from Solana to Ethereum", func() { + var err error + baseSequence, err = s.Solana.Chain.GetNextSequenceNumber(ctx, s.ClientSequencePDA) + s.Require().NoError(err) + + namespacedSequence = solana.CalculateNamespacedSequence(baseSequence, ics27_gmp.ProgramID, s.SolanaRelayer.PublicKey()) + seqBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(seqBytes, namespacedSequence) + + packetCommitmentPDA, _ := solana.Ics26Router.PacketCommitmentWithArgSeedPDA(ics26_router.ProgramID, []byte(EthClientIDOnSolana), seqBytes) + pendingTransferPDA, _ := solana.Ift.PendingTransferPDA(ift.ProgramID, s.IFTMintBytes(), []byte(EthClientIDOnSolana), seqBytes) + + solanaClockTime, err := s.Solana.Chain.GetSolanaClockTime(ctx) + s.Require().NoError(err) + + transferMsg := ift.IftStateIftTransferMsg{ + ClientId: EthClientIDOnSolana, + Receiver: ethUserAddr.Hex(), + Amount: EthSolanaIFTTransferAmount, + TimeoutTimestamp: solanaClockTime + 900, + } + + attestationClientStatePDA, _ := solana.Attestation.ClientPDA(attestation.ProgramID) + consensusStatePDA := s.deriveAttestationConsensusStatePDA(ctx, attestationClientStatePDA) + + transferIx, err := ift.NewIftTransferInstruction( + transferMsg, s.IFTAppState, s.IFTAppMintState, s.IFTBridge, s.IFTMint(), s.SenderTokenAccount, + s.SolanaRelayer.PublicKey(), s.SolanaRelayer.PublicKey(), + token.ProgramID, solanago.SystemProgramID, ics27_gmp.ProgramID, s.GMPAppStatePDA, + ics26_router.ProgramID, s.RouterStatePDA, s.ClientSequencePDA, packetCommitmentPDA, + s.GMPIBCAppPDA, s.IBCClientPDA, + attestation.ProgramID, attestationClientStatePDA, solanago.SysVarInstructionsPubkey, consensusStatePDA, pendingTransferPDA, + ) + s.Require().NoError(err) + + computeBudgetIx := solana.NewComputeBudgetInstruction(ethSolComputeUnits) + tx, err := s.Solana.Chain.NewTransactionFromInstructions(s.SolanaRelayer.PublicKey(), computeBudgetIx, transferIx) + s.Require().NoError(err) + + solanaTransferTxSig, err = s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, tx, rpc.CommitmentConfirmed, s.SolanaRelayer) + s.Require().NoError(err) + s.T().Logf("Solana -> Ethereum transfer tx: %s", solanaTransferTxSig) + })) + + s.Require().True(s.Run("Verify tokens burned on Solana", func() { + balance, err := s.Solana.Chain.GetTokenBalance(ctx, s.SenderTokenAccount) + s.Require().NoError(err) + s.Require().Equal(EthSolanaIFTMintAmount-EthSolanaIFTTransferAmount, balance, "Tokens should be burned after transfer") + })) + + s.Require().True(s.Run("Verify pending transfer exists on Solana", func() { + s.Solana.Chain.VerifyPendingTransferExists(ctx, s.T(), s.Require(), + ift.ProgramID, s.IFTMint(), EthClientIDOnSolana, namespacedSequence) + })) + + var ethRecvTxHash []byte + s.Require().True(s.Run("Relay packet to Ethereum (execution fails)", func() { + resp, err := s.RelayerClient.RelayByTx(ctx, &relayertypes.RelayByTxRequest{ + SrcChain: testvalues.SolanaChainID, + DstChain: eth.ChainID.String(), + SourceTxIds: [][]byte{[]byte(solanaTransferTxSig.String())}, + SrcClientId: EthClientIDOnSolana, + DstClientId: SolanaClientIDOnEth, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Tx) + + receipt, err := eth.BroadcastTx(ctx, s.ethUser, 15_000_000, &ics26Address, resp.Tx) + s.Require().NoError(err) + s.Require().Equal(ethtypes.ReceiptStatusSuccessful, receipt.Status) + ethRecvTxHash = receipt.TxHash.Bytes() + })) + + s.Require().True(s.Run("Verify no balance minted on Ethereum", func() { + iftContract, err := evmift.NewContract(ethIFTAddress, eth.RPCClient) + s.Require().NoError(err) + + balance, err := iftContract.BalanceOf(nil, ethUserAddr) + s.Require().NoError(err) + s.Require().Equal("0", balance.String(), "Ethereum should have no tokens") + })) + + s.Require().True(s.Run("Relay error ack to Solana", func() { + resp, err := s.RelayerClient.RelayByTx(ctx, &relayertypes.RelayByTxRequest{ + SrcChain: eth.ChainID.String(), + DstChain: testvalues.SolanaChainID, + SourceTxIds: [][]byte{ethRecvTxHash}, + SrcClientId: SolanaClientIDOnEth, + DstClientId: EthClientIDOnSolana, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Tx) + + sig, err := s.Solana.Chain.SubmitChunkedRelayPackets(ctx, s.T(), resp, s.SolanaRelayer) + s.Require().NoError(err) + s.T().Logf("Error ack relayed: %s", sig) + })) + + s.Require().True(s.Run("Verify tokens refunded on Solana", func() { + balance, err := s.Solana.Chain.GetTokenBalance(ctx, s.SenderTokenAccount) + s.Require().NoError(err) + s.Require().Equal(EthSolanaIFTMintAmount, balance, "Tokens should be refunded after error ack") + })) + + s.Require().True(s.Run("Verify pending transfer closed on Solana", func() { + s.Solana.Chain.VerifyPendingTransferClosed(ctx, s.T(), s.Require(), + ift.ProgramID, s.IFTMint(), EthClientIDOnSolana, namespacedSequence) + })) +} + +func (s *EthereumSolanaIFTTestSuite) deriveAttestationConsensusStatePDA(ctx context.Context, clientStatePDA solanago.PublicKey) solanago.PublicKey { + accountInfo, err := s.Solana.Chain.RPCClient.GetAccountInfoWithOpts(ctx, clientStatePDA, &rpc.GetAccountInfoOpts{ + Commitment: rpc.CommitmentConfirmed, + }) + s.Require().NoError(err) + + clientState, err := attestation.ParseAccount_AttestationTypesClientState(accountInfo.Value.Data.GetBinary()) + s.Require().NoError(err) + + heightBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(heightBytes, clientState.LatestHeight) + + consensusStatePDA, _ := solana.Attestation.ConsensusStateWithArgSeedPDA( + attestation.ProgramID, + heightBytes, + ) + return consensusStatePDA +} + +func (s *EthereumSolanaIFTTestSuite) updateAttestationClientOnSolana(ctx context.Context, ethChainID string) { + resp, err := s.RelayerClient.UpdateClient(ctx, &relayertypes.UpdateClientRequest{ + SrcChain: ethChainID, + DstChain: testvalues.SolanaChainID, + DstClientId: EthClientIDOnSolana, + }) + s.Require().NoError(err) + s.Require().NotEmpty(resp.Tx) + + var solanaUpdateClient relayertypes.SolanaUpdateClient + err = proto.Unmarshal(resp.Tx, &solanaUpdateClient) + s.Require().NoError(err) + s.Require().NotEmpty(solanaUpdateClient.AssemblyTx) + + unsignedSolanaTx, err := solanago.TransactionFromDecoder(bin.NewBinDecoder(solanaUpdateClient.AssemblyTx)) + s.Require().NoError(err) + + sig, err := s.Solana.Chain.SignAndBroadcastTxWithRetry(ctx, unsignedSolanaTx, rpc.CommitmentFinalized, s.SolanaRelayer) + s.Require().NoError(err) + s.T().Logf("Attestation client updated - tx: %s", sig) +} diff --git a/e2e/interchaintestv8/relayer/builder.go b/e2e/interchaintestv8/relayer/builder.go index a96d7ad20..343b74e7e 100644 --- a/e2e/interchaintestv8/relayer/builder.go +++ b/e2e/interchaintestv8/relayer/builder.go @@ -442,6 +442,95 @@ func (b *ConfigBuilder) CosmosToSolanaAttested(p CosmosToSolanaAttestedParams) * return b } +type EthToSolanaAttestedParams struct { + EthChainID string + SolanaChainID string + EthRPC string + ICS26Address string + SolanaRPC string + ICS26ProgramID string + FeePayer string + ALTAddress string // Optional + AttestorEndpoints []string // Required for attestation mode + AttestorTimeout int // Optional, defaults to 5000 + QuorumThreshold int // Optional, defaults to 1 +} + +func (b *ConfigBuilder) EthToSolanaAttested(p EthToSolanaAttestedParams) *ConfigBuilder { + aggConfig := DefaultAggregatorConfig() + if len(p.AttestorEndpoints) > 0 { + aggConfig.Attestor.AttestorEndpoints = p.AttestorEndpoints + } + if p.AttestorTimeout > 0 { + aggConfig.Attestor.AttestorQueryTimeoutMs = p.AttestorTimeout + } + if p.QuorumThreshold > 0 { + aggConfig.Attestor.QuorumThreshold = p.QuorumThreshold + } + + var altAddress *string + if p.ALTAddress != "" { + altAddress = &p.ALTAddress + } + + module := ModuleConfig{ + Name: ModuleEthToSolana, + SrcChain: p.EthChainID, + DstChain: p.SolanaChainID, + Config: EthToSolanaModuleConfig{ + Ics26Address: p.ICS26Address, + EthRpcUrl: p.EthRPC, + SolanaRpcUrl: p.SolanaRPC, + SolanaIcs26ProgramId: p.ICS26ProgramID, + SolanaFeePayer: p.FeePayer, + SolanaAltAddress: altAddress, + Mode: AttestedMode{Config: aggConfig}, + }, + } + b.modules = append(b.modules, module) + return b +} + +type SolanaToEthAttestedParams struct { + SolanaChainID string + EthChainID string + SolanaRPC string + ICS26ProgramID string + EthRPC string + ICS26Address string + AttestorEndpoints []string + AttestorTimeout int // Optional, defaults to 5000 + QuorumThreshold int // Optional, defaults to 1 +} + +func (b *ConfigBuilder) SolanaToEthAttested(p SolanaToEthAttestedParams) *ConfigBuilder { + aggConfig := DefaultAggregatorConfig() + if len(p.AttestorEndpoints) > 0 { + aggConfig.Attestor.AttestorEndpoints = p.AttestorEndpoints + } + if p.AttestorTimeout > 0 { + aggConfig.Attestor.AttestorQueryTimeoutMs = p.AttestorTimeout + } + if p.QuorumThreshold > 0 { + aggConfig.Attestor.QuorumThreshold = p.QuorumThreshold + } + + module := ModuleConfig{ + Name: ModuleSolanaToEth, + SrcChain: p.SolanaChainID, + DstChain: p.EthChainID, + Config: SolanaToEthModuleConfig{ + SolanaRpcUrl: p.SolanaRPC, + SolanaIcs26ProgramId: p.ICS26ProgramID, + EthRpcUrl: p.EthRPC, + Ics26Address: p.ICS26Address, + Mode: AttestedMode{Config: aggConfig}, + }, + } + b.modules = append(b.modules, module) + return b +} + // ============================================================================= // Helper constructors for common prover configurations // ============================================================================= diff --git a/e2e/interchaintestv8/relayer/config.go b/e2e/interchaintestv8/relayer/config.go index 9a25fba35..57cd9773f 100644 --- a/e2e/interchaintestv8/relayer/config.go +++ b/e2e/interchaintestv8/relayer/config.go @@ -21,6 +21,8 @@ const ( ModuleEthToEth = "eth_to_eth" ModuleSolanaToCosmos = "solana_to_cosmos" ModuleCosmosToSolana = "cosmos_to_solana" + ModuleEthToSolana = "eth_to_solana" + ModuleSolanaToEth = "solana_to_eth" ) // Config represents the relayer configuration structure aligned with the Rust RelayerConfig @@ -324,3 +326,21 @@ type CosmosToSolanaModuleConfig struct { SkipPreVerifyThreshold *int `json:"skip_pre_verify_threshold,omitempty"` Mode TxBuilderMode `json:"mode,omitempty"` } + +type EthToSolanaModuleConfig struct { + Ics26Address string `json:"ics26_address"` + EthRpcUrl string `json:"eth_rpc_url"` + SolanaRpcUrl string `json:"solana_rpc_url"` + SolanaIcs26ProgramId string `json:"solana_ics26_program_id"` + SolanaFeePayer string `json:"solana_fee_payer"` + SolanaAltAddress *string `json:"solana_alt_address,omitempty"` + Mode TxBuilderMode `json:"mode"` +} + +type SolanaToEthModuleConfig struct { + SolanaRpcUrl string `json:"solana_rpc_url"` + SolanaIcs26ProgramId string `json:"solana_ics26_program_id"` + EthRpcUrl string `json:"eth_rpc_url"` + Ics26Address string `json:"ics26_address"` + Mode TxBuilderMode `json:"mode"` +} diff --git a/e2e/interchaintestv8/solana/pda.go b/e2e/interchaintestv8/solana/pda.go index d90536b87..88f348897 100644 --- a/e2e/interchaintestv8/solana/pda.go +++ b/e2e/interchaintestv8/solana/pda.go @@ -290,6 +290,17 @@ func (ics27GmpPDAs) IbcAppGmpportPDA(programID solanago.PublicKey) (solanago.Pub return pda, bump } +func (ics27GmpPDAs) PayloadHintWithAccountSeedPDA(programID solanago.PublicKey, payer []byte) (solanago.PublicKey, uint8) { + pda, bump, err := solanago.FindProgramAddress( + [][]byte{[]byte("payload_hint"), payer}, + programID, + ) + if err != nil { + panic(fmt.Sprintf("failed to derive Ics27Gmp.PayloadHintWithAccountSeedPDA PDA: %v", err)) + } + return pda, bump +} + func (ics27GmpPDAs) RouterStatePDA(programID solanago.PublicKey) (solanago.PublicKey, uint8) { pda, bump, err := solanago.FindProgramAddress( [][]byte{[]byte("router_state")}, diff --git a/justfile b/justfile index 13956b599..be998c1e2 100644 --- a/justfile +++ b/justfile @@ -1037,6 +1037,12 @@ test-e2e-cosmos-ethereum-ift testname: @echo "Running {{testname}} test..." just test-e2e TestWithCosmosEthereumIFTTestSuite/{{testname}} +# Run the e2e tests in the EthereumSolanaIFTTestSuite. For example, `just test-e2e-ethereum-solana-ift Test_EthSolana_IFT_Roundtrip` +[group('test')] +test-e2e-ethereum-solana-ift testname: + @echo "Running {{testname}} test..." + just test-e2e TestWithEthereumSolanaIFTTestSuite/{{testname}} + # Run the e2e tests in the IbcSolanaAttestationTestSuite. For example, `just test-e2e-solana-attestation Test_Attestation_Deploy` [group('test')] test-e2e-solana-attestation testname: diff --git a/nix/common.nix b/nix/common.nix index cc4a54a32..096e6fe20 100644 --- a/nix/common.nix +++ b/nix/common.nix @@ -5,5 +5,6 @@ jq parallel quicktype + foundry-bin ]; } diff --git a/nix/solidity.nix b/nix/solidity.nix index e131ed95a..f5aaa0e6c 100644 --- a/nix/solidity.nix +++ b/nix/solidity.nix @@ -4,7 +4,6 @@ system, }: { packages = with pkgs; [ - foundry-bin go-ethereum solc_0_8_28 (inputs.solc.mkDefault pkgs solc_0_8_28) diff --git a/packages/go-anchor/ics27gmp/accounts.go b/packages/go-anchor/ics27gmp/accounts.go index 9b7391f84..cf1df9207 100644 --- a/packages/go-anchor/ics27gmp/accounts.go +++ b/packages/go-anchor/ics27gmp/accounts.go @@ -57,6 +57,13 @@ func ParseAnyAccount(accountData []byte) (any, error) { return nil, fmt.Errorf("failed to unmarshal account as Ics27GmpStateGmpCallResultAccount: %w", err) } return value, nil + case Account_Ics27GmpStateSolanaPayloadHint: + value := new(Ics27GmpStateSolanaPayloadHint) + err := value.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal account as Ics27GmpStateSolanaPayloadHint: %w", err) + } + return value, nil default: return nil, fmt.Errorf("unknown discriminator: %s", binary.FormatDiscriminator(discriminator)) } @@ -163,3 +170,20 @@ func ParseAccount_Ics27GmpStateGmpCallResultAccount(accountData []byte) (*Ics27G } return acc, nil } + +func ParseAccount_Ics27GmpStateSolanaPayloadHint(accountData []byte) (*Ics27GmpStateSolanaPayloadHint, error) { + decoder := binary.NewBorshDecoder(accountData) + discriminator, err := decoder.ReadDiscriminator() + if err != nil { + return nil, fmt.Errorf("failed to peek discriminator: %w", err) + } + if discriminator != Account_Ics27GmpStateSolanaPayloadHint { + return nil, fmt.Errorf("expected discriminator %v, got %s", Account_Ics27GmpStateSolanaPayloadHint, binary.FormatDiscriminator(discriminator)) + } + acc := new(Ics27GmpStateSolanaPayloadHint) + err = acc.UnmarshalWithDecoder(decoder) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal account of type Ics27GmpStateSolanaPayloadHint: %w", err) + } + return acc, nil +} diff --git a/packages/go-anchor/ics27gmp/discriminators.go b/packages/go-anchor/ics27gmp/discriminators.go index 54453c487..504390762 100644 --- a/packages/go-anchor/ics27gmp/discriminators.go +++ b/packages/go-anchor/ics27gmp/discriminators.go @@ -11,6 +11,7 @@ var ( Account_Ics26RouterStateRouterState = [8]byte{141, 157, 194, 155, 75, 208, 200, 27} Account_Ics27GmpStateGmpAppState = [8]byte{133, 28, 169, 125, 62, 118, 161, 140} Account_Ics27GmpStateGmpCallResultAccount = [8]byte{236, 115, 234, 143, 137, 175, 113, 32} + Account_Ics27GmpStateSolanaPayloadHint = [8]byte{112, 44, 35, 65, 69, 20, 34, 184} ) // Event discriminators @@ -36,4 +37,6 @@ var ( Instruction_PauseApp = [8]byte{142, 191, 211, 112, 238, 129, 131, 66} Instruction_UnpauseApp = [8]byte{73, 253, 89, 192, 87, 42, 245, 3} Instruction_SetAccessManager = [8]byte{95, 209, 134, 89, 195, 69, 35, 122} + Instruction_StorePayloadHint = [8]byte{174, 40, 210, 189, 169, 2, 26, 139} + Instruction_ClosePayloadHint = [8]byte{84, 6, 153, 154, 191, 204, 174, 91} ) diff --git a/packages/go-anchor/ics27gmp/instructions.go b/packages/go-anchor/ics27gmp/instructions.go index d8631ac71..518839dd0 100644 --- a/packages/go-anchor/ics27gmp/instructions.go +++ b/packages/go-anchor/ics27gmp/instructions.go @@ -506,3 +506,81 @@ func NewSetAccessManagerInstruction( buf__.Bytes(), ), nil } + +// Builds a "store_payload_hint" instruction. +// Store a payload hint for ABI-encoded recv packets +func NewStorePayloadHintInstruction( + // Params: + dataParam []byte, + + // Accounts: + hintAccount solanago.PublicKey, + payerAccount solanago.PublicKey, + systemProgramAccount solanago.PublicKey, +) (solanago.Instruction, error) { + buf__ := new(bytes.Buffer) + enc__ := binary.NewBorshEncoder(buf__) + + // Encode the instruction discriminator. + err := enc__.WriteBytes(Instruction_StorePayloadHint[:], false) + if err != nil { + return nil, fmt.Errorf("failed to write instruction discriminator: %w", err) + } + { + // Serialize `dataParam`: + err = enc__.Encode(dataParam) + if err != nil { + return nil, errors.NewField("dataParam", err) + } + } + accounts__ := solanago.AccountMetaSlice{} + + // Add the accounts to the instruction. + { + // Account 0 "hint": Writable, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(hintAccount, true, false)) + // Account 1 "payer": Writable, Signer, Required + accounts__.Append(solanago.NewAccountMeta(payerAccount, true, true)) + // Account 2 "system_program": Read-only, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(systemProgramAccount, false, false)) + } + + // Create the instruction. + return solanago.NewInstruction( + ProgramID, + accounts__, + buf__.Bytes(), + ), nil +} + +// Builds a "close_payload_hint" instruction. +// Close a payload hint account and refund rent +func NewClosePayloadHintInstruction( + hintAccount solanago.PublicKey, + payerAccount solanago.PublicKey, +) (solanago.Instruction, error) { + buf__ := new(bytes.Buffer) + enc__ := binary.NewBorshEncoder(buf__) + + // Encode the instruction discriminator. + err := enc__.WriteBytes(Instruction_ClosePayloadHint[:], false) + if err != nil { + return nil, fmt.Errorf("failed to write instruction discriminator: %w", err) + } + accounts__ := solanago.AccountMetaSlice{} + + // Add the accounts to the instruction. + { + // Account 0 "hint": Writable, Non-signer, Required + accounts__.Append(solanago.NewAccountMeta(hintAccount, true, false)) + // Account 1 "payer": Writable, Signer, Required + accounts__.Append(solanago.NewAccountMeta(payerAccount, true, true)) + } + + // Create the instruction. + return solanago.NewInstruction( + ProgramID, + accounts__, + buf__.Bytes(), + ), nil +} diff --git a/packages/go-anchor/ics27gmp/types.go b/packages/go-anchor/ics27gmp/types.go index 347408481..62d7a66d6 100644 --- a/packages/go-anchor/ics27gmp/types.go +++ b/packages/go-anchor/ics27gmp/types.go @@ -1360,6 +1360,29 @@ func UnmarshalIcs27GmpStateGmpCallResultAccount(buf []byte) (*Ics27GmpStateGmpCa return obj, nil } +// Encoding format for outbound GMP packets. +// +// Determines how the `GmpPacketData` is serialized and what encoding string +// is set in the IBC payload. The sender must use the encoding the destination +// chain's ICS27 GMP module expects. +type Ics27GmpStateGmpEncoding binary.BorshEnum + +const ( + Ics27GmpStateGmpEncoding_Protobuf Ics27GmpStateGmpEncoding = iota + Ics27GmpStateGmpEncoding_Abi +) + +func (value Ics27GmpStateGmpEncoding) String() string { + switch value { + case Ics27GmpStateGmpEncoding_Protobuf: + return "Protobuf" + case Ics27GmpStateGmpEncoding_Abi: + return "Abi" + default: + return "" + } +} + // Send call message (unvalidated input from user) type Ics27GmpStateSendCallMsg struct { // Source client identifier @@ -1379,6 +1402,9 @@ type Ics27GmpStateSendCallMsg struct { // Optional memo Memo string `json:"memo"` + + // Encoding format for the IBC payload. + Encoding Ics27GmpStateGmpEncoding `json:"encoding"` } func (obj Ics27GmpStateSendCallMsg) MarshalWithEncoder(encoder *binary.Encoder) (err error) { @@ -1412,6 +1438,11 @@ func (obj Ics27GmpStateSendCallMsg) MarshalWithEncoder(encoder *binary.Encoder) if err != nil { return errors.NewField("Memo", err) } + // Serialize `Encoding`: + err = encoder.Encode(obj.Encoding) + if err != nil { + return errors.NewField("Encoding", err) + } return nil } @@ -1456,6 +1487,11 @@ func (obj *Ics27GmpStateSendCallMsg) UnmarshalWithDecoder(decoder *binary.Decode if err != nil { return errors.NewField("Memo", err) } + // Deserialize `Encoding`: + err = decoder.Decode(&obj.Encoding) + if err != nil { + return errors.NewField("Encoding", err) + } return nil } @@ -1476,6 +1512,72 @@ func UnmarshalIcs27GmpStateSendCallMsg(buf []byte) (*Ics27GmpStateSendCallMsg, e return obj, nil } +// Hint account storing a protobuf-encoded `GmpSolanaPayload`. +// +// Used by the relayer to provide the execution payload for ABI-encoded packets, +// since ABI payloads don't contain the Solana-specific execution data. +// +// PDA seeds: `["payload_hint", payer_pubkey]` +type Ics27GmpStateSolanaPayloadHint struct { + Bump uint8 `json:"bump"` + Data []byte `json:"data"` +} + +func (obj Ics27GmpStateSolanaPayloadHint) MarshalWithEncoder(encoder *binary.Encoder) (err error) { + // Serialize `Bump`: + err = encoder.Encode(obj.Bump) + if err != nil { + return errors.NewField("Bump", err) + } + // Serialize `Data`: + err = encoder.Encode(obj.Data) + if err != nil { + return errors.NewField("Data", err) + } + return nil +} + +func (obj Ics27GmpStateSolanaPayloadHint) Marshal() ([]byte, error) { + buf := bytes.NewBuffer(nil) + encoder := binary.NewBorshEncoder(buf) + err := obj.MarshalWithEncoder(encoder) + if err != nil { + return nil, fmt.Errorf("error while encoding Ics27GmpStateSolanaPayloadHint: %w", err) + } + return buf.Bytes(), nil +} + +func (obj *Ics27GmpStateSolanaPayloadHint) UnmarshalWithDecoder(decoder *binary.Decoder) (err error) { + // Deserialize `Bump`: + err = decoder.Decode(&obj.Bump) + if err != nil { + return errors.NewField("Bump", err) + } + // Deserialize `Data`: + err = decoder.Decode(&obj.Data) + if err != nil { + return errors.NewField("Data", err) + } + return nil +} + +func (obj *Ics27GmpStateSolanaPayloadHint) Unmarshal(buf []byte) error { + err := obj.UnmarshalWithDecoder(binary.NewBorshDecoder(buf)) + if err != nil { + return fmt.Errorf("error while unmarshaling Ics27GmpStateSolanaPayloadHint: %w", err) + } + return nil +} + +func UnmarshalIcs27GmpStateSolanaPayloadHint(buf []byte) (*Ics27GmpStateSolanaPayloadHint, error) { + obj := new(Ics27GmpStateSolanaPayloadHint) + err := obj.Unmarshal(buf) + if err != nil { + return nil, err + } + return obj, nil +} + // Message for onAcknowledgementPacket callback // Sent from router to IBC app when an acknowledgement is received type SolanaIbcTypesAppMsgsOnAcknowledgementPacketMsg struct { diff --git a/packages/relayer/lib/Cargo.toml b/packages/relayer/lib/Cargo.toml index 4ba33f4b6..145dd2be3 100644 --- a/packages/relayer/lib/Cargo.toml +++ b/packages/relayer/lib/Cargo.toml @@ -42,6 +42,7 @@ solana-client = { workspace = true } solana-transaction-status = { workspace = true } solana-ibc-types = { path = "../../../programs/solana/packages/solana-ibc-types" } solana-ibc-constants = { path = "../../../programs/solana/packages/solana-ibc-constants" } +solana-ibc-proto = { path = "../../../programs/solana/packages/solana-ibc-proto" } ibc-proto = { workspace = true } ibc-proto-eureka = { workspace = true } diff --git a/packages/relayer/lib/src/utils/mod.rs b/packages/relayer/lib/src/utils/mod.rs index 5225e68c7..8442b5fa2 100644 --- a/packages/relayer/lib/src/utils/mod.rs +++ b/packages/relayer/lib/src/utils/mod.rs @@ -69,3 +69,4 @@ pub mod eth_attested; pub mod eth_eureka; pub mod solana; pub mod solana_attested; +pub mod solana_gmp; diff --git a/packages/relayer/lib/src/utils/solana_attested.rs b/packages/relayer/lib/src/utils/solana_attested.rs index 143629c62..f1b0b4329 100644 --- a/packages/relayer/lib/src/utils/solana_attested.rs +++ b/packages/relayer/lib/src/utils/solana_attested.rs @@ -3,14 +3,51 @@ use crate::aggregator::rpc::{AggregatedAttestation, CommitmentType}; use crate::aggregator::Aggregator; use crate::utils::attestor::get_packet_attestation; +use crate::utils::solana::TimeoutPacketWithChunks; use alloy::sol_types::SolValue; -use anyhow::Result; +use anyhow::{Context, Result}; use borsh::BorshSerialize; -use ibc_eureka_solidity_types::ics26::IICS26RouterMsgs::Packet; +use ibc_eureka_solidity_types::ics26::IICS26RouterMsgs::{Packet, Payload}; use ibc_proto_eureka::ibc::core::{ channel::v2::{MsgAcknowledgement, MsgRecvPacket}, client::v1::Height, }; +use solana_ibc_constants::CHUNK_DATA_SIZE; +use solana_sdk::{ + compute_budget::ComputeBudgetInstruction, instruction::Instruction, pubkey::Pubkey, +}; + +/// Maximum compute units allowed per Solana transaction. +const MAX_COMPUTE_UNIT_LIMIT: u32 = 1_400_000; + +/// Priority fee in micro-lamports per compute unit. +const DEFAULT_PRIORITY_FEE: u64 = 1000; + +/// Trait abstracting the Solana tx builder methods needed for attestation +/// update_client transactions. +/// +/// Implemented by both eth-to-solana `SolanaTxBuilder` and cosmos-to-solana +/// `TxBuilder` to allow shared attestation logic. +pub trait SolanaAttestationTxBuilder { + /// Resolves the light client program ID for the given client ID. + fn resolve_client_program_id(&self, client_id: &str) -> Result; + /// Fetches the minimum required signatures from the attestation light client. + fn attestation_client_min_sigs(&self, program_id: Pubkey) -> Result; + /// Resolves the access manager program ID from the router state. + fn resolve_access_manager_program_id(&self) -> Result; + /// Returns the fee payer public key. + fn fee_payer(&self) -> Pubkey; + /// Serializes instructions into a versioned transaction. + fn create_tx_bytes(&self, instructions: &[Instruction]) -> Result>; +} + +/// Result of building an attestation update_client transaction. +pub struct AttestationUpdateClientResult { + /// Serialized assembly transaction bytes. + pub assembly_tx: Vec, + /// The target height for this update. + pub target_height: u64, +} /// Borsh-serialized membership proof for Solana attestation light client. /// @@ -30,6 +67,10 @@ pub struct SolanaMembershipProof { /// * `attested_data` - ABI-encoded attestation data from the aggregator /// * `signatures` - ECDSA signatures from attestors /// * `max_signatures` - Maximum number of signatures to include (Solana tx size limit) +/// +/// # Panics +/// +/// Panics if borsh serialization of the membership proof fails. #[must_use] pub fn build_solana_membership_proof( attested_data: Vec, @@ -90,10 +131,40 @@ pub fn collect_ack_packets(ack_msgs: &[MsgAcknowledgement]) -> Vec> { .collect() } +/// Collects ABI-encoded packets from Solana timeout messages. +#[must_use] +pub fn collect_timeout_packets_solana(timeout_msgs: &[TimeoutPacketWithChunks]) -> Vec> { + timeout_msgs + .iter() + .map(|t| { + Packet { + sequence: t.msg.packet.sequence, + sourceClient: t.msg.packet.source_client.clone(), + destClient: t.msg.packet.dest_client.clone(), + timeoutTimestamp: u64::try_from(t.msg.packet.timeout_timestamp).unwrap_or(0), + payloads: t + .msg + .packet + .payloads + .iter() + .map(|p| Payload { + sourcePort: p.source_port.clone(), + destPort: p.dest_port.clone(), + version: p.version.clone(), + encoding: p.encoding.clone(), + value: p.value.clone().into(), + }) + .collect(), + } + .abi_encode() + }) + .collect() +} + /// Injects attestation proofs into Solana-bound IBC messages. /// /// This function: -/// 1. Collects packets from recv and ack messages +/// 1. Collects packets from recv, ack and timeout messages /// 2. Fetches attestations from the aggregator in parallel /// 3. Builds Borsh-encoded proofs /// 4. Injects proofs into the messages @@ -102,6 +173,7 @@ pub fn collect_ack_packets(ack_msgs: &[MsgAcknowledgement]) -> Vec> { /// * `aggregator` - The aggregator client /// * `recv_msgs` - Mutable recv messages to inject proofs into /// * `ack_msgs` - Mutable ack messages to inject proofs into +/// * `timeout_msgs` - Mutable timeout messages to inject proofs into /// * `target_height` - Height to fetch attestations at /// * `max_signatures` - Maximum signatures to include (Solana tx size limit) /// @@ -111,13 +183,15 @@ pub async fn inject_solana_attestor_proofs( aggregator: &Aggregator, recv_msgs: &mut [MsgRecvPacket], ack_msgs: &mut [MsgAcknowledgement], + timeout_msgs: &mut [TimeoutPacketWithChunks], target_height: &Height, max_signatures: usize, ) -> Result<()> { let recv_packets = collect_recv_packets(recv_msgs); let ack_packets = collect_ack_packets(ack_msgs); + let timeout_packets = collect_timeout_packets_solana(timeout_msgs); - let (recv_attestation, ack_attestation) = tokio::join!( + let (recv_attestation, ack_attestation, timeout_attestation) = tokio::join!( fetch_packet_attestation( aggregator, recv_packets, @@ -130,6 +204,12 @@ pub async fn inject_solana_attestor_proofs( target_height.revision_height, CommitmentType::Ack ), + fetch_packet_attestation( + aggregator, + timeout_packets, + target_height.revision_height, + CommitmentType::Receipt + ), ); if let Some(attestation) = recv_attestation? { @@ -164,5 +244,161 @@ pub async fn inject_solana_attestor_proofs( ); } + if let Some(attestation) = timeout_attestation? { + let proof_bytes = build_solana_membership_proof( + attestation.attested_data, + attestation.signatures, + max_signatures, + ); + for timeout in timeout_msgs.iter_mut() { + timeout.proof_chunks.clone_from(&proof_bytes); + timeout.msg.proof.height = target_height.revision_height; + timeout.msg.proof.total_chunks = + u8::try_from(proof_bytes.len().div_ceil(CHUNK_DATA_SIZE).max(1)).unwrap_or(u8::MAX); + } + tracing::info!( + "Injected attestation proof into {} timeout messages", + timeout_msgs.len() + ); + } + Ok(()) } + +/// Builds an attestation `update_client` transaction for the given height. +/// +/// Fetches the state attestation from the aggregator, builds a membership +/// proof and assembles the Solana transaction. +/// +/// # Errors +/// Returns an error if fetching the attestation or building the transaction fails. +pub async fn build_attestation_update_client_tx( + aggregator: &Aggregator, + tx_builder: &impl SolanaAttestationTxBuilder, + dst_client_id: &str, + target_height: u64, +) -> Result { + tracing::info!( + "Building attestation update_client for height {}", + target_height + ); + + let light_client_program_id = tx_builder.resolve_client_program_id(dst_client_id)?; + let min_sigs = tx_builder.attestation_client_min_sigs(light_client_program_id)?; + + let state_attestation = aggregator.get_state_attestation(target_height).await?; + + tracing::info!( + "Aggregator returned {} signatures, attestation_data size: {} bytes", + state_attestation.signatures.len(), + state_attestation.attested_data.len() + ); + + let proof_bytes = build_solana_membership_proof( + state_attestation.attested_data, + state_attestation.signatures, + min_sigs, + ); + + let assembly_tx = build_update_client_instruction_tx( + tx_builder, + target_height, + proof_bytes, + light_client_program_id, + )?; + + Ok(AttestationUpdateClientResult { + assembly_tx, + target_height, + }) +} + +/// Updates the attestation light client to the aggregator's latest finalized +/// height. +/// +/// # Errors +/// Returns an error if fetching the latest height or building the transaction fails. +pub async fn update_attestation_client_tx( + aggregator: &Aggregator, + tx_builder: &impl SolanaAttestationTxBuilder, + dst_client_id: &str, +) -> Result { + let latest_height = aggregator.get_latest_height().await?; + tracing::info!( + "Updating attestation client {} to latest height {}", + dst_client_id, + latest_height + ); + build_attestation_update_client_tx(aggregator, tx_builder, dst_client_id, latest_height).await +} + +/// Builds the raw Solana instruction and transaction bytes for an attestation +/// `update_client` call. +fn build_update_client_instruction_tx( + tx_builder: &impl SolanaAttestationTxBuilder, + new_height: u64, + proof: Vec, + light_client_program_id: Pubkey, +) -> Result> { + use sha2::{Digest, Sha256}; + use solana_ibc_types::attestation::{ + AppState as AttestationAppState, ClientState as AttestationClientState, + ConsensusState as AttestationConsensusState, + }; + + let (client_state_pda, _) = AttestationClientState::pda(light_client_program_id); + let (new_consensus_state_pda, _) = + AttestationConsensusState::pda(new_height, light_client_program_id); + let (app_state_pda, _) = AttestationAppState::pda(light_client_program_id); + + let access_manager_program_id = tx_builder.resolve_access_manager_program_id()?; + let (access_manager_pda, _) = Pubkey::find_program_address( + &[solana_ibc_types::AccessManager::SEED], + &access_manager_program_id, + ); + + let discriminator = { + let mut hasher = Sha256::new(); + hasher.update(b"global:update_client"); + let result = hasher.finalize(); + <[u8; 8]>::try_from(&result[..8]).expect("sha256 output is at least 8 bytes") + }; + + #[derive(BorshSerialize)] + struct UpdateClientParams { + proof: Vec, + } + + let mut data = discriminator.to_vec(); + BorshSerialize::serialize(&new_height, &mut data).context("Failed to serialize new_height")?; + BorshSerialize::serialize(&UpdateClientParams { proof }, &mut data) + .context("Failed to serialize UpdateClientParams")?; + + let instruction = Instruction { + program_id: light_client_program_id, + accounts: vec![ + solana_sdk::instruction::AccountMeta::new(client_state_pda, false), + solana_sdk::instruction::AccountMeta::new(new_consensus_state_pda, false), + solana_sdk::instruction::AccountMeta::new_readonly(app_state_pda, false), + solana_sdk::instruction::AccountMeta::new_readonly(access_manager_pda, false), + solana_sdk::instruction::AccountMeta::new_readonly( + solana_sdk::sysvar::instructions::ID, + false, + ), + solana_sdk::instruction::AccountMeta::new(tx_builder.fee_payer(), true), + solana_sdk::instruction::AccountMeta::new_readonly( + solana_sdk::system_program::ID, + false, + ), + ], + data, + }; + + let instructions = vec![ + ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT), + ComputeBudgetInstruction::set_compute_unit_price(DEFAULT_PRIORITY_FEE), + instruction, + ]; + + tx_builder.create_tx_bytes(&instructions) +} diff --git a/packages/relayer/lib/src/utils/solana_gmp.rs b/packages/relayer/lib/src/utils/solana_gmp.rs new file mode 100644 index 000000000..ccb3004b3 --- /dev/null +++ b/packages/relayer/lib/src/utils/solana_gmp.rs @@ -0,0 +1,193 @@ +//! GMP (General Message Passing) account extraction utilities. +//! +//! This module handles extraction of accounts from GMP payloads for Solana transaction building. +//! GMP enables cross-chain message execution by encoding Solana instructions in IBC packets. + +use std::str::FromStr; + +use anyhow::Result; +use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey}; + +use solana_ibc_proto::{GmpPacketData, GmpSolanaPayload, Protobuf}; + +/// GMP (General Message Passing) port identifier. +pub const GMP_PORT_ID: &str = "gmpport"; + +/// Protobuf encoding type for GMP packets. +pub const PROTOBUF_ENCODING: &str = "application/x-protobuf"; + +/// Extract GMP accounts from packet payload. +/// +/// # Arguments +/// * `dest_port` - The destination port ID +/// * `encoding` - The payload encoding type +/// * `payload_value` - The raw payload data +/// * `dest_client` - The destination client ID (local client on Solana) +/// * `ibc_app_program_id` - The IBC app program ID (GMP program) +/// +/// # Returns +/// Vector of GMP accounts needed for the transaction. +/// +/// # Errors +/// Returns error if payload extraction fails. +pub fn extract_gmp_accounts( + dest_port: &str, + encoding: &str, + payload_value: &[u8], + dest_client: &str, + ibc_app_program_id: Pubkey, +) -> Result> { + if !is_gmp_payload(dest_port, encoding) { + tracing::debug!( + "Skipping non-GMP payload: dest_port='{}', encoding='{}'", + dest_port, + encoding + ); + return Ok(Vec::new()); + } + + let Some(validated_packet) = decode_gmp_packet(payload_value, dest_port) else { + return Ok(Vec::new()); + }; + + build_gmp_account_list(validated_packet, dest_client, ibc_app_program_id) +} + +/// Check if payload should be processed as GMP. +fn is_gmp_payload(dest_port: &str, encoding: &str) -> bool { + // Accept empty encoding for Cosmos compatibility + dest_port == GMP_PORT_ID && (encoding.is_empty() || encoding == PROTOBUF_ENCODING) +} + +/// Decode and validate GMP packet, returning None on failure. +fn decode_gmp_packet(payload_value: &[u8], dest_port: &str) -> Option { + match GmpPacketData::decode_vec(payload_value) { + Ok(packet) => Some(packet), + Err(e) => { + tracing::warn!("Failed to decode GMP packet for port {}: {e:?}", dest_port); + None + } + } +} + +/// Build the complete account list from GMP packet. +fn build_gmp_account_list( + packet: GmpPacketData, + dest_client: &str, + ibc_app_program_id: Pubkey, +) -> Result> { + let target_program = Pubkey::from_str(&packet.receiver) + .map_err(|e| anyhow::anyhow!("Invalid target program pubkey: {e}"))?; + + let client_id = solana_ibc_types::ClientId::new(dest_client) + .map_err(|e| anyhow::anyhow!("Invalid client ID: {e:?}"))?; + + let salt_clone = packet.salt.clone(); + let gmp_account = solana_ibc_types::GMPAccount::new( + client_id, + packet.sender.clone(), + packet.salt, + &ibc_app_program_id, + ); + + let (gmp_account_pda, _bump) = gmp_account.pda(); + + // Critical for debugging PrivilegeEscalation - keep at INFO + let salt_bytes: &[u8] = &salt_clone; + tracing::info!( + "GMP: client='{}', sender='{}', salt={:?} ({} bytes) → pda={}", + dest_client, + packet.sender, + salt_bytes, + salt_bytes.len(), + gmp_account_pda + ); + + // Include both gmp_account_pda and target_program in remaining accounts. + // The router passes these generically to GMP, which extracts target_program from [1]. + let mut account_metas = vec![ + AccountMeta { + pubkey: gmp_account_pda, + is_signer: false, + is_writable: false, + }, + AccountMeta { + pubkey: target_program, + is_signer: false, + is_writable: false, + }, + ]; + + let gmp_solana_payload = GmpSolanaPayload::decode_vec(&packet.payload) + .map_err(|e| anyhow::anyhow!("Failed to decode/validate GMP Solana payload: {e}"))?; + + // Note: is_signer is always false for transaction-level accounts. + // Accounts that need to sign during CPI (e.g., PDAs) will have their + // signing authority established via `invoke_signed` by the GMP program. + for account_meta in &gmp_solana_payload.accounts { + account_metas.push(AccountMeta { + pubkey: account_meta.pubkey, + is_signer: false, + is_writable: account_meta.is_writable, + }); + } + + tracing::debug!( + "GMP extraction: {} accounts, gmp_pda={}, target={}", + account_metas.len(), + gmp_account_pda, + target_program + ); + + Ok(account_metas) +} + +/// Find the GMP call result PDA for a given packet. +/// +/// Returns `Some(pda)` if the packet is from the GMP port, where the PDA +/// stores the acknowledgement or timeout result of a GMP call. +/// +/// Returns `None` for non-GMP ports or invalid sequence (0). +#[must_use] +pub fn find_gmp_result_pda( + source_port: &str, + source_client: &str, + sequence: u64, + gmp_program_id: Pubkey, +) -> Option { + if source_port != GMP_PORT_ID || sequence == 0 { + return None; + } + + let (pda, _bump) = + solana_ibc_types::GMPCallResult::pda(source_client, sequence, &gmp_program_id); + Some(pda) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn find_gmp_result_pda_returns_none_for_non_gmp_port() { + let program_id = Pubkey::new_unique(); + assert!(find_gmp_result_pda("transfer", "client-0", 1, program_id).is_none()); + } + + #[test] + fn find_gmp_result_pda_returns_none_for_zero_sequence() { + let program_id = Pubkey::new_unique(); + assert!(find_gmp_result_pda(GMP_PORT_ID, "client-0", 0, program_id).is_none()); + } + + #[test] + fn find_gmp_result_pda_returns_pda_for_valid_gmp_packet() { + let program_id = Pubkey::new_unique(); + let result = find_gmp_result_pda(GMP_PORT_ID, "client-0", 1, program_id); + assert!(result.is_some()); + + // Verify deterministic derivation + let result2 = find_gmp_result_pda(GMP_PORT_ID, "client-0", 1, program_id); + assert_eq!(result, result2); + } +} diff --git a/packages/relayer/modules/cosmos-to-solana/src/gmp.rs b/packages/relayer/modules/cosmos-to-solana/src/gmp.rs index 12a24860d..8feb4ed55 100644 --- a/packages/relayer/modules/cosmos-to-solana/src/gmp.rs +++ b/packages/relayer/modules/cosmos-to-solana/src/gmp.rs @@ -1,201 +1,5 @@ -//! GMP (General Message Passing) account extraction utilities -//! -//! This module handles extraction of accounts from GMP payloads for Solana transaction building. -//! GMP enables cross-chain message execution by encoding Solana instructions in IBC packets. +//! GMP account extraction - delegates to shared `ibc_eureka_relayer_lib::utils::solana_gmp`. -use std::str::FromStr; - -use anyhow::Result; -use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey}; - -use crate::constants::{GMP_PORT_ID, PROTOBUF_ENCODING}; -use crate::proto::{GmpPacketData, GmpSolanaPayload, Protobuf}; - -/// Extract GMP accounts from packet payload -/// -/// # Arguments -/// * `dest_port` - The destination port ID -/// * `encoding` - The payload encoding type -/// * `payload_value` - The raw payload data -/// * `dest_client` - The destination client ID (local client on Solana) -/// * `ibc_app_program_id` - The IBC app program ID (GMP program) -/// -/// # Returns -/// Vector of GMP accounts -/// -/// # Errors -/// Returns error if payload extraction fails -pub fn extract_gmp_accounts( - dest_port: &str, - encoding: &str, - payload_value: &[u8], - dest_client: &str, - ibc_app_program_id: Pubkey, -) -> Result> { - // Only process GMP port payloads - if !is_gmp_payload(dest_port, encoding) { - tracing::debug!( - "Skipping non-GMP payload: dest_port='{}', encoding='{}'", - dest_port, - encoding - ); - return Ok(Vec::new()); - } - - // Decode and validate GMP packet - let Some(validated_packet) = decode_gmp_packet(payload_value, dest_port) else { - return Ok(Vec::new()); - }; - - // Build account list from validated packet - build_gmp_account_list(validated_packet, dest_client, ibc_app_program_id, dest_port) -} - -/// Check if payload should be processed as GMP -fn is_gmp_payload(dest_port: &str, encoding: &str) -> bool { - // Accept empty encoding for Cosmos compatibility - dest_port == GMP_PORT_ID && (encoding.is_empty() || encoding == PROTOBUF_ENCODING) -} - -/// Decode and validate GMP packet, returning None on failure -fn decode_gmp_packet(payload_value: &[u8], dest_port: &str) -> Option { - match GmpPacketData::decode_vec(payload_value) { - Ok(packet) => Some(packet), - Err(e) => { - tracing::warn!("Failed to decode GMP packet for port {}: {e:?}", dest_port); - None - } - } -} - -/// Build the complete account list from GMP packet -fn build_gmp_account_list( - packet: GmpPacketData, - dest_client: &str, - ibc_app_program_id: Pubkey, - _dest_port: &str, -) -> Result> { - // Parse receiver as Solana Pubkey (target program) - let target_program = Pubkey::from_str(&packet.receiver) - .map_err(|e| anyhow::anyhow!("Invalid target program pubkey: {e}"))?; - - // Construct typed ClientId (local client on Solana tracking source chain) - let client_id = solana_ibc_types::ClientId::new(dest_client) - .map_err(|e| anyhow::anyhow!("Invalid client ID: {e:?}"))?; - - let salt_clone = packet.salt.clone(); - let gmp_account = solana_ibc_types::GMPAccount::new( - client_id, - packet.sender.clone(), - packet.salt, - &ibc_app_program_id, - ); - - let (gmp_account_pda, _bump) = gmp_account.pda(); - - // Critical for debugging PrivilegeEscalation - keep at INFO - let salt_bytes: &[u8] = &salt_clone; - tracing::info!( - "GMP: client='{}', sender='{}', salt={:?} ({} bytes) → pda={}", - dest_client, - packet.sender, - salt_bytes, - salt_bytes.len(), - gmp_account_pda - ); - - // Include both gmp_account_pda and target_program in remaining accounts - // The router passes these generically to GMP, which extracts target_program from [1] - let mut account_metas = vec![ - AccountMeta { - pubkey: gmp_account_pda, - is_signer: false, - is_writable: false, // readonly - stateless, no account creation - }, - AccountMeta { - pubkey: target_program, - is_signer: false, - is_writable: false, // target_program is readonly (it's a program ID) - }, - ]; - - // Decode and validate GMP Solana payload - let gmp_solana_payload = GmpSolanaPayload::decode_vec(&packet.payload) - .map_err(|e| anyhow::anyhow!("Failed to decode/validate GMP Solana payload: {e}"))?; - - add_instruction_accounts(&gmp_solana_payload, &mut account_metas); - - tracing::debug!( - "GMP extraction: {} accounts, gmp_pda={}, target={}", - account_metas.len(), - gmp_account_pda, - target_program - ); - - Ok(account_metas) -} - -/// Add accounts from `GmpSolanaPayload` to the account list -/// -/// Note: `is_signer` is always false for transaction-level accounts. -/// Accounts that need to sign during CPI (e.g., PDAs) will have their -/// signing authority established via `invoke_signed` by the GMP program. -fn add_instruction_accounts(instruction: &GmpSolanaPayload, account_metas: &mut Vec) { - for account_meta in &instruction.accounts { - account_metas.push(AccountMeta { - pubkey: account_meta.pubkey, - is_signer: false, // PDAs sign via invoke_signed, not as tx signers - is_writable: account_meta.is_writable, - }); - } -} - -/// Find the GMP call result PDA for a given packet. -/// -/// Returns `Some(pda)` if the packet is from the GMP port, where the PDA -/// stores the acknowledgement or timeout result of a GMP call. -/// -/// Returns `None` for non-GMP ports or invalid sequence (0). -#[must_use] -pub fn find_gmp_result_pda( - source_port: &str, - source_client: &str, - sequence: u64, - gmp_program_id: Pubkey, -) -> Option { - if source_port != GMP_PORT_ID || sequence == 0 { - return None; - } - - let (pda, _bump) = - solana_ibc_types::GMPCallResult::pda(source_client, sequence, &gmp_program_id); - Some(pda) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn find_gmp_result_pda_returns_none_for_non_gmp_port() { - let program_id = Pubkey::new_unique(); - assert!(find_gmp_result_pda("transfer", "client-0", 1, program_id).is_none()); - } - - #[test] - fn find_gmp_result_pda_returns_none_for_zero_sequence() { - let program_id = Pubkey::new_unique(); - assert!(find_gmp_result_pda(GMP_PORT_ID, "client-0", 0, program_id).is_none()); - } - - #[test] - fn find_gmp_result_pda_returns_pda_for_valid_gmp_packet() { - let program_id = Pubkey::new_unique(); - let result = find_gmp_result_pda(GMP_PORT_ID, "client-0", 1, program_id); - assert!(result.is_some()); - - // Verify deterministic derivation - let result2 = find_gmp_result_pda(GMP_PORT_ID, "client-0", 1, program_id); - assert_eq!(result, result2); - } -} +pub use ibc_eureka_relayer_lib::utils::solana_gmp::{ + extract_gmp_accounts, find_gmp_result_pda, GMP_PORT_ID, PROTOBUF_ENCODING, +}; diff --git a/packages/relayer/modules/cosmos-to-solana/src/lib.rs b/packages/relayer/modules/cosmos-to-solana/src/lib.rs index f0947b2bb..6dee182b3 100644 --- a/packages/relayer/modules/cosmos-to-solana/src/lib.rs +++ b/packages/relayer/modules/cosmos-to-solana/src/lib.rs @@ -218,6 +218,19 @@ impl RelayerService for CosmosToSolanaRelayerModuleService { tracing::debug!("Fetched {} target events", target_events.len()); + // For timeouts in attested mode, get the current source chain height + // where non-membership is proven + let timeout_relay_height = if self.tx_builder.is_attested() && !target_events.is_empty() { + Some( + self.src_listener + .get_block_height() + .await + .map_err(to_tonic_status)?, + ) + } else { + None + }; + let (packet_txs, update_client) = self .tx_builder .relay_events( @@ -227,6 +240,7 @@ impl RelayerService for CosmosToSolanaRelayerModuleService { &inner_req.dst_client_id, &inner_req.src_packet_sequences, &inner_req.dst_packet_sequences, + timeout_relay_height, ) .await .map_err(to_tonic_status)?; @@ -325,6 +339,7 @@ impl CosmosToSolanaTxBuilder { dst_client_id: &str, src_packet_seqs: &[u64], dst_packet_seqs: &[u64], + timeout_relay_height: Option, ) -> anyhow::Result<( Vec, Option, @@ -338,6 +353,7 @@ impl CosmosToSolanaTxBuilder { dst_client_id: dst_client_id.to_string(), src_packet_seqs: src_packet_seqs.to_vec(), dst_packet_seqs: dst_packet_seqs.to_vec(), + timeout_relay_height, }) .await } @@ -349,6 +365,7 @@ impl CosmosToSolanaTxBuilder { dst_client_id: dst_client_id.to_string(), src_packet_seqs: src_packet_seqs.to_vec(), dst_packet_seqs: dst_packet_seqs.to_vec(), + timeout_relay_height, }) .await } @@ -371,4 +388,8 @@ impl CosmosToSolanaTxBuilder { Self::Attested(tb) => tb.update_client(dst_client_id).await, } } + + const fn is_attested(&self) -> bool { + matches!(self, Self::Attested(_)) + } } diff --git a/packages/relayer/modules/cosmos-to-solana/src/tx_builder.rs b/packages/relayer/modules/cosmos-to-solana/src/tx_builder.rs index d8226e892..be5aea071 100644 --- a/packages/relayer/modules/cosmos-to-solana/src/tx_builder.rs +++ b/packages/relayer/modules/cosmos-to-solana/src/tx_builder.rs @@ -76,6 +76,9 @@ pub struct RelayParams { pub src_packet_seqs: Vec, /// Packet sequences from the destination chain pub dst_packet_seqs: Vec, + /// For timeout packets, the current height from the source chain where + /// non-membership needs to be proven. Required when processing timeouts. + pub timeout_relay_height: Option, } /// Maximum compute units allowed per Solana transaction @@ -382,6 +385,7 @@ impl TxBuilder { dst_client_id, src_packet_seqs, dst_packet_seqs, + timeout_relay_height: _, } = params; let solana_ics07_program_id = self.resolve_client_program_id(&dst_client_id)?; @@ -729,7 +733,7 @@ impl AttestedTxBuilder { let max_timeout_ts = TxBuilder::max_timeout_timestamp(¶ms.dest_events); // Build timeout messages from destination events - let timeout_msgs = solana_utils::target_events_to_timeout_msgs( + let mut timeout_msgs = solana_utils::target_events_to_timeout_msgs( params.dest_events, ¶ms.src_client_id, ¶ms.dst_client_id, @@ -787,28 +791,21 @@ impl AttestedTxBuilder { let needs_update = needs_height_update || needs_timestamp_update; - // Determine proof height: use current if sufficient, otherwise need to update + // For timeouts, use the source chain height (where non-membership is proven). + // This ensures the consensus state timestamp is past the packet's timeout. let proof_height = if needs_update { - required_height.max(current_height.saturating_add(1)) - } else { - current_height - }; - - tracing::info!( - "Attestation client at height {}, required height {}, needs_update: {} (height: {}, timestamp: {})", - current_height, - required_height, - needs_update, - needs_height_update, - needs_timestamp_update - ); + let min_height = required_height.max(current_height.saturating_add(1)); + let target = params + .timeout_relay_height + .map_or(min_height, |h| h.max(min_height)); - // Only wait for aggregator if we need to update - if needs_update { tracing::info!( - "Waiting for aggregator to finalize height {} (max event height: {})", - proof_height, - max_height + "Attestation client at height {}, waiting for aggregator to finalize height {} \ + (needs_height: {}, needs_timestamp: {})", + current_height, + target, + needs_height_update, + needs_timestamp_update ); wait_for_condition( @@ -816,14 +813,17 @@ impl AttestedTxBuilder { Duration::from_secs(1), || async { let finalized_height = self.aggregator.get_latest_height().await?; - Ok(finalized_height >= proof_height) + Ok(finalized_height >= target) }, ) .await .context("Timeout waiting for aggregator to finalize height")?; - tracing::info!("Aggregator has finalized height {}", proof_height); - } + tracing::info!("Aggregator has finalized height {}", target); + target + } else { + current_height + }; let min_sigs = client_state.min_required_sigs as usize; tracing::info!("Attestation client requires {} signatures", min_sigs); @@ -837,6 +837,7 @@ impl AttestedTxBuilder { &self.aggregator, &mut recv_msgs, &mut ack_msgs, + &mut timeout_msgs, &target_height, min_sigs, ) @@ -844,10 +845,21 @@ impl AttestedTxBuilder { // Build update_client transaction only if needed let update_client = if needs_update { - Some( - self.build_attestation_update_client(¶ms.dst_client_id, proof_height) - .await?, + let result = solana_attested::build_attestation_update_client_tx( + &self.aggregator, + &self.tx_builder, + ¶ms.dst_client_id, + proof_height, ) + .await?; + Some(api::SolanaUpdateClient { + chunk_txs: vec![], + alt_create_tx: vec![], + alt_extend_txs: vec![], + assembly_tx: result.assembly_tx, + target_height: result.target_height, + cleanup_tx: vec![], + }) } else { tracing::info!( "Client already at sufficient height {}, skipping update", @@ -863,144 +875,25 @@ impl AttestedTxBuilder { Ok((packets, update_client)) } - /// Build update_client transaction for attestation light client. - async fn build_attestation_update_client( - &self, - dst_client_id: &str, - target_height: u64, - ) -> Result { - tracing::info!( - "Building attestation update_client for height {}", - target_height - ); - - let light_client_program_id = self.tx_builder.resolve_client_program_id(dst_client_id)?; - let min_sigs = self - .tx_builder - .attestation_client_min_sigs(light_client_program_id)?; - tracing::info!("Attestation client requires {} signatures", min_sigs); - - // Get state attestation from aggregator - let state_attestation = self.aggregator.get_state_attestation(target_height).await?; - - tracing::info!( - "Aggregator returned {} signatures, attestation_data size: {} bytes", - state_attestation.signatures.len(), - state_attestation.attested_data.len() - ); - - let proof_bytes = solana_attested::build_solana_membership_proof( - state_attestation.attested_data, - state_attestation.signatures, - min_sigs, - ); - - tracing::info!("Proof bytes size: {} bytes", proof_bytes.len()); - - // Build the update_client instruction for attestation light client - let update_tx = self.build_attestation_update_client_tx( - target_height, - proof_bytes, - light_client_program_id, - )?; - - tracing::info!("Update transaction size: {} bytes", update_tx.len()); - - Ok(api::SolanaUpdateClient { - chunk_txs: vec![], - alt_create_tx: vec![], - alt_extend_txs: vec![], - assembly_tx: update_tx, - target_height, - cleanup_tx: vec![], - }) - } - - /// Build update_client transaction bytes for attestation light client. - fn build_attestation_update_client_tx( - &self, - new_height: u64, - proof: Vec, - light_client_program_id: Pubkey, - ) -> Result> { - use sha2::{Digest, Sha256}; - use solana_ibc_types::attestation::{ - AppState as AttestationAppState, ClientState as AttestationClientState, - ConsensusState as AttestationConsensusState, - }; - - let (client_state_pda, _) = AttestationClientState::pda(light_client_program_id); - let (new_consensus_state_pda, _) = - AttestationConsensusState::pda(new_height, light_client_program_id); - let (app_state_pda, _) = AttestationAppState::pda(light_client_program_id); - - let access_manager_program_id = self.tx_builder.resolve_access_manager_program_id()?; - let (access_manager_pda, _) = Pubkey::find_program_address( - &[solana_ibc_types::AccessManager::SEED], - &access_manager_program_id, - ); - - // Build instruction data: discriminator + borsh(new_height, params) - // Anchor discriminator = sha256("global:update_client")[..8] - let discriminator = { - let mut hasher = Sha256::new(); - hasher.update(b"global:update_client"); - let result = hasher.finalize(); - <[u8; 8]>::try_from(&result[..8]).expect("sha256 output is at least 8 bytes") - }; - - #[derive(AnchorSerialize)] - struct UpdateClientParams { - proof: Vec, - } - - let mut data = discriminator.to_vec(); - new_height - .serialize(&mut data) - .context("Failed to serialize new_height")?; - UpdateClientParams { proof } - .serialize(&mut data) - .context("Failed to serialize UpdateClientParams")?; - - let instruction = solana_sdk::instruction::Instruction { - program_id: light_client_program_id, - accounts: vec![ - solana_sdk::instruction::AccountMeta::new(client_state_pda, false), - solana_sdk::instruction::AccountMeta::new(new_consensus_state_pda, false), - solana_sdk::instruction::AccountMeta::new_readonly(app_state_pda, false), - solana_sdk::instruction::AccountMeta::new_readonly(access_manager_pda, false), - solana_sdk::instruction::AccountMeta::new_readonly( - solana_sdk::sysvar::instructions::ID, - false, - ), - solana_sdk::instruction::AccountMeta::new(self.tx_builder.fee_payer, true), - solana_sdk::instruction::AccountMeta::new_readonly( - solana_sdk::system_program::ID, - false, - ), - ], - data, - }; - - let mut instructions = TxBuilder::extend_compute_ix(); - instructions.push(instruction); - - self.tx_builder.create_tx_bytes(&instructions) - } - /// Update the attestation light client to the latest height. /// /// # Errors /// Returns an error if fetching attestations or building transactions fails. pub async fn update_client(&self, dst_client_id: &str) -> Result { - let latest_height = self.aggregator.get_latest_height().await?; - tracing::info!( - "Updating attestation client {} to latest height {}", + let result = solana_attested::update_attestation_client_tx( + &self.aggregator, + &self.tx_builder, dst_client_id, - latest_height - ); - self.build_attestation_update_client(dst_client_id, latest_height) - .await + ) + .await?; + Ok(api::SolanaUpdateClient { + chunk_txs: vec![], + alt_create_tx: vec![], + alt_extend_txs: vec![], + assembly_tx: result.assembly_tx, + target_height: result.target_height, + cleanup_tx: vec![], + }) } async fn build_packet_transactions( @@ -1035,9 +928,6 @@ impl AttestedTxBuilder { } for timeout in timeout_msgs { - // Note: Timeout proofs for attestation mode require non-membership attestations - // from the aggregator. Currently, timeout proof data is initialized with empty values. - // The build_timeout_packet_chunked will use whatever proof data is available. tracing::info!( "Building timeout packet for sequence {}", timeout.msg.packet.sequence diff --git a/packages/relayer/modules/cosmos-to-solana/src/tx_builder/transaction.rs b/packages/relayer/modules/cosmos-to-solana/src/tx_builder/transaction.rs index 95320c4f9..33889b3cd 100644 --- a/packages/relayer/modules/cosmos-to-solana/src/tx_builder/transaction.rs +++ b/packages/relayer/modules/cosmos-to-solana/src/tx_builder/transaction.rs @@ -392,3 +392,27 @@ impl super::TxBuilder { .unwrap_or(false) } } + +impl ibc_eureka_relayer_lib::utils::solana_attested::SolanaAttestationTxBuilder + for super::TxBuilder +{ + fn resolve_client_program_id(&self, client_id: &str) -> Result { + self.resolve_client_program_id(client_id) + } + + fn attestation_client_min_sigs(&self, program_id: Pubkey) -> Result { + self.attestation_client_min_sigs(program_id) + } + + fn resolve_access_manager_program_id(&self) -> Result { + self.resolve_access_manager_program_id() + } + + fn fee_payer(&self) -> Pubkey { + self.fee_payer + } + + fn create_tx_bytes(&self, instructions: &[Instruction]) -> Result> { + self.create_tx_bytes(instructions) + } +} diff --git a/packages/relayer/modules/eth-to-solana/Cargo.toml b/packages/relayer/modules/eth-to-solana/Cargo.toml new file mode 100644 index 000000000..fb4489c1c --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "ibc-eureka-relayer-eth-to-solana" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +ibc-eureka-relayer-core = { workspace = true, default-features = false } +ibc-eureka-relayer-lib.workspace = true +ibc-eureka-utils.workspace = true +ibc-eureka-solidity-types = { workspace = true, features = ["rpc"] } + +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true + +tonic = { workspace = true, default-features = true } +anyhow = { workspace = true, features = ["std"] } +tracing = { workspace = true, default-features = true } + +# EVM dependencies +alloy = { workspace = true, features = ["full", "node-bindings"] } + +# Solana dependencies +solana-client.workspace = true +solana-sdk.workspace = true +solana-transaction-status.workspace = true +bincode.workspace = true +sha2.workspace = true +anchor-lang.workspace = true +borsh.workspace = true + +ibc-proto-eureka.workspace = true +prost.workspace = true +solana-ibc-types = { path = "../../../../programs/solana/packages/solana-ibc-types", features = ["light-client"] } +solana-ibc-constants = { path = "../../../../programs/solana/packages/solana-ibc-constants" } +solana-ibc-proto = { path = "../../../../programs/solana/packages/solana-ibc-proto" } + +# SPL Token dependencies +spl-token = "8.0" +spl-associated-token-account = "7.0" diff --git a/packages/relayer/modules/eth-to-solana/src/constants.rs b/packages/relayer/modules/eth-to-solana/src/constants.rs new file mode 100644 index 000000000..8b13ee0af --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/src/constants.rs @@ -0,0 +1,4 @@ +//! Constants for the Eth to Solana relayer + +/// Anchor account discriminator size (first 8 bytes of account data) +pub const ANCHOR_DISCRIMINATOR_SIZE: usize = 8; diff --git a/packages/relayer/modules/eth-to-solana/src/gmp.rs b/packages/relayer/modules/eth-to-solana/src/gmp.rs new file mode 100644 index 000000000..8feb4ed55 --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/src/gmp.rs @@ -0,0 +1,5 @@ +//! GMP account extraction - delegates to shared `ibc_eureka_relayer_lib::utils::solana_gmp`. + +pub use ibc_eureka_relayer_lib::utils::solana_gmp::{ + extract_gmp_accounts, find_gmp_result_pda, GMP_PORT_ID, PROTOBUF_ENCODING, +}; diff --git a/packages/relayer/modules/eth-to-solana/src/ift.rs b/packages/relayer/modules/eth-to-solana/src/ift.rs new file mode 100644 index 000000000..61a7a428f --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/src/ift.rs @@ -0,0 +1,266 @@ +//! IFT `claim_refund` instruction builder for ack/timeout packets. +//! +//! After GMP processes ack/timeout and creates `GMPCallResultAccount`, +//! the relayer calls IFT's `claim_refund` to process refunds. + +use std::str::FromStr; +use std::sync::{Arc, LazyLock}; + +use anchor_lang::prelude::*; +use anyhow::Result; +use sha2::{Digest, Sha256}; +use solana_client::rpc_client::RpcClient; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, +}; +use spl_associated_token_account::get_associated_token_address; + +use alloy::sol_types::SolValue; + +use crate::constants::ANCHOR_DISCRIMINATOR_SIZE; +use crate::gmp::{GMP_PORT_ID, PROTOBUF_ENCODING}; +use crate::proto::{GmpPacketData, Protobuf}; +use crate::tx_builder::payload_translator::{AbiGmpPacketData, ABI_ENCODING}; + +/// IFT PDA seeds (must match ift program) +const IFT_APP_STATE_SEED: &[u8] = b"ift_app_state"; +const IFT_APP_MINT_STATE_SEED: &[u8] = b"ift_app_mint_state"; +const IFT_BRIDGE_SEED: &[u8] = b"ift_bridge"; +const PENDING_TRANSFER_SEED: &[u8] = b"pending_transfer"; +const MINT_AUTHORITY_SEED: &[u8] = b"ift_mint_authority"; + +/// GMP result PDA seed (must match ics27-gmp program) +const GMP_RESULT_SEED: &[u8] = b"gmp_result"; + +static PENDING_TRANSFER_DISCRIMINATOR: LazyLock<[u8; 8]> = LazyLock::new(|| { + let mut hasher = Sha256::new(); + hasher.update(b"account:PendingTransfer"); + let result = hasher.finalize(); + result[..8].try_into().expect("sha256 produces 32 bytes") +}); + +static FINALIZE_TRANSFER_DISCRIMINATOR: LazyLock<[u8; 8]> = LazyLock::new(|| { + let mut hasher = Sha256::new(); + hasher.update(b"global:finalize_transfer"); + let result = hasher.finalize(); + result[..8].try_into().expect("sha256 produces 32 bytes") +}); + +/// Deserialized `PendingTransfer` account data +#[derive(AnchorSerialize, AnchorDeserialize, Debug)] +struct PendingTransfer { + pub version: u8, + pub bump: u8, + pub mint: Pubkey, + pub client_id: String, + pub sequence: u64, + pub sender: Pubkey, + pub amount: u64, + pub timestamp: i64, + pub _reserved: [u8; 32], +} + +/// Parameters for building IFT `claim_refund` instruction +pub struct ClaimRefundParams<'a> { + pub source_port: &'a str, + pub encoding: &'a str, + pub payload_value: &'a [u8], + pub source_client: &'a str, + pub sequence: u64, + pub solana_client: &'a Arc, + pub gmp_program_id: Pubkey, + pub fee_payer: Pubkey, +} + +/// Build IFT `claim_refund` instruction if this packet is from IFT. +/// Returns None if the packet is not an IFT transfer or no pending transfer exists. +pub fn build_claim_refund_instruction(params: &ClaimRefundParams<'_>) -> Option { + // Only process GMP port packets with known encoding + let is_protobuf = params.encoding.is_empty() || params.encoding == PROTOBUF_ENCODING; + let is_abi = params.encoding == ABI_ENCODING; + + if params.source_port != GMP_PORT_ID || !(is_protobuf || is_abi) { + return None; + } + + // Decode sender from the GMP packet (protobuf or ABI) + let sender_string = if is_abi { + match ::abi_decode(params.payload_value) { + Ok(packet) => packet.sender, + Err(e) => { + tracing::warn!(error = ?e, "IFT: Failed to ABI decode GMP packet"); + return None; + } + } + } else { + match GmpPacketData::decode_vec(params.payload_value) { + Ok(packet) => packet.sender.to_string(), + Err(e) => { + tracing::warn!(error = ?e, "IFT: Failed to decode GMP packet"); + return None; + } + } + }; + + // Parse sender as Pubkey (the IFT program ID) + let ift_program_id = match Pubkey::from_str(&sender_string) { + Ok(pk) => pk, + Err(e) => { + tracing::warn!(error = ?e, sender = %sender_string, "IFT: GMP sender is not a valid Pubkey"); + return None; + } + }; + + // Try to find pending transfer for this (client_id, sequence) + let pending_transfer = match find_pending_transfer( + params.solana_client, + ift_program_id, + params.source_client, + params.sequence, + ) { + Ok(Some(pt)) => pt, + Ok(None) => { + tracing::debug!( + client_id = params.source_client, + sequence = params.sequence, + "IFT: No pending transfer found" + ); + return None; + } + Err(e) => { + tracing::error!(error = ?e, "IFT: Error searching for pending transfer"); + return None; + } + }; + + tracing::debug!( + mint = %pending_transfer.mint, + amount = pending_transfer.amount, + sequence = params.sequence, + "IFT: Building claim_refund instruction" + ); + + Some(build_finalize_transfer_ix( + ift_program_id, + params.gmp_program_id, + &pending_transfer, + params.source_client, + params.sequence, + params.fee_payer, + )) +} + +fn find_pending_transfer( + solana_client: &Arc, + ift_program_id: Pubkey, + client_id: &str, + sequence: u64, +) -> Result> { + let all_accounts = solana_client + .get_program_accounts(&ift_program_id) + .map_err(|e| anyhow::anyhow!("Failed to get program accounts: {e}"))?; + + let accounts: Vec<_> = all_accounts + .into_iter() + .filter(|(_, account)| { + account.data.len() >= ANCHOR_DISCRIMINATOR_SIZE + && account.data[..ANCHOR_DISCRIMINATOR_SIZE] == *PENDING_TRANSFER_DISCRIMINATOR + }) + .collect(); + + for (pubkey, account) in accounts { + let mut data = &account.data[ANCHOR_DISCRIMINATOR_SIZE..]; + let pending = match PendingTransfer::deserialize(&mut data) { + Ok(p) => p, + Err(e) => { + tracing::debug!(error = ?e, account = %pubkey, "IFT: Failed to deserialize account"); + continue; + } + }; + + if pending.client_id == client_id && pending.sequence == sequence { + return Ok(Some(pending)); + } + } + + Ok(None) +} + +fn build_finalize_transfer_ix( + ift_program_id: Pubkey, + gmp_program_id: Pubkey, + pending_transfer: &PendingTransfer, + client_id: &str, + sequence: u64, + fee_payer: Pubkey, +) -> Instruction { + let mint = pending_transfer.mint; + + // Derive PDAs + let (app_state_pda, _) = Pubkey::find_program_address(&[IFT_APP_STATE_SEED], &ift_program_id); + + let (app_mint_state_pda, _) = + Pubkey::find_program_address(&[IFT_APP_MINT_STATE_SEED, mint.as_ref()], &ift_program_id); + + let (ift_bridge_pda, _) = Pubkey::find_program_address( + &[IFT_BRIDGE_SEED, mint.as_ref(), client_id.as_bytes()], + &ift_program_id, + ); + + let (pending_transfer_pda, _) = Pubkey::find_program_address( + &[ + PENDING_TRANSFER_SEED, + mint.as_ref(), + client_id.as_bytes(), + &sequence.to_le_bytes(), + ], + &ift_program_id, + ); + + // GMP result PDA - owned by GMP program + let (gmp_result_pda, _) = Pubkey::find_program_address( + &[ + GMP_RESULT_SEED, + client_id.as_bytes(), + &sequence.to_le_bytes(), + ], + &gmp_program_id, + ); + + let (mint_authority_pda, _) = + Pubkey::find_program_address(&[MINT_AUTHORITY_SEED, mint.as_ref()], &ift_program_id); + + let sender_token_account = get_associated_token_address(&pending_transfer.sender, &mint); + + // Account order must match IFT's FinalizeTransfer struct + let accounts = vec![ + AccountMeta::new_readonly(app_state_pda, false), + AccountMeta::new(app_mint_state_pda, false), + AccountMeta::new_readonly(ift_bridge_pda, false), + AccountMeta::new(pending_transfer_pda, false), + AccountMeta::new_readonly(gmp_result_pda, false), + AccountMeta::new(mint, false), + AccountMeta::new_readonly(mint_authority_pda, false), + AccountMeta::new(sender_token_account, false), + AccountMeta::new(fee_payer, true), + AccountMeta::new_readonly(spl_token::id(), false), + AccountMeta::new_readonly(solana_sdk::system_program::id(), false), + AccountMeta::new_readonly(solana_sdk::sysvar::instructions::id(), false), + ]; + + // Build instruction data: discriminator + client_id (string) + sequence (u64) + let mut data = FINALIZE_TRANSFER_DISCRIMINATOR.to_vec(); + // Anchor serializes String as length-prefixed (u32 + bytes) + let client_id_bytes = client_id.as_bytes(); + let client_id_len = u32::try_from(client_id_bytes.len()).expect("client_id length fits in u32"); + data.extend_from_slice(&client_id_len.to_le_bytes()); + data.extend_from_slice(client_id_bytes); + data.extend_from_slice(&sequence.to_le_bytes()); + + Instruction { + program_id: ift_program_id, + accounts, + data, + } +} diff --git a/packages/relayer/modules/eth-to-solana/src/ift_payload.rs b/packages/relayer/modules/eth-to-solana/src/ift_payload.rs new file mode 100644 index 000000000..511582348 --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/src/ift_payload.rs @@ -0,0 +1,308 @@ +//! IFT payload translation: ABI-encoded (bytes32, uint256) → GmpSolanaPayload. +//! +//! EVM sends `abi.encode(bytes32(receiver), uint256(amount))`. +//! This module decodes it and builds a `GmpSolanaPayload` with the correct +//! accounts matching `ift_mint.rs`. + +use std::sync::Arc; + +use anchor_lang::prelude::*; +use anyhow::{Context, Result}; +use solana_client::rpc_client::RpcClient; +use solana_sdk::{commitment_config::CommitmentConfig, pubkey::Pubkey}; +use spl_associated_token_account::get_associated_token_address; + +use crate::constants::ANCHOR_DISCRIMINATOR_SIZE; +use crate::proto::{GmpSolanaPayload, SolanaAccountMeta}; + +/// IFT PDA seeds (must match ift program) +const IFT_APP_STATE_SEED: &[u8] = b"ift_app_state"; +const IFT_APP_MINT_STATE_SEED: &[u8] = b"ift_app_mint_state"; +const IFT_BRIDGE_SEED: &[u8] = b"ift_bridge"; +const MINT_AUTHORITY_SEED: &[u8] = b"ift_mint_authority"; + +/// Decoded IFT mint payload from EVM ABI encoding. +#[derive(Debug, Clone)] +pub struct IFTMintPayload { + /// Receiver Solana pubkey (decoded from bytes32) + pub receiver: Pubkey, + /// Amount to mint + pub amount: u64, +} + +/// Decode the ABI-encoded IFT payload from EVM. +/// +/// EVM sends: `abi.encode(bytes32(receiver), uint256(amount))` +/// This is 64 bytes: 32 bytes receiver + 32 bytes amount (big-endian). +pub fn decode_ift_mint_payload(abi_payload: &[u8]) -> Result { + if abi_payload.len() != 64 { + anyhow::bail!( + "IFT payload must be 64 bytes (bytes32 + uint256), got {}", + abi_payload.len() + ); + } + + // First 32 bytes: bytes32 receiver (Solana pubkey) + let receiver_bytes: [u8; 32] = abi_payload[..32] + .try_into() + .context("Failed to extract receiver bytes")?; + let receiver = Pubkey::from(receiver_bytes); + + // Next 32 bytes: uint256 amount (big-endian) + let amount_bytes: [u8; 32] = abi_payload[32..64] + .try_into() + .context("Failed to extract amount bytes")?; + + // Verify the amount fits in u64 (upper 24 bytes must be zero) + if amount_bytes[..24] != [0u8; 24] { + anyhow::bail!("IFT amount exceeds u64 max"); + } + + let amount = u64::from_be_bytes( + amount_bytes[24..32] + .try_into() + .context("Failed to convert amount to u64")?, + ); + + Ok(IFTMintPayload { receiver, amount }) +} + +/// Parameters for building the IFT mint GMP payload. +pub struct BuildIFTMintParams { + /// The IFT program ID on Solana + pub ift_program_id: Pubkey, + /// The GMP program ID on Solana + pub gmp_program_id: Pubkey, + /// The mint address + pub mint: Pubkey, + /// Destination client ID (on Solana, tracking the source EVM chain) + pub dst_client_id: String, + /// Fee payer for the transaction + pub fee_payer: Pubkey, +} + +/// Build a `GmpSolanaPayload` for the IFT mint instruction. +/// +/// This creates the payload that GMP will use to CPI into the IFT program. +/// Account order must match `IFTMint` in `ift_mint.rs`. +pub fn build_ift_mint_gmp_payload( + decoded: &IFTMintPayload, + params: &BuildIFTMintParams, + solana_client: &Arc, +) -> Result { + let mint = params.mint; + let ift_program_id = params.ift_program_id; + + // Derive PDAs + let (app_state_pda, _) = Pubkey::find_program_address(&[IFT_APP_STATE_SEED], &ift_program_id); + + let (app_mint_state_pda, _) = + Pubkey::find_program_address(&[IFT_APP_MINT_STATE_SEED, mint.as_ref()], &ift_program_id); + + let (ift_bridge_pda, _) = Pubkey::find_program_address( + &[ + IFT_BRIDGE_SEED, + mint.as_ref(), + params.dst_client_id.as_bytes(), + ], + &ift_program_id, + ); + + let (mint_authority_pda, _) = + Pubkey::find_program_address(&[MINT_AUTHORITY_SEED, mint.as_ref()], &ift_program_id); + + let receiver_token_account = get_associated_token_address(&decoded.receiver, &mint); + + // Derive GMP account PDA - the counterparty_ift_address is read from the bridge + let counterparty_ift_address = + read_bridge_counterparty_address(solana_client, &ift_bridge_pda)?; + + let client_id = solana_ibc_types::ClientId::new(¶ms.dst_client_id) + .map_err(|e| anyhow::anyhow!("Invalid client ID: {e:?}"))?; + + let gmp_account = solana_ibc_types::GMPAccount::new( + client_id, + counterparty_ift_address + .try_into() + .map_err(|_| anyhow::anyhow!("Invalid counterparty address for Sender"))?, + solana_ibc_types::ics27::Salt::empty(), + ¶ms.gmp_program_id, + ); + let (gmp_account_pda, _) = gmp_account.pda(); + + // Borsh-encode the IFTMintMsg + let mint_msg = IFTMintMsgBorsh { + receiver: decoded.receiver, + amount: decoded.amount, + }; + let data = borsh::to_vec(&mint_msg).context("Failed to Borsh-encode IFTMintMsg")?; + + // Prepend the Anchor discriminator for `ift_mint` + let discriminator = { + use sha2::{Digest, Sha256}; + let mut hasher = Sha256::new(); + hasher.update(b"global:ift_mint"); + let result = hasher.finalize(); + <[u8; 8]>::try_from(&result[..8]).expect("sha256 output is at least 8 bytes") + }; + + let mut instruction_data = discriminator.to_vec(); + instruction_data.extend_from_slice(&data); + + // Build accounts matching IFTMint struct order in ift_mint.rs, + // EXCLUDING the payer (which on_recv_packet injects via payer_position): + // 0: app_state (readonly) + // 1: app_mint_state (writable) + // 2: ift_bridge (readonly) + // 3: mint (writable) + // 4: mint_authority (readonly) + // 5: receiver_token_account (writable) + // 6: receiver_owner (readonly) + // 7: gmp_account (signer via CPI) + // -- payer injected here by on_recv_packet -- + // 8: token_program (readonly) + // 9: associated_token_program (readonly) + // 10: system_program (readonly) + let accounts = vec![ + SolanaAccountMeta { + pubkey: app_state_pda, + is_signer: false, + is_writable: false, + }, + SolanaAccountMeta { + pubkey: app_mint_state_pda, + is_signer: false, + is_writable: true, + }, + SolanaAccountMeta { + pubkey: ift_bridge_pda, + is_signer: false, + is_writable: false, + }, + SolanaAccountMeta { + pubkey: mint, + is_signer: false, + is_writable: true, + }, + SolanaAccountMeta { + pubkey: mint_authority_pda, + is_signer: false, + is_writable: false, + }, + SolanaAccountMeta { + pubkey: receiver_token_account, + is_signer: false, + is_writable: true, + }, + SolanaAccountMeta { + pubkey: decoded.receiver, + is_signer: false, + is_writable: false, + }, + SolanaAccountMeta { + pubkey: gmp_account_pda, + is_signer: true, + is_writable: false, + }, + SolanaAccountMeta { + pubkey: spl_token::id(), + is_signer: false, + is_writable: false, + }, + SolanaAccountMeta { + pubkey: spl_associated_token_account::id(), + is_signer: false, + is_writable: false, + }, + SolanaAccountMeta { + pubkey: solana_sdk::system_program::id(), + is_signer: false, + is_writable: false, + }, + ]; + + // payer_position = 8: on_recv_packet inserts payer at this index, + // shifting token_program et al. to their correct final positions + Ok(GmpSolanaPayload { + data: instruction_data, + accounts, + payer_position: Some(8), + }) +} + +/// Borsh-serializable IFTMintMsg matching the on-chain struct. +#[derive(AnchorSerialize, AnchorDeserialize)] +struct IFTMintMsgBorsh { + pub receiver: Pubkey, + pub amount: u64, +} + +/// Read the counterparty IFT address from the IFTBridge account on-chain. +fn read_bridge_counterparty_address( + solana_client: &Arc, + bridge_pda: &Pubkey, +) -> Result { + let account = solana_client + .get_account_with_commitment(bridge_pda, CommitmentConfig::confirmed()) + .map_err(|e| anyhow::anyhow!("Failed to fetch IFTBridge account: {e}"))? + .value + .ok_or_else(|| anyhow::anyhow!("IFTBridge account not found at {bridge_pda}"))?; + + if account.data.len() < ANCHOR_DISCRIMINATOR_SIZE { + anyhow::bail!("IFTBridge account data too short"); + } + + // Deserialize IFTBridge (skip Anchor discriminator) + let mut data = &account.data[ANCHOR_DISCRIMINATOR_SIZE..]; + + // Manual deserialization to extract counterparty_ift_address: + // IFTBridge layout: version(1) + bump(1) + mint(32) + client_id(4+len) + counterparty_ift_address(4+len) + ... + #[derive(AnchorDeserialize)] + struct IFTBridgePartial { + _version: u8, + _bump: u8, + _mint: Pubkey, + _client_id: String, + counterparty_ift_address: String, + } + + let bridge = IFTBridgePartial::deserialize(&mut data) + .context("Failed to deserialize IFTBridge account")?; + + Ok(bridge.counterparty_ift_address) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_decode_ift_mint_payload() { + // Create a test payload: bytes32 receiver + uint256 amount + let receiver = Pubkey::new_unique(); + let amount: u64 = 1_000_000; + + let mut payload = Vec::with_capacity(64); + payload.extend_from_slice(&receiver.to_bytes()); // 32 bytes + payload.extend_from_slice(&[0u8; 24]); // padding for uint256 + payload.extend_from_slice(&amount.to_be_bytes()); // 8 bytes + + let decoded = decode_ift_mint_payload(&payload).unwrap(); + assert_eq!(decoded.receiver, receiver); + assert_eq!(decoded.amount, amount); + } + + #[test] + fn test_decode_ift_mint_payload_wrong_size() { + let payload = vec![0u8; 63]; // too short + assert!(decode_ift_mint_payload(&payload).is_err()); + } + + #[test] + fn test_decode_ift_mint_payload_amount_overflow() { + let mut payload = vec![0u8; 64]; + // Set first byte of uint256 amount area (upper 24 bytes must be zero for u64) + payload[32] = 1; + assert!(decode_ift_mint_payload(&payload).is_err()); + } +} diff --git a/packages/relayer/modules/eth-to-solana/src/lib.rs b/packages/relayer/modules/eth-to-solana/src/lib.rs new file mode 100644 index 000000000..af971da31 --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/src/lib.rs @@ -0,0 +1,351 @@ +//! One-sided relayer module from Ethereum to Solana. +//! +//! Listens for IBC events on EVM, translates IFT payloads (ABI → GmpSolanaPayload), +//! and builds Solana transactions with chunking, ALT, and attestation support. + +#![deny(clippy::nursery, clippy::pedantic, warnings)] +#![allow(missing_docs, unused_crate_dependencies)] + +pub mod constants; +pub mod gmp; +pub mod ift; +pub mod ift_payload; +pub mod proto; +pub mod tx_builder; + +use std::collections::HashMap; + +use alloy::{ + primitives::{Address, TxHash}, + providers::{Provider, RootProvider}, +}; +use ibc_eureka_relayer_lib::aggregator::Config as AggregatorConfig; +use ibc_eureka_relayer_lib::listener::{eth_eureka, solana, ChainListenerService}; +use ibc_eureka_relayer_lib::service_utils::{ + parse_eth_tx_hashes, parse_solana_tx_hashes, to_tonic_status, +}; +use tonic::{Request, Response}; + +use ibc_eureka_relayer_core::{ + api::{self, relayer_service_server::RelayerService}, + modules::RelayerModule, +}; + +/// The `EthToSolanaRelayerModule` defines the Ethereum to Solana relayer module. +#[derive(Clone, Copy, Debug)] +pub struct EthToSolanaRelayerModule; + +/// The relayer service from Ethereum to Solana. +struct EthToSolanaRelayerModuleService { + /// Source chain listener for Ethereum. + eth_listener: eth_eureka::ChainListener, + /// Target chain listener for Solana. + target_listener: solana::ChainListener, + /// Transaction builder. + tx_builder: EthToSolanaTxBuilder, +} + +/// Enum wrapping transaction builders for different modes. +enum EthToSolanaTxBuilder { + /// Attestation light client mode. + Attested(tx_builder::AttestedTxBuilder), +} + +/// Configuration for the Ethereum to Solana relayer module. +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct EthToSolanaConfig { + /// The ICS26 contract address on Ethereum. + pub ics26_address: Address, + /// The EVM RPC URL. + pub eth_rpc_url: String, + /// The Solana RPC URL. + pub solana_rpc_url: String, + /// The Solana ICS26 router program ID. + pub solana_ics26_program_id: String, + /// The Solana fee payer address. + pub solana_fee_payer: String, + /// Address Lookup Table address for reducing transaction size (optional). + pub solana_alt_address: Option, + /// Transaction builder mode. + pub mode: EthToSolanaTxBuilderMode, +} + +/// Transaction builder mode for Eth to Solana relay. +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum EthToSolanaTxBuilderMode { + /// Attestation light client mode. + Attested(AggregatorConfig), +} + +impl EthToSolanaRelayerModuleService { + async fn new(config: EthToSolanaConfig) -> anyhow::Result { + let provider = RootProvider::builder() + .connect(&config.eth_rpc_url) + .await + .map_err(|e| anyhow::anyhow!("failed to create EVM provider: {e}"))?; + + let eth_listener = eth_eureka::ChainListener::new(config.ics26_address, provider); + + let solana_ics26_program_id = config + .solana_ics26_program_id + .parse() + .map_err(|e| anyhow::anyhow!("Invalid Solana ICS26 program ID: {e}"))?; + + let target_listener = + solana::ChainListener::new(config.solana_rpc_url.clone(), solana_ics26_program_id); + + let fee_payer = config + .solana_fee_payer + .parse() + .map_err(|e| anyhow::anyhow!("Invalid fee payer address: {e}"))?; + + let alt_address = config + .solana_alt_address + .as_ref() + .map(|addr| addr.parse()) + .transpose() + .map_err(|e| anyhow::anyhow!("Invalid ALT address: {e}"))?; + + let tx_builder = match config.mode { + EthToSolanaTxBuilderMode::Attested(aggregator_config) => { + let base_builder = tx_builder::SolanaTxBuilder::new( + target_listener.client().clone(), + solana_ics26_program_id, + fee_payer, + alt_address, + )?; + let attested_builder = tx_builder::AttestedTxBuilder::new( + aggregator_config, + base_builder, + config.ics26_address, + ) + .await + .map_err(|e| anyhow::anyhow!("Failed to create attested tx builder: {e}"))?; + EthToSolanaTxBuilder::Attested(attested_builder) + } + }; + + Ok(Self { + eth_listener, + target_listener, + tx_builder, + }) + } +} + +#[tonic::async_trait] +impl RelayerService for EthToSolanaRelayerModuleService { + async fn info( + &self, + _request: Request, + ) -> Result, tonic::Status> { + tracing::debug!("Handling info request for Eth to Solana"); + + Ok(Response::new(api::InfoResponse { + source_chain: Some(api::Chain { + chain_id: self + .eth_listener + .chain_id() + .await + .map_err(to_tonic_status)?, + ibc_version: "2".to_string(), + ibc_contract: self.tx_builder.ics26_eth_address().to_string(), + }), + target_chain: Some(api::Chain { + chain_id: "solana-localnet".to_string(), + ibc_version: "2".to_string(), + ibc_contract: self.target_listener.ics26_program_id().to_string(), + }), + metadata: HashMap::default(), + })) + } + + async fn relay_by_tx( + &self, + request: Request, + ) -> Result, tonic::Status> { + let inner_req = request.into_inner(); + tracing::debug!( + "Relay request: {} source txs, {} timeout txs", + inner_req.source_tx_ids.len(), + inner_req.timeout_tx_ids.len() + ); + + let eth_tx_hashes = parse_eth_tx_hashes(inner_req.source_tx_ids)?; + let eth_txs = eth_tx_hashes.into_iter().map(TxHash::from).collect(); + let target_txs = parse_solana_tx_hashes(inner_req.timeout_tx_ids)?; + + let src_events = self + .eth_listener + .fetch_tx_events(eth_txs) + .await + .map_err(|e| tonic::Status::from_error(e.into()))?; + + tracing::debug!("Fetched {} src events from EVM", src_events.len()); + + let target_events = self + .target_listener + .fetch_tx_events(target_txs) + .await + .map_err(|e| tonic::Status::from_error(e.into()))?; + + tracing::debug!("Fetched {} target events from Solana", target_events.len()); + + // For timeouts, get the current source chain height where non-membership + // is proven. This ensures the consensus state timestamp is past the timeout. + let timeout_relay_height = if target_events.is_empty() { + None + } else { + Some( + self.eth_listener + .get_block_number() + .await + .map_err(to_tonic_status)?, + ) + }; + + let (packet_txs, update_client) = self + .tx_builder + .relay_events( + src_events, + target_events, + &inner_req.src_client_id, + &inner_req.dst_client_id, + &inner_req.src_packet_sequences, + &inner_req.dst_packet_sequences, + timeout_relay_height, + ) + .await + .map_err(to_tonic_status)?; + + tracing::debug!( + "Relay completed: {} packets{}", + packet_txs.len(), + if update_client.is_some() { + " +update" + } else { + "" + } + ); + + let batch = api::SolanaRelayPacketBatch { + packets: packet_txs, + update_client, + }; + let tx = prost::Message::encode_to_vec(&batch); + + Ok(Response::new(api::RelayByTxResponse { + tx, + address: String::new(), + })) + } + + async fn create_client( + &self, + request: Request, + ) -> Result, tonic::Status> { + tracing::debug!("Handling create client request for Eth to Solana"); + + let inner_req = request.into_inner(); + let tx = self + .tx_builder + .create_client(&inner_req.parameters) + .await + .map_err(|e| tonic::Status::from_error(e.into()))?; + + Ok(Response::new(api::CreateClientResponse { + tx, + address: String::new(), + })) + } + + async fn update_client( + &self, + request: Request, + ) -> Result, tonic::Status> { + tracing::info!("Handling update client request for Eth to Solana"); + + let inner_req = request.into_inner(); + let solana_update_client = self + .tx_builder + .update_client(&inner_req.dst_client_id) + .await + .map_err(|e| tonic::Status::from_error(e.into()))?; + + let tx = prost::Message::encode_to_vec(&solana_update_client); + + Ok(Response::new(api::UpdateClientResponse { + tx, + address: String::new(), + })) + } +} + +#[tonic::async_trait] +impl RelayerModule for EthToSolanaRelayerModule { + fn name(&self) -> &'static str { + "eth_to_solana" + } + + async fn create_service( + &self, + config: serde_json::Value, + ) -> anyhow::Result> { + let config: EthToSolanaConfig = serde_json::from_value(config)?; + let service = EthToSolanaRelayerModuleService::new(config).await?; + Ok(Box::new(service)) + } +} + +impl EthToSolanaTxBuilder { + async fn relay_events( + &self, + src_events: Vec, + target_events: Vec, + src_client_id: &str, + dst_client_id: &str, + src_packet_seqs: &[u64], + dst_packet_seqs: &[u64], + timeout_relay_height: Option, + ) -> anyhow::Result<( + Vec, + Option, + )> { + match self { + Self::Attested(tb) => { + tb.relay_events(tx_builder::RelayParams { + src_events, + dest_events: target_events, + src_client_id: src_client_id.to_string(), + dst_client_id: dst_client_id.to_string(), + src_packet_seqs: src_packet_seqs.to_vec(), + dst_packet_seqs: dst_packet_seqs.to_vec(), + timeout_relay_height, + }) + .await + } + } + } + + async fn create_client(&self, parameters: &HashMap) -> anyhow::Result> { + match self { + Self::Attested(tb) => tb.tx_builder().create_client(parameters).await, + } + } + + async fn update_client( + &self, + dst_client_id: &str, + ) -> anyhow::Result { + match self { + Self::Attested(tb) => tb.update_client(dst_client_id).await, + } + } + + const fn ics26_eth_address(&self) -> &Address { + match self { + Self::Attested(tb) => tb.ics26_eth_address(), + } + } +} diff --git a/packages/relayer/modules/eth-to-solana/src/proto.rs b/packages/relayer/modules/eth-to-solana/src/proto.rs new file mode 100644 index 000000000..d786fa910 --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/src/proto.rs @@ -0,0 +1,5 @@ +//! Protobuf types for GMP relayer +//! +//! Re-exports from the shared solana-ibc-proto crate. + +pub use solana_ibc_proto::{GmpPacketData, GmpSolanaPayload, Protobuf, SolanaAccountMeta}; diff --git a/packages/relayer/modules/eth-to-solana/src/tx_builder.rs b/packages/relayer/modules/eth-to-solana/src/tx_builder.rs new file mode 100644 index 000000000..1c8d6cbcb --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/src/tx_builder.rs @@ -0,0 +1,383 @@ +//! Transaction building for Eth-to-Solana relay. +//! +//! Supports attestation-based light client mode. + +mod attested; +mod packets; +pub(crate) mod payload_translator; + +use std::sync::Arc; + +use anchor_lang::prelude::*; +use anyhow::{Context, Result}; +use solana_client::rpc_client::RpcClient; +use solana_sdk::{ + address_lookup_table::{ + instruction::{create_lookup_table, extend_lookup_table}, + state::AddressLookupTable, + AddressLookupTableAccount, + }, + commitment_config::CommitmentConfig, + compute_budget::ComputeBudgetInstruction, + instruction::Instruction, + message::{v0, VersionedMessage}, + pubkey::Pubkey, + transaction::VersionedTransaction, +}; + +use crate::constants::ANCHOR_DISCRIMINATOR_SIZE; +use ibc_eureka_relayer_lib::events::{EurekaEventWithHeight, SolanaEurekaEventWithHeight}; +use solana_ibc_types::router::{IBCApp, RouterState}; + +pub use attested::AttestedTxBuilder; + +/// Parameters for relaying events between Ethereum and Solana. +pub struct RelayParams { + /// Events from the source chain (Ethereum) + pub src_events: Vec, + /// Events from the destination chain (Solana) + pub dest_events: Vec, + /// Client ID on the source chain + pub src_client_id: String, + /// Client ID on the destination chain + pub dst_client_id: String, + /// Packet sequences from the source chain + pub src_packet_seqs: Vec, + /// Packet sequences from the destination chain + pub dst_packet_seqs: Vec, + /// For timeout packets, the current height from the source chain where + /// non-membership needs to be proven. Required when processing timeouts. + pub timeout_relay_height: Option, +} + +/// Maximum compute units allowed per Solana transaction. +pub(crate) const MAX_COMPUTE_UNIT_LIMIT: u32 = 1_400_000; + +/// Priority fee in micro-lamports per compute unit. +pub(crate) const DEFAULT_PRIORITY_FEE: u64 = 1000; + +/// Solana-side transaction builder. +/// +/// Contains only the Solana RPC and program config needed for building +/// transactions, without any source-chain specific dependencies. +pub struct SolanaTxBuilder { + /// The target Solana RPC client. + pub target_solana_client: Arc, + /// The Solana ICS26 router program ID. + pub solana_ics26_program_id: Pubkey, + /// The fee payer address for transactions. + pub fee_payer: Pubkey, + /// Address Lookup Table address for reducing transaction size. + pub alt_address: Option, +} + +impl SolanaTxBuilder { + /// Creates a new `SolanaTxBuilder`. + pub fn new( + target_solana_client: Arc, + solana_ics26_program_id: Pubkey, + fee_payer: Pubkey, + alt_address: Option, + ) -> Result { + Ok(Self { + target_solana_client, + solana_ics26_program_id, + fee_payer, + alt_address, + }) + } + + /// Resolves the access manager program ID from the router state. + pub(crate) fn resolve_access_manager_program_id(&self) -> Result { + let (router_state_pda, _) = RouterState::pda(self.solana_ics26_program_id); + + let account = self + .target_solana_client + .get_account_with_commitment(&router_state_pda, CommitmentConfig::confirmed()) + .map_err(|e| anyhow::anyhow!("Failed to fetch RouterState account: {e}"))? + .value + .ok_or_else(|| anyhow::anyhow!("Router state account not found"))?; + + if account.data.len() < ANCHOR_DISCRIMINATOR_SIZE { + return Err(anyhow::anyhow!("Account data too short for RouterState")); + } + + let mut data = &account.data[ANCHOR_DISCRIMINATOR_SIZE..]; + let router_state = solana_ibc_types::RouterState::deserialize(&mut data) + .map_err(|e| anyhow::anyhow!("Failed to deserialize RouterState: {e}"))?; + + Ok(router_state.access_manager) + } + + /// Resolve the IBC app program ID for a given port. + pub(crate) fn resolve_port_program_id(&self, port_id: &str) -> Result { + let (ibc_app_account, _) = IBCApp::pda(port_id, self.solana_ics26_program_id); + + let account = self + .target_solana_client + .get_account_with_commitment(&ibc_app_account, CommitmentConfig::confirmed()) + .map_err(|e| { + anyhow::anyhow!("Failed to fetch IBCApp account for port '{port_id}': {e}") + })? + .value + .ok_or_else(|| anyhow::anyhow!("IBCApp account not found for port '{port_id}'"))?; + + if account.data.len() < ANCHOR_DISCRIMINATOR_SIZE { + return Err(anyhow::anyhow!("Account data too short for IBCApp")); + } + + let mut data = &account.data[ANCHOR_DISCRIMINATOR_SIZE..]; + let ibc_app = solana_ibc_types::IBCApp::deserialize(&mut data) + .map_err(|e| anyhow::anyhow!("Failed to deserialize IBCApp: {e}"))?; + + Ok(ibc_app.app_program_id) + } + + /// Resolve the light client program ID. + pub(crate) fn resolve_client_program_id(&self, client_id: &str) -> Result { + let (client_account, _) = + solana_ibc_types::Client::pda(client_id, self.solana_ics26_program_id); + + let account = self + .target_solana_client + .get_account_with_commitment(&client_account, CommitmentConfig::confirmed()) + .map_err(|e| anyhow::anyhow!("Failed to fetch Client account for '{client_id}': {e}"))? + .value + .ok_or_else(|| anyhow::anyhow!("Client account not found for '{client_id}'"))?; + + if account.data.len() < ANCHOR_DISCRIMINATOR_SIZE { + return Err(anyhow::anyhow!("Account data too short for Client")); + } + + let mut data = &account.data[ANCHOR_DISCRIMINATOR_SIZE..]; + let client = solana_ibc_types::ClientAccount::deserialize(&mut data) + .map_err(|e| anyhow::anyhow!("Failed to deserialize Client: {e}"))?; + + Ok(client.client_program_id) + } + + /// Fetch the attestation light client state from Solana. + pub(crate) fn attestation_client_state( + &self, + light_client_program_id: Pubkey, + ) -> Result { + use solana_ibc_types::attestation::ClientState as AttestationClientState; + + let (client_state_pda, _) = AttestationClientState::pda(light_client_program_id); + + let account = self + .target_solana_client + .get_account_with_commitment(&client_state_pda, CommitmentConfig::confirmed()) + .context("Failed to fetch attestation client state account")? + .value + .ok_or_else(|| anyhow::anyhow!("Attestation client state account not found"))?; + + let mut data = &account.data[ANCHOR_DISCRIMINATOR_SIZE..]; + let client_state = AttestationClientState::deserialize(&mut data) + .context("Failed to deserialize attestation client state")?; + + Ok(client_state) + } + + /// Fetch the minimum required signatures from the attestation light client. + pub(crate) fn attestation_client_min_sigs( + &self, + light_client_program_id: Pubkey, + ) -> Result { + Ok(self + .attestation_client_state(light_client_program_id)? + .min_required_sigs as usize) + } + + /// Fetch the attestation consensus state timestamp at a given height (seconds). + pub(crate) fn attestation_consensus_state_timestamp_secs( + &self, + height: u64, + light_client_program_id: Pubkey, + ) -> Result { + use solana_ibc_types::attestation::ConsensusState as AttestationConsensusState; + let (consensus_state_pda, _) = + AttestationConsensusState::pda(height, light_client_program_id); + + let account = self + .target_solana_client + .get_account_with_commitment(&consensus_state_pda, CommitmentConfig::confirmed()) + .context("Failed to fetch attestation consensus state account")? + .value + .ok_or_else(|| anyhow::anyhow!("Attestation consensus state account not found"))?; + + let mut data = &account.data[ANCHOR_DISCRIMINATOR_SIZE..]; + let consensus_state = AttestationConsensusState::deserialize(&mut data) + .context("Failed to deserialize attestation consensus state")?; + + Ok(consensus_state.timestamp) + } + + pub(crate) fn create_tx_bytes(&self, instructions: &[Instruction]) -> Result> { + if instructions.is_empty() { + anyhow::bail!("No instructions to execute on Solana"); + } + + let recent_blockhash = self.get_recent_blockhash()?; + + let alt_addresses = match self.alt_address { + Some(alt_address) => self.fetch_alt_addresses(alt_address)?, + None => vec![], + }; + + self.create_v0_tx(instructions, recent_blockhash, alt_addresses) + } + + pub(crate) fn create_tx_bytes_with_alt( + &self, + instructions: &[Instruction], + alt_address: Pubkey, + alt_addresses: Vec, + ) -> Result> { + let recent_blockhash = self.get_recent_blockhash()?; + + let alt_account = AddressLookupTableAccount { + key: alt_address, + addresses: alt_addresses, + }; + + let v0_message = + self.compile_v0_message_with_alt(instructions, recent_blockhash, alt_account)?; + + Self::serialize_v0_transaction(v0_message) + } + + pub(crate) fn get_recent_blockhash(&self) -> Result { + self.target_solana_client + .get_latest_blockhash() + .map_err(|e| anyhow::anyhow!("Failed to get blockhash: {e}")) + } + + pub(crate) fn create_v0_tx( + &self, + instructions: &[Instruction], + recent_blockhash: solana_sdk::hash::Hash, + alt_addresses: Vec, + ) -> Result> { + let v0_message = if alt_addresses.is_empty() { + self.compile_v0_message(instructions, recent_blockhash)? + } else { + let alt_account = AddressLookupTableAccount { + key: self.alt_address.expect("ALT address should be set"), + addresses: alt_addresses, + }; + self.compile_v0_message_with_alt(instructions, recent_blockhash, alt_account)? + }; + + Self::serialize_v0_transaction(v0_message) + } + + pub(crate) fn compile_v0_message( + &self, + instructions: &[Instruction], + recent_blockhash: solana_sdk::hash::Hash, + ) -> Result { + v0::Message::try_compile(&self.fee_payer, instructions, &[], recent_blockhash) + .map_err(|e| anyhow::anyhow!("Failed to compile v0 message: {e}")) + } + + pub(crate) fn compile_v0_message_with_alt( + &self, + instructions: &[Instruction], + recent_blockhash: solana_sdk::hash::Hash, + alt_account: AddressLookupTableAccount, + ) -> Result { + v0::Message::try_compile( + &self.fee_payer, + instructions, + &[alt_account], + recent_blockhash, + ) + .map_err(|e| anyhow::anyhow!("Failed to compile v0 message with ALT: {e}")) + } + + pub(crate) fn serialize_v0_transaction(v0_message: v0::Message) -> Result> { + let num_signatures = v0_message.header.num_required_signatures as usize; + let versioned_tx = VersionedTransaction { + signatures: vec![solana_sdk::signature::Signature::default(); num_signatures], + message: VersionedMessage::V0(v0_message), + }; + let serialized_tx = bincode::serialize(&versioned_tx)?; + Ok(serialized_tx) + } + + pub(crate) fn fetch_alt_addresses(&self, alt_address: Pubkey) -> Result> { + let alt_account = self + .target_solana_client + .get_account_with_commitment(&alt_address, CommitmentConfig::confirmed()) + .map_err(|e| anyhow::anyhow!("Failed to fetch ALT account {alt_address}: {e}"))? + .value + .ok_or_else(|| anyhow::anyhow!("ALT account {alt_address} not found"))?; + + let lookup_table = AddressLookupTable::deserialize(&alt_account.data) + .map_err(|e| anyhow::anyhow!("Failed to deserialize ALT: {e}"))?; + + Ok(lookup_table.addresses.to_vec()) + } + + pub(crate) fn build_create_alt_tx(&self, slot: u64) -> Result> { + let (create_ix, _alt_address) = create_lookup_table(self.fee_payer, self.fee_payer, slot); + self.create_tx_bytes(&[create_ix]) + } + + pub(crate) fn build_extend_alt_tx(&self, slot: u64, accounts: Vec) -> Result> { + let (alt_address, _) = derive_alt_address(slot, self.fee_payer); + let extend_ix = + extend_lookup_table(alt_address, self.fee_payer, Some(self.fee_payer), accounts); + self.create_tx_bytes(&[extend_ix]) + } + + pub(crate) fn extend_compute_ix() -> Vec { + let compute_budget_ix = + ComputeBudgetInstruction::set_compute_unit_limit(MAX_COMPUTE_UNIT_LIMIT); + let priority_fee_ix = + ComputeBudgetInstruction::set_compute_unit_price(DEFAULT_PRIORITY_FEE); + vec![compute_budget_ix, priority_fee_ix] + } + + /// Create a client on Solana (stub for attestation mode). + pub async fn create_client( + &self, + _parameters: &std::collections::HashMap, + ) -> Result> { + anyhow::bail!("create_client not yet implemented for eth-to-solana") + } +} + +impl ibc_eureka_relayer_lib::utils::solana_attested::SolanaAttestationTxBuilder + for SolanaTxBuilder +{ + fn resolve_client_program_id(&self, client_id: &str) -> Result { + self.resolve_client_program_id(client_id) + } + + fn attestation_client_min_sigs(&self, program_id: Pubkey) -> Result { + self.attestation_client_min_sigs(program_id) + } + + fn resolve_access_manager_program_id(&self) -> Result { + self.resolve_access_manager_program_id() + } + + fn fee_payer(&self) -> Pubkey { + self.fee_payer + } + + fn create_tx_bytes(&self, instructions: &[Instruction]) -> Result> { + self.create_tx_bytes(instructions) + } +} + +/// Derive ALT address from current slot and authority. +#[must_use] +pub fn derive_alt_address(slot: u64, authority: Pubkey) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[authority.as_ref(), &slot.to_le_bytes()], + &solana_sdk::address_lookup_table::program::id(), + ) +} diff --git a/packages/relayer/modules/eth-to-solana/src/tx_builder/attested.rs b/packages/relayer/modules/eth-to-solana/src/tx_builder/attested.rs new file mode 100644 index 000000000..05c4ca179 --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/src/tx_builder/attested.rs @@ -0,0 +1,313 @@ +//! Attestation-based transaction builder for Eth-to-Solana relay. + +use std::time::Duration; + +use alloy::primitives::Address; +use anyhow::{Context, Result}; +use ibc_eureka_relayer_lib::{ + aggregator::{Aggregator, Config as AggregatorConfig}, + events::{solana::SolanaEurekaEvent, SolanaEurekaEventWithHeight}, + utils::{ + cosmos as cosmos_utils, + solana::{ + self as solana_utils, ibc_to_solana_ack_packet, ibc_to_solana_recv_packet, + target_events_to_timeout_msgs, + }, + solana_attested, wait_for_condition, + }, +}; +use ibc_proto_eureka::ibc::core::{ + channel::v2::{MsgAcknowledgement, MsgRecvPacket}, + client::v1::Height, +}; +use solana_sdk::commitment_config::CommitmentConfig; + +use ibc_eureka_relayer_core::api::{self, SolanaPacketTxs}; + +use super::{RelayParams, SolanaTxBuilder}; + +/// Transaction builder using attestation proofs for Eth-to-Solana relay. +pub struct AttestedTxBuilder { + aggregator: Aggregator, + tx_builder: SolanaTxBuilder, + ics26_eth_address: Address, +} + +impl AttestedTxBuilder { + /// Create a new [`AttestedTxBuilder`] instance. + pub async fn new( + aggregator_config: AggregatorConfig, + tx_builder: SolanaTxBuilder, + ics26_eth_address: Address, + ) -> Result { + let aggregator = Aggregator::from_config(aggregator_config).await?; + Ok(Self { + aggregator, + tx_builder, + ics26_eth_address, + }) + } + + /// Get the inner `SolanaTxBuilder` reference. + pub fn tx_builder(&self) -> &SolanaTxBuilder { + &self.tx_builder + } + + /// Get the ICS26 Ethereum address. + pub const fn ics26_eth_address(&self) -> &Address { + &self.ics26_eth_address + } + + /// Relay events from Ethereum to Solana using attestations. + pub async fn relay_events( + &self, + params: RelayParams, + ) -> Result<(Vec, Option)> { + tracing::info!( + "Building attested relay: {} src events, {} target events", + params.src_events.len(), + params.dest_events.len() + ); + + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH)? + .as_secs(); + + let (mut recv_msgs, mut ack_msgs) = cosmos_utils::src_events_to_recv_and_ack_msgs( + params.src_events.clone(), + ¶ms.src_client_id, + ¶ms.dst_client_id, + ¶ms.src_packet_seqs, + ¶ms.dst_packet_seqs, + "", + now, + ); + + // Get Solana slot for timeout packet handling + let slot = self + .tx_builder + .target_solana_client + .get_slot_with_commitment(CommitmentConfig::finalized()) + .map_err(|e| anyhow::anyhow!("Failed to get Solana slot: {e}"))?; + + // Extract max timeout timestamp before consuming dest_events + let max_timeout_ts = max_timeout_timestamp(¶ms.dest_events); + + // Build timeout messages from destination events + let mut timeout_msgs = target_events_to_timeout_msgs( + params.dest_events, + ¶ms.src_client_id, + ¶ms.dst_client_id, + ¶ms.dst_packet_seqs, + slot, + now, + ); + + if recv_msgs.is_empty() && ack_msgs.is_empty() && timeout_msgs.is_empty() { + tracing::info!("No packets to relay"); + return Ok((vec![], None)); + } + + // Resolve light client program ID for the destination client + let light_client_program_id = self + .tx_builder + .resolve_client_program_id(¶ms.dst_client_id)?; + + // Get current attestation client state + let client_state = self + .tx_builder + .attestation_client_state(light_client_program_id)?; + let current_height = client_state.latest_height; + + let max_height = params + .src_events + .iter() + .map(|e| e.height) + .max() + .unwrap_or(0); + let required_height = max_height.saturating_add(1); + + let needs_height_update = current_height < required_height; + + // Check if timestamp update is needed for timeouts + let consensus_ts = max_timeout_ts.and_then(|_| { + self.tx_builder + .attestation_consensus_state_timestamp_secs(current_height, light_client_program_id) + .ok() + }); + + let needs_timestamp_update = matches!( + (consensus_ts, max_timeout_ts), + (Some(cs_ts), Some(timeout_ts)) if cs_ts < timeout_ts + ); + + let needs_update = needs_height_update || needs_timestamp_update; + + // For timeouts, use the source chain height (where non-membership is proven). + // This ensures the consensus state timestamp is past the packet's timeout. + let proof_height = if needs_update { + let min_height = required_height.max(current_height.saturating_add(1)); + let target = params + .timeout_relay_height + .map_or(min_height, |h| h.max(min_height)); + + tracing::info!( + "Attestation client at height {}, waiting for aggregator to finalize height {}", + current_height, + target + ); + + wait_for_condition( + Duration::from_secs(25 * 60), + Duration::from_secs(1), + || async { + let finalized_height = self.aggregator.get_latest_height().await?; + Ok(finalized_height >= target) + }, + ) + .await + .context("Timeout waiting for aggregator to finalize height")?; + + tracing::info!("Aggregator has finalized height {}", target); + target + } else { + current_height + }; + + let min_sigs = client_state.min_required_sigs as usize; + + let target_height = Height { + revision_number: 0, + revision_height: proof_height, + }; + + solana_attested::inject_solana_attestor_proofs( + &self.aggregator, + &mut recv_msgs, + &mut ack_msgs, + &mut timeout_msgs, + &target_height, + min_sigs, + ) + .await?; + + // Build update_client transaction only if needed + let update_client = if needs_update { + let result = solana_attested::build_attestation_update_client_tx( + &self.aggregator, + &self.tx_builder, + ¶ms.dst_client_id, + proof_height, + ) + .await?; + Some(api::SolanaUpdateClient { + chunk_txs: vec![], + alt_create_tx: vec![], + alt_extend_txs: vec![], + assembly_tx: result.assembly_tx, + target_height: result.target_height, + cleanup_tx: vec![], + }) + } else { + None + }; + + let packets = self + .build_packet_transactions(recv_msgs, ack_msgs, timeout_msgs) + .await?; + + Ok((packets, update_client)) + } + + /// Update the attestation light client to the latest height. + pub async fn update_client(&self, dst_client_id: &str) -> Result { + let result = solana_attested::update_attestation_client_tx( + &self.aggregator, + &self.tx_builder, + dst_client_id, + ) + .await?; + Ok(api::SolanaUpdateClient { + chunk_txs: vec![], + alt_create_tx: vec![], + alt_extend_txs: vec![], + assembly_tx: result.assembly_tx, + target_height: result.target_height, + cleanup_tx: vec![], + }) + } + + async fn build_packet_transactions( + &self, + recv_msgs: Vec, + ack_msgs: Vec, + timeout_msgs: Vec, + ) -> Result> { + let mut results = Vec::new(); + + for msg in recv_msgs { + // Build hint for ABI-encoded payloads (original payload stays intact) + let abi_hint = self.tx_builder.build_abi_hint_if_needed(&msg)?; + + let recv_with_chunks = ibc_to_solana_recv_packet(msg)?; + let mut packet_txs = self.tx_builder.build_recv_packet_chunked( + &recv_with_chunks.msg, + &recv_with_chunks.payload_chunks, + &recv_with_chunks.proof_chunks, + abi_hint.as_ref(), + )?; + + // Prepend store_hint tx before chunk uploads for ABI payloads + if let Some(hint) = &abi_hint { + let mut hint_instructions = super::SolanaTxBuilder::extend_compute_ix(); + hint_instructions.push(hint.store_hint_instruction.clone()); + let hint_tx = self.tx_builder.create_tx_bytes(&hint_instructions)?; + packet_txs.chunks.insert(0, hint_tx); + } + + results.push(packet_txs); + } + + for msg in ack_msgs { + let ack_with_chunks = ibc_to_solana_ack_packet(msg)?; + let packet_txs = self + .tx_builder + .build_ack_packet_chunked( + &ack_with_chunks.msg, + &ack_with_chunks.payload_chunks, + &ack_with_chunks.proof_chunks, + ) + .await?; + results.push(packet_txs); + } + + for timeout in timeout_msgs { + tracing::info!( + "Building timeout packet for sequence {}", + timeout.msg.packet.sequence + ); + let packet_txs = self.tx_builder.build_timeout_packet_chunked( + &timeout.msg, + &timeout.payload_chunks, + &timeout.proof_chunks, + )?; + results.push(packet_txs); + } + + Ok(results) + } +} + +/// Extract maximum timeout timestamp from Solana destination events. +/// +/// Looks at `SendPacket` events to find the maximum timeout timestamp, +/// which indicates the latest timeout we may need to prove against. +fn max_timeout_timestamp(events: &[SolanaEurekaEventWithHeight]) -> Option { + events + .iter() + .filter_map(|e| match &e.event { + SolanaEurekaEvent::SendPacket(send) => u64::try_from(send.timeout_timestamp).ok(), + SolanaEurekaEvent::WriteAcknowledgement(_) => None, + }) + .max() +} diff --git a/packages/relayer/modules/eth-to-solana/src/tx_builder/packets.rs b/packages/relayer/modules/eth-to-solana/src/tx_builder/packets.rs new file mode 100644 index 000000000..014e71c5e --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/src/tx_builder/packets.rs @@ -0,0 +1,1009 @@ +//! Packet instruction builders and chunking for recv, ack, and timeout packets. + +use std::collections::HashSet; + +use anchor_lang::prelude::*; +use anyhow::Result; +use solana_sdk::{ + commitment_config::CommitmentConfig, + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, +}; + +use ibc_eureka_relayer_core::api::SolanaPacketTxs; +use solana_ibc_constants::CHUNK_DATA_SIZE; +use solana_ibc_types::{ + router::{ + router_instructions, Client, Commitment, IBCApp, IBCAppState, MsgCleanupChunks, + PayloadChunk, ProofChunk, RouterState, + }, + AccessManager, MsgAckPacket, MsgRecvPacket, MsgTimeoutPacket, MsgUploadChunk, +}; + +use solana_ibc_types::attestation::{ + ClientState as AttestationClientState, ConsensusState as AttestationConsensusState, +}; + +use crate::constants::ANCHOR_DISCRIMINATOR_SIZE; +use crate::{gmp, ift}; + +use super::derive_alt_address; + +/// Result type for ALT transaction building: (`create_alt_tx`, `extend_alt_txs`, `packet_txs`) +type AltBuildResult = (Vec, Vec, Vec>); + +/// Maximum accounts that fit in a Solana transaction without ALT +const MAX_ACCOUNTS_WITHOUT_ALT: usize = 20; + +/// Batch size for ALT extension transactions +const ALT_EXTEND_BATCH_SIZE: usize = 20; + +/// Derives client state and consensus state PDAs based on client type. +fn derive_light_client_pdas( + client_id: &str, + height: u64, + light_client_program_id: Pubkey, +) -> Result<(Pubkey, Pubkey)> { + match solana_ibc_constants::client_type_from_id(client_id) { + Some(solana_ibc_constants::CLIENT_TYPE_ATTESTATION) => { + let (cs, _) = AttestationClientState::pda(light_client_program_id); + let (cons, _) = AttestationConsensusState::pda(height, light_client_program_id); + Ok((cs, cons)) + } + Some(solana_ibc_constants::CLIENT_TYPE_TENDERMINT) => { + // Tendermint clients require chain_id for PDA derivation which is not + // available in eth-to-solana relay. This should not occur in practice. + anyhow::bail!( + "Tendermint client type not supported for eth-to-solana relay (client: {client_id})" + ) + } + _ => { + anyhow::bail!("Unknown client type for client ID: {client_id}") + } + } +} + +/// Extracted payload info for recv packet processing. +struct RecvPayloadInfo<'a> { + dest_port: &'a str, + encoding: &'a str, + value: &'a [u8], +} + +/// Extract payload info from either `packet.payloads` or metadata + `payload_data`. +fn extract_recv_payload_info<'a>( + msg: &'a MsgRecvPacket, + payload_data: &'a [Vec], +) -> Result> { + if msg.packet.payloads.is_empty() { + let [metadata] = msg.payloads.as_slice() else { + anyhow::bail!("Expected exactly one recv packet payload metadata element"); + }; + let value = payload_data + .first() + .ok_or_else(|| anyhow::anyhow!("Missing payload data"))? + .as_slice(); + Ok(RecvPayloadInfo { + dest_port: &metadata.dest_port, + encoding: &metadata.encoding, + value, + }) + } else { + let [payload] = msg.packet.payloads.as_slice() else { + anyhow::bail!("Expected exactly one recv packet payload element"); + }; + Ok(RecvPayloadInfo { + dest_port: &payload.dest_port, + encoding: &payload.encoding, + value: &payload.value, + }) + } +} + +/// Extract `source_port` from either inline payloads or chunked metadata. +fn extract_source_port<'a>( + packet_payloads: &'a [solana_ibc_types::Payload], + metadata_payloads: &'a [solana_ibc_types::router::PayloadMetadata], + context: &str, +) -> Result<&'a str> { + if !packet_payloads.is_empty() { + let [payload] = packet_payloads else { + anyhow::bail!( + "Expected exactly one {context} packet payload element, got {}", + packet_payloads.len() + ); + }; + Ok(&payload.source_port) + } else if !metadata_payloads.is_empty() { + let [payload_meta] = metadata_payloads else { + anyhow::bail!( + "Expected exactly one {context} packet payload metadata element, got {}", + metadata_payloads.len() + ); + }; + Ok(&payload_meta.source_port) + } else { + anyhow::bail!("No payload data found in either packet.payloads or payloads metadata"); + } +} + +// --------------------------------------------------------------------------- +// Instruction builders +// --------------------------------------------------------------------------- + +impl super::SolanaTxBuilder { + fn build_recv_packet_instruction( + &self, + msg: &MsgRecvPacket, + chunk_accounts: Vec, + payload_data: &[Vec], + abi_hint: Option<&super::payload_translator::AbiHintInfo>, + ) -> Result { + let payload_info = extract_recv_payload_info(msg, payload_data)?; + + let (router_state, _) = RouterState::pda(self.solana_ics26_program_id); + let (ibc_app, _) = IBCApp::pda(payload_info.dest_port, self.solana_ics26_program_id); + let (packet_receipt, _) = Commitment::packet_receipt_pda( + &msg.packet.dest_client, + msg.packet.sequence, + self.solana_ics26_program_id, + ); + let (packet_ack, _) = Commitment::packet_ack_pda( + &msg.packet.dest_client, + msg.packet.sequence, + self.solana_ics26_program_id, + ); + let (client, _) = Client::pda(&msg.packet.dest_client, self.solana_ics26_program_id); + + let light_client_program_id = self.resolve_client_program_id(&msg.packet.dest_client)?; + let (client_state, consensus_state) = derive_light_client_pdas( + &msg.packet.dest_client, + msg.proof.height, + light_client_program_id, + )?; + + let ibc_app_program_id = self.resolve_port_program_id(payload_info.dest_port)?; + let (ibc_app_state, _) = IBCAppState::pda(ibc_app_program_id); + let access_manager_program_id = self.resolve_access_manager_program_id()?; + let (access_manager, _) = AccessManager::pda(access_manager_program_id); + + let mut accounts = vec![ + AccountMeta::new_readonly(router_state, false), + AccountMeta::new_readonly(access_manager, false), + AccountMeta::new_readonly(ibc_app, false), + AccountMeta::new(packet_receipt, false), + AccountMeta::new(packet_ack, false), + AccountMeta::new_readonly(ibc_app_program_id, false), + AccountMeta::new(ibc_app_state, false), + AccountMeta::new(self.fee_payer, true), + AccountMeta::new_readonly(solana_sdk::system_program::id(), false), + AccountMeta::new_readonly(solana_sdk::sysvar::instructions::id(), false), + AccountMeta::new_readonly(client, false), + AccountMeta::new_readonly(light_client_program_id, false), + AccountMeta::new_readonly(client_state, false), + AccountMeta::new_readonly(consensus_state, false), + ]; + accounts.extend( + chunk_accounts + .into_iter() + .map(|a| AccountMeta::new(a, false)), + ); + + if let Some(hint) = abi_hint { + // For ABI payloads, use pre-built GMP accounts with hint PDA + accounts.extend(hint.gmp_accounts.clone()); + } else { + // For protobuf payloads, extract GMP accounts normally + let gmp_accounts = gmp::extract_gmp_accounts( + payload_info.dest_port, + payload_info.encoding, + payload_info.value, + &msg.packet.dest_client, + ibc_app_program_id, + )?; + accounts.extend(gmp_accounts); + } + + let mut data = router_instructions::recv_packet_discriminator().to_vec(); + data.extend_from_slice(&msg.try_to_vec()?); + + Ok(Instruction { + program_id: self.solana_ics26_program_id, + accounts, + data, + }) + } + + async fn build_ack_packet_instruction( + &self, + msg: &MsgAckPacket, + chunk_accounts: Vec, + ) -> Result { + let source_port = extract_source_port(&msg.packet.payloads, &msg.payloads, "ack")?; + + let (router_state, _) = RouterState::pda(self.solana_ics26_program_id); + let (ibc_app_pda, _) = IBCApp::pda(source_port, self.solana_ics26_program_id); + + let ibc_app_account = self + .target_solana_client + .get_account_with_commitment(&ibc_app_pda, CommitmentConfig::confirmed()) + .map_err(|e| anyhow::anyhow!("Failed to get IBC app account: {e}"))? + .value + .ok_or_else(|| anyhow::anyhow!("IBC app account not found"))?; + + if ibc_app_account.data.len() < ANCHOR_DISCRIMINATOR_SIZE { + anyhow::bail!("Account data too short for IBCApp account"); + } + + let mut account_data = &ibc_app_account.data[ANCHOR_DISCRIMINATOR_SIZE..]; + let ibc_app = solana_ibc_types::IBCApp::deserialize(&mut account_data) + .map_err(|e| anyhow::anyhow!("Failed to deserialize IBCApp account: {e}"))?; + let ibc_app_program = ibc_app.app_program_id; + + let (app_state, _) = IBCAppState::pda(ibc_app_program); + let (packet_commitment, _) = Commitment::packet_commitment_pda( + &msg.packet.source_client, + msg.packet.sequence, + self.solana_ics26_program_id, + ); + let (client, _) = Client::pda(&msg.packet.source_client, self.solana_ics26_program_id); + + let light_client_program_id = self.resolve_client_program_id(&msg.packet.source_client)?; + let (client_state, consensus_state) = derive_light_client_pdas( + &msg.packet.source_client, + msg.proof.height, + light_client_program_id, + )?; + + let access_manager_program_id = self.resolve_access_manager_program_id()?; + let (access_manager, _) = AccessManager::pda(access_manager_program_id); + + let mut accounts = vec![ + AccountMeta::new_readonly(router_state, false), + AccountMeta::new_readonly(access_manager, false), + AccountMeta::new_readonly(ibc_app_pda, false), + AccountMeta::new(packet_commitment, false), + AccountMeta::new_readonly(ibc_app_program, false), + AccountMeta::new(app_state, false), + AccountMeta::new(self.fee_payer, true), + AccountMeta::new_readonly(solana_sdk::system_program::id(), false), + AccountMeta::new_readonly(solana_sdk::sysvar::instructions::id(), false), + AccountMeta::new_readonly(client, false), + AccountMeta::new_readonly(light_client_program_id, false), + AccountMeta::new_readonly(client_state, false), + AccountMeta::new_readonly(consensus_state, false), + ]; + accounts.extend( + chunk_accounts + .into_iter() + .map(|a| AccountMeta::new(a, false)), + ); + + if let Some(result_pda) = gmp::find_gmp_result_pda( + source_port, + &msg.packet.source_client, + msg.packet.sequence, + ibc_app_program, + ) { + accounts.push(AccountMeta::new(result_pda, false)); + } + + let mut data = router_instructions::ack_packet_discriminator().to_vec(); + data.extend_from_slice(&msg.try_to_vec()?); + + Ok(Instruction { + program_id: self.solana_ics26_program_id, + accounts, + data, + }) + } + + fn build_timeout_packet_instruction( + &self, + msg: &MsgTimeoutPacket, + chunk_accounts: Vec, + ) -> Result { + let source_port = extract_source_port(&msg.packet.payloads, &msg.payloads, "timeout")?; + + let (router_state, _) = RouterState::pda(self.solana_ics26_program_id); + let (ibc_app, _) = IBCApp::pda(source_port, self.solana_ics26_program_id); + let (packet_commitment, _) = Commitment::packet_commitment_pda( + &msg.packet.source_client, + msg.packet.sequence, + self.solana_ics26_program_id, + ); + + let ibc_app_program_id = self.resolve_port_program_id(source_port)?; + let (ibc_app_state, _) = IBCAppState::pda(ibc_app_program_id); + let (client, _) = Client::pda(&msg.packet.source_client, self.solana_ics26_program_id); + + let light_client_program_id = self.resolve_client_program_id(&msg.packet.source_client)?; + let (client_state, consensus_state) = derive_light_client_pdas( + &msg.packet.source_client, + msg.proof.height, + light_client_program_id, + )?; + + let access_manager_program_id = self.resolve_access_manager_program_id()?; + let (access_manager, _) = AccessManager::pda(access_manager_program_id); + + let mut accounts = vec![ + AccountMeta::new_readonly(router_state, false), + AccountMeta::new_readonly(access_manager, false), + AccountMeta::new_readonly(ibc_app, false), + AccountMeta::new(packet_commitment, false), + AccountMeta::new_readonly(ibc_app_program_id, false), + AccountMeta::new(ibc_app_state, false), + AccountMeta::new(self.fee_payer, true), + AccountMeta::new_readonly(solana_sdk::system_program::id(), false), + AccountMeta::new_readonly(solana_sdk::sysvar::instructions::id(), false), + AccountMeta::new_readonly(client, false), + AccountMeta::new_readonly(light_client_program_id, false), + AccountMeta::new_readonly(client_state, false), + AccountMeta::new_readonly(consensus_state, false), + ]; + accounts.extend( + chunk_accounts + .into_iter() + .map(|a| AccountMeta::new(a, false)), + ); + + if let Some(result_pda) = gmp::find_gmp_result_pda( + source_port, + &msg.packet.source_client, + msg.packet.sequence, + ibc_app_program_id, + ) { + accounts.push(AccountMeta::new(result_pda, false)); + } + + let mut data = router_instructions::timeout_packet_discriminator().to_vec(); + data.extend_from_slice(&msg.try_to_vec()?); + + Ok(Instruction { + program_id: self.solana_ics26_program_id, + accounts, + data, + }) + } +} + +// --------------------------------------------------------------------------- +// Chunking and high-level packet builders +// --------------------------------------------------------------------------- + +impl super::SolanaTxBuilder { + fn split_into_chunks(data: &[u8]) -> Vec> { + data.chunks(CHUNK_DATA_SIZE).map(<[u8]>::to_vec).collect() + } + + fn build_upload_payload_chunk_instruction( + &self, + client_id: &str, + sequence: u64, + payload_index: u8, + chunk_index: u8, + chunk_data: Vec, + ) -> Result { + let msg = MsgUploadChunk { + client_id: client_id.to_string(), + sequence, + payload_index, + chunk_index, + chunk_data, + }; + + let (router_state, _) = RouterState::pda(self.solana_ics26_program_id); + let access_manager_program_id = self.resolve_access_manager_program_id()?; + let (access_manager, _) = AccessManager::pda(access_manager_program_id); + + let (chunk_pda, _) = PayloadChunk::pda( + self.fee_payer, + client_id, + sequence, + payload_index, + chunk_index, + self.solana_ics26_program_id, + ); + + let accounts = vec![ + AccountMeta::new_readonly(router_state, false), + AccountMeta::new_readonly(access_manager, false), + AccountMeta::new(chunk_pda, false), + AccountMeta::new(self.fee_payer, true), + AccountMeta::new_readonly(solana_sdk::system_program::id(), false), + AccountMeta::new_readonly(solana_sdk::sysvar::instructions::id(), false), + ]; + + let mut data = router_instructions::upload_payload_chunk_discriminator().to_vec(); + data.extend_from_slice(&msg.try_to_vec()?); + + Ok(Instruction { + program_id: self.solana_ics26_program_id, + accounts, + data, + }) + } + + fn build_upload_proof_chunk_instruction( + &self, + client_id: &str, + sequence: u64, + chunk_index: u8, + chunk_data: Vec, + ) -> Result { + let msg = MsgUploadChunk { + client_id: client_id.to_string(), + sequence, + payload_index: 0, + chunk_index, + chunk_data, + }; + + let (router_state, _) = RouterState::pda(self.solana_ics26_program_id); + let access_manager_program_id = self.resolve_access_manager_program_id()?; + let (access_manager, _) = AccessManager::pda(access_manager_program_id); + + let (chunk_pda, _) = ProofChunk::pda( + self.fee_payer, + client_id, + sequence, + chunk_index, + self.solana_ics26_program_id, + ); + + let accounts = vec![ + AccountMeta::new_readonly(router_state, false), + AccountMeta::new_readonly(access_manager, false), + AccountMeta::new(chunk_pda, false), + AccountMeta::new(self.fee_payer, true), + AccountMeta::new_readonly(solana_sdk::system_program::id(), false), + AccountMeta::new_readonly(solana_sdk::sysvar::instructions::id(), false), + ]; + + let mut data = router_instructions::upload_proof_chunk_discriminator().to_vec(); + data.extend_from_slice(&msg.try_to_vec()?); + + Ok(Instruction { + program_id: self.solana_ics26_program_id, + accounts, + data, + }) + } + + fn build_packet_chunk_txs( + &self, + client_id: &str, + sequence: u64, + msg_payloads: &[solana_ibc_types::router::PayloadMetadata], + payload_data: &[Vec], + proof_total_chunks: u8, + proof_data: &[u8], + ) -> Result>> { + let mut chunk_txs = Vec::new(); + + for (payload_idx, data) in payload_data.iter().enumerate() { + let payload_index = u8::try_from(payload_idx) + .map_err(|_| anyhow::anyhow!("Payload index exceeds u8 max"))?; + + if payload_idx < msg_payloads.len() && msg_payloads[payload_idx].total_chunks > 0 { + let chunks = Self::split_into_chunks(data); + for (chunk_idx, chunk_data) in chunks.iter().enumerate() { + let chunk_index = u8::try_from(chunk_idx) + .map_err(|_| anyhow::anyhow!("Chunk index exceeds u8 max"))?; + + let instruction = self.build_upload_payload_chunk_instruction( + client_id, + sequence, + payload_index, + chunk_index, + chunk_data.clone(), + )?; + + chunk_txs.push(self.create_tx_bytes(&[instruction])?); + } + } + } + + if proof_total_chunks > 0 { + let chunks = Self::split_into_chunks(proof_data); + for (chunk_idx, chunk_data) in chunks.iter().enumerate() { + let chunk_index = u8::try_from(chunk_idx) + .map_err(|_| anyhow::anyhow!("Chunk index exceeds u8 max"))?; + + let instruction = self.build_upload_proof_chunk_instruction( + client_id, + sequence, + chunk_index, + chunk_data.clone(), + )?; + + chunk_txs.push(self.create_tx_bytes(&[instruction])?); + } + } + + Ok(chunk_txs) + } + + fn build_chunk_remaining_accounts( + &self, + client_id: &str, + sequence: u64, + msg_payloads: &[solana_ibc_types::router::PayloadMetadata], + payload_data: &[Vec], + proof_total_chunks: u8, + ) -> Result> { + let mut remaining_account_pubkeys = Vec::new(); + + for (payload_idx, _data) in payload_data.iter().enumerate() { + let payload_index = u8::try_from(payload_idx) + .map_err(|_| anyhow::anyhow!("Payload index exceeds u8 max"))?; + + if payload_idx < msg_payloads.len() && msg_payloads[payload_idx].total_chunks > 0 { + for chunk_idx in 0..msg_payloads[payload_idx].total_chunks { + let (chunk_pda, _) = PayloadChunk::pda( + self.fee_payer, + client_id, + sequence, + payload_index, + chunk_idx, + self.solana_ics26_program_id, + ); + remaining_account_pubkeys.push(chunk_pda); + } + } + } + + if proof_total_chunks > 0 { + for chunk_idx in 0..proof_total_chunks { + let (chunk_pda, _) = ProofChunk::pda( + self.fee_payer, + client_id, + sequence, + chunk_idx, + self.solana_ics26_program_id, + ); + remaining_account_pubkeys.push(chunk_pda); + } + } + + Ok(remaining_account_pubkeys) + } + + fn build_packet_cleanup_tx( + &self, + client_id: &str, + sequence: u64, + msg_payloads: &[solana_ibc_types::router::PayloadMetadata], + proof_total_chunks: u8, + ) -> Result> { + let mut accounts = vec![AccountMeta::new(self.fee_payer, true)]; + + for (payload_idx, payload_metadata) in msg_payloads.iter().enumerate() { + let payload_index = u8::try_from(payload_idx) + .map_err(|_| anyhow::anyhow!("Payload index exceeds u8 max"))?; + + for chunk_index in 0..payload_metadata.total_chunks { + let (chunk_pda, _) = PayloadChunk::pda( + self.fee_payer, + client_id, + sequence, + payload_index, + chunk_index, + self.solana_ics26_program_id, + ); + accounts.push(AccountMeta::new(chunk_pda, false)); + } + } + + for chunk_index in 0..proof_total_chunks { + let (chunk_pda, _) = ProofChunk::pda( + self.fee_payer, + client_id, + sequence, + chunk_index, + self.solana_ics26_program_id, + ); + accounts.push(AccountMeta::new(chunk_pda, false)); + } + + let payload_chunks: Vec = msg_payloads.iter().map(|p| p.total_chunks).collect(); + let msg = MsgCleanupChunks { + client_id: client_id.to_string(), + sequence, + payload_chunks, + total_proof_chunks: proof_total_chunks, + }; + + let mut data = router_instructions::cleanup_chunks_discriminator().to_vec(); + data.extend_from_slice(&msg.try_to_vec()?); + + let instruction = Instruction { + program_id: self.solana_ics26_program_id, + accounts, + data, + }; + + let mut instructions = Self::extend_compute_ix(); + instructions.push(instruction); + + self.create_tx_bytes(&instructions) + } + + /// Derives the GMP result PDA bytes for a single-payload packet. + fn derive_gmp_result_pda_bytes( + &self, + payloads: &[solana_ibc_types::PayloadMetadata], + source_client: &str, + sequence: u64, + ) -> Result> { + match payloads { + [payload] => Ok(self + .resolve_port_program_id(&payload.source_port) + .inspect_err(|err| { + tracing::warn!( + err = ?err, + "Failed to resolve program id for port {}", + &payload.source_port + ); + }) + .ok() + .and_then(|gmp_program_id| { + gmp::find_gmp_result_pda( + &payload.source_port, + source_client, + sequence, + gmp_program_id, + ) + .map(|pda| pda.to_bytes().to_vec()) + }) + .unwrap_or_default()), + [] => Ok(vec![]), + _ => anyhow::bail!("Multi-payload is not yet supported"), + } + } + + /// Count unique accounts across all instructions. + fn count_unique_accounts(instructions: &[Instruction], fee_payer: Pubkey) -> usize { + let mut accounts: HashSet = HashSet::new(); + accounts.insert(fee_payer); + for ix in instructions { + accounts.insert(ix.program_id); + for acc in &ix.accounts { + accounts.insert(acc.pubkey); + } + } + accounts.len() + } + + /// Check if account exists on-chain. + fn account_exists(&self, pubkey: &Pubkey) -> bool { + match self + .target_solana_client + .get_account_with_commitment(pubkey, CommitmentConfig::confirmed()) + { + Ok(response) => response.value.is_some(), + Err(e) => { + tracing::warn!(%pubkey, error = %e, "RPC error checking account existence"); + false + } + } + } + + /// Build transaction with ALT support. + fn build_tx_with_alt(&self, instructions: &[Instruction]) -> Result { + let slot = self + .target_solana_client + .get_slot_with_commitment(CommitmentConfig::processed()) + .map_err(|e| anyhow::anyhow!("Failed to get slot for ALT: {e}"))?; + + let (alt_address, _) = derive_alt_address(slot, self.fee_payer); + + let mut alt_accounts: Vec = Vec::new(); + let mut seen: HashSet = HashSet::new(); + + alt_accounts.push(self.fee_payer); + seen.insert(self.fee_payer); + alt_accounts.push(solana_sdk::system_program::id()); + seen.insert(solana_sdk::system_program::id()); + + for ix in instructions { + if seen.insert(ix.program_id) { + alt_accounts.push(ix.program_id); + } + for acc in &ix.accounts { + if seen.insert(acc.pubkey) { + if self.account_exists(&acc.pubkey) { + alt_accounts.push(acc.pubkey); + } else { + tracing::debug!("Excluding non-existent account {} from ALT", acc.pubkey); + } + } + } + } + + tracing::debug!( + "Building ALT: {} accounts, address={}, slot={}", + alt_accounts.len(), + alt_address, + slot + ); + + let alt_create_tx = self.build_create_alt_tx(slot)?; + + let alt_extend_txs: Vec> = alt_accounts + .chunks(ALT_EXTEND_BATCH_SIZE) + .map(|batch| self.build_extend_alt_tx(slot, batch.to_vec())) + .collect::>>()?; + + let final_tx = self.create_tx_bytes_with_alt(instructions, alt_address, alt_accounts)?; + + Ok((final_tx, alt_create_tx, alt_extend_txs)) + } + + /// Builds IFT `claim_refund` transaction for ack/timeout packets. + /// + /// Returns `None` if not applicable (non-IFT packet, no pending transfer). + /// Tokens are safe in `PendingTransfer` and user can manually claim later. + fn build_ift_claim_refund_tx( + &self, + payloads: &[solana_ibc_types::PayloadMetadata], + payload_data: &[Vec], + source_client: &str, + sequence: u64, + ) -> Option> { + let [payload] = payloads else { + return None; + }; + + let [data] = payload_data else { + return None; + }; + + let gmp_program_id = match self.resolve_port_program_id(&payload.source_port) { + Ok(id) => id, + Err(e) => { + tracing::warn!( + source_port = %payload.source_port, + error = ?e, + "IFT: Failed to resolve port program ID for claim_refund" + ); + return None; + } + }; + + let params = ift::ClaimRefundParams { + source_port: &payload.source_port, + encoding: &payload.encoding, + payload_value: data, + source_client, + sequence, + solana_client: &self.target_solana_client, + gmp_program_id, + fee_payer: self.fee_payer, + }; + + let instruction = ift::build_claim_refund_instruction(¶ms)?; + + let mut instructions = Self::extend_compute_ix(); + instructions.push(instruction); + + match self.create_tx_bytes(&instructions) { + Ok(tx_bytes) => Some(tx_bytes), + Err(e) => { + tracing::warn!( + source_client = %source_client, + sequence = sequence, + error = ?e, + "IFT: Failed to create claim_refund transaction" + ); + None + } + } + } + + // ----------------------------------------------------------------------- + // High-level chunked packet builders (called from attested.rs) + // ----------------------------------------------------------------------- + + pub(crate) fn build_recv_packet_chunked( + &self, + msg: &MsgRecvPacket, + payload_data: &[Vec], + proof_data: &[u8], + abi_hint: Option<&super::payload_translator::AbiHintInfo>, + ) -> Result { + let chunk_txs = self.build_packet_chunk_txs( + &msg.packet.dest_client, + msg.packet.sequence, + &msg.payloads, + payload_data, + msg.proof.total_chunks, + proof_data, + )?; + + let remaining_account_pubkeys = self.build_chunk_remaining_accounts( + &msg.packet.dest_client, + msg.packet.sequence, + &msg.payloads, + payload_data, + msg.proof.total_chunks, + )?; + + let recv_instruction = self.build_recv_packet_instruction( + msg, + remaining_account_pubkeys, + payload_data, + abi_hint, + )?; + + let mut instructions = Self::extend_compute_ix(); + instructions.push(recv_instruction); + + let recv_tx = self.create_tx_bytes(&instructions)?; + + let cleanup_tx = self.build_packet_cleanup_tx( + &msg.packet.dest_client, + msg.packet.sequence, + &msg.payloads, + msg.proof.total_chunks, + )?; + + Ok(SolanaPacketTxs { + chunks: chunk_txs, + final_tx: recv_tx, + cleanup_tx, + alt_create_tx: vec![], + alt_extend_txs: vec![], + gmp_result_pda: Vec::new(), + ift_finalize_transfer_tx: vec![], + }) + } + + pub(crate) async fn build_ack_packet_chunked( + &self, + msg: &MsgAckPacket, + payload_data: &[Vec], + proof_data: &[u8], + ) -> Result { + let chunk_txs = self.build_packet_chunk_txs( + &msg.packet.source_client, + msg.packet.sequence, + &msg.payloads, + payload_data, + msg.proof.total_chunks, + proof_data, + )?; + + let remaining_account_pubkeys = self.build_chunk_remaining_accounts( + &msg.packet.source_client, + msg.packet.sequence, + &msg.payloads, + payload_data, + msg.proof.total_chunks, + )?; + + let ack_instruction = self + .build_ack_packet_instruction(msg, remaining_account_pubkeys) + .await?; + + let mut instructions = Self::extend_compute_ix(); + instructions.push(ack_instruction); + + let unique_accounts = Self::count_unique_accounts(&instructions, self.fee_payer); + + tracing::debug!( + "ack_packet: {} unique accounts (threshold {})", + unique_accounts, + MAX_ACCOUNTS_WITHOUT_ALT + ); + + let (ack_tx, alt_create_tx, alt_extend_txs) = if unique_accounts > MAX_ACCOUNTS_WITHOUT_ALT + { + tracing::debug!( + "Using ALT: {} accounts exceeds {}", + unique_accounts, + MAX_ACCOUNTS_WITHOUT_ALT + ); + self.build_tx_with_alt(&instructions)? + } else { + (self.create_tx_bytes(&instructions)?, vec![], vec![]) + }; + + let cleanup_tx = self.build_packet_cleanup_tx( + &msg.packet.source_client, + msg.packet.sequence, + &msg.payloads, + msg.proof.total_chunks, + )?; + + let gmp_result_pda = self.derive_gmp_result_pda_bytes( + &msg.payloads, + &msg.packet.source_client, + msg.packet.sequence, + )?; + + let ift_finalize_transfer_tx = self + .build_ift_claim_refund_tx( + &msg.payloads, + payload_data, + &msg.packet.source_client, + msg.packet.sequence, + ) + .unwrap_or_default(); + + Ok(SolanaPacketTxs { + chunks: chunk_txs, + final_tx: ack_tx, + cleanup_tx, + alt_create_tx, + alt_extend_txs, + gmp_result_pda, + ift_finalize_transfer_tx, + }) + } + + pub(crate) fn build_timeout_packet_chunked( + &self, + msg: &MsgTimeoutPacket, + payload_data: &[Vec], + proof_data: &[u8], + ) -> Result { + let chunk_txs = self.build_packet_chunk_txs( + &msg.packet.source_client, + msg.packet.sequence, + &msg.payloads, + payload_data, + msg.proof.total_chunks, + proof_data, + )?; + + let remaining_account_pubkeys = self.build_chunk_remaining_accounts( + &msg.packet.source_client, + msg.packet.sequence, + &msg.payloads, + payload_data, + msg.proof.total_chunks, + )?; + + let timeout_instruction = + self.build_timeout_packet_instruction(msg, remaining_account_pubkeys)?; + + let mut instructions = Self::extend_compute_ix(); + instructions.push(timeout_instruction); + + let timeout_tx = self.create_tx_bytes(&instructions)?; + + let cleanup_tx = self.build_packet_cleanup_tx( + &msg.packet.source_client, + msg.packet.sequence, + &msg.payloads, + msg.proof.total_chunks, + )?; + + let gmp_result_pda = self.derive_gmp_result_pda_bytes( + &msg.payloads, + &msg.packet.source_client, + msg.packet.sequence, + )?; + + let ift_finalize_transfer_tx = self + .build_ift_claim_refund_tx( + &msg.payloads, + payload_data, + &msg.packet.source_client, + msg.packet.sequence, + ) + .unwrap_or_default(); + + Ok(SolanaPacketTxs { + chunks: chunk_txs, + final_tx: timeout_tx, + cleanup_tx, + alt_create_tx: vec![], + alt_extend_txs: vec![], + gmp_result_pda, + ift_finalize_transfer_tx, + }) + } +} diff --git a/packages/relayer/modules/eth-to-solana/src/tx_builder/payload_translator.rs b/packages/relayer/modules/eth-to-solana/src/tx_builder/payload_translator.rs new file mode 100644 index 000000000..f62959dea --- /dev/null +++ b/packages/relayer/modules/eth-to-solana/src/tx_builder/payload_translator.rs @@ -0,0 +1,310 @@ +//! ABI payload hint builder for Eth→Solana relay. +//! +//! When relaying from Ethereum to Solana, packet payloads arrive with +//! `encoding = "application/x-solidity-abi"` and ABI-encoded values. +//! Instead of translating the payload (which breaks IBC commitment verification), +//! we build a `SolanaPayloadHint` that the GMP program reads to execute the CPI. +//! The original ABI payload is kept intact for commitment verification. + +use std::sync::{Arc, LazyLock}; + +use alloy::sol_types::SolValue; +use anchor_lang::prelude::*; +use anyhow::{Context, Result}; +use sha2::{Digest, Sha256}; +use solana_client::rpc_client::RpcClient; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, +}; + +use crate::gmp::GMP_PORT_ID; +use crate::ift_payload; +use crate::proto::Protobuf; + +use super::SolanaTxBuilder; + +/// ABI encoding identifier used by Ethereum ICS27 GMP. +pub(crate) const ABI_ENCODING: &str = "application/x-solidity-abi"; + +/// Payload hint PDA seed (must match GMP program's `SolanaPayloadHint::SEED`). +const PAYLOAD_HINT_SEED: &[u8] = b"payload_hint"; + +/// Anchor discriminator for `IFTBridge` accounts. +static IFT_BRIDGE_DISCRIMINATOR: LazyLock<[u8; 8]> = LazyLock::new(|| { + let mut hasher = Sha256::new(); + hasher.update(b"account:IFTBridge"); + let result = hasher.finalize(); + result[..8].try_into().expect("sha256 produces 32 bytes") +}); + +/// Anchor discriminator for `store_payload_hint` instruction. +static STORE_PAYLOAD_HINT_DISCRIMINATOR: LazyLock<[u8; 8]> = LazyLock::new(|| { + let mut hasher = Sha256::new(); + hasher.update(b"global:store_payload_hint"); + let result = hasher.finalize(); + result[..8].try_into().expect("sha256 produces 32 bytes") +}); + +// ABI type matching Solidity's `IICS27GMPMsgs.GMPPacketData`. +alloy::sol! { + struct AbiGmpPacketData { + string sender; + string receiver; + bytes salt; + bytes payload; + string memo; + } +} + +/// Info needed to handle an ABI-encoded GMP payload. +pub struct AbiHintInfo { + /// Store hint instruction (to be sent before recv_packet). + pub store_hint_instruction: Instruction, + /// GMP remaining accounts for the recv_packet instruction. + /// Layout: [gmp_pda, target_program, hint_pda, execution_accounts...] + pub gmp_accounts: Vec, +} + +impl SolanaTxBuilder { + /// Build hint info for ABI-encoded GMP payloads, if applicable. + /// + /// Returns `None` if the payload is not ABI-encoded or not a GMP packet. + /// The original payload data is NOT modified. + pub fn build_abi_hint_if_needed( + &self, + msg: &ibc_proto_eureka::ibc::core::channel::v2::MsgRecvPacket, + ) -> Result> { + let packet = match msg.packet.as_ref() { + Some(p) => p, + None => return Ok(None), + }; + + let dest_client = &packet.destination_client; + + for payload in &packet.payloads { + if payload.encoding != ABI_ENCODING { + continue; + } + + if payload.destination_port != GMP_PORT_ID { + continue; + } + + return self.build_single_abi_hint(payload, dest_client).map(Some); + } + + Ok(None) + } + + /// Build hint info for a single ABI-encoded GMP payload. + fn build_single_abi_hint( + &self, + payload: &ibc_proto_eureka::ibc::core::channel::v2::Payload, + dest_client: &str, + ) -> Result { + tracing::info!("Building ABI hint for dest_client={dest_client}"); + + // 1. ABI-decode the outer GMPPacketData + let abi_gmp: AbiGmpPacketData = SolValue::abi_decode(&payload.value) + .map_err(|e| anyhow::anyhow!("Failed to ABI decode GMPPacketData: {e}"))?; + + tracing::debug!( + sender = %abi_gmp.sender, + receiver = %abi_gmp.receiver, + payload_len = abi_gmp.payload.len(), + "Decoded ABI GMPPacketData" + ); + + // 2. Parse receiver as the target program ID + let ift_program_id: Pubkey = abi_gmp + .receiver + .parse() + .map_err(|e| anyhow::anyhow!("Invalid receiver as Solana pubkey: {e}"))?; + + // 3. Decode inner IFT payload: abi.encode(bytes32(receiver_pubkey), uint256(amount)) + let ift_decoded = ift_payload::decode_ift_mint_payload(&abi_gmp.payload) + .context("Failed to decode inner IFT mint payload")?; + + tracing::debug!( + receiver = %ift_decoded.receiver, + amount = ift_decoded.amount, + "Decoded IFT mint payload" + ); + + // 4. Find the mint from IFT bridge state on Solana + let mint = + find_ift_mint_for_client(&self.target_solana_client, &ift_program_id, dest_client) + .context("Failed to find IFT mint for client")?; + + tracing::debug!(mint = %mint, "Found IFT mint for bridge"); + + // 5. Resolve GMP program ID from the IBC app registration + let gmp_program_id = self + .resolve_port_program_id(GMP_PORT_ID) + .context("Failed to resolve GMP program ID")?; + + // 6. Build the GmpSolanaPayload with correct accounts and instruction data + let gmp_solana_payload = ift_payload::build_ift_mint_gmp_payload( + &ift_decoded, + &ift_payload::BuildIFTMintParams { + ift_program_id, + gmp_program_id, + mint, + dst_client_id: dest_client.to_string(), + fee_payer: self.fee_payer, + }, + &self.target_solana_client, + ) + .context("Failed to build IFT mint GMP payload")?; + + // 7. Encode GmpSolanaPayload as protobuf bytes (for the hint account) + let solana_payload_bytes = Protobuf::::encode_vec( + gmp_solana_payload.clone(), + ); + + // 8. Build the store_payload_hint instruction + let hint_pda = self.derive_hint_pda(gmp_program_id); + let store_hint_ix = + self.build_store_hint_instruction(gmp_program_id, hint_pda, &solana_payload_bytes); + + // 9. Build GMP account PDA from ABI-decoded fields + let sender: solana_ibc_proto::Sender = abi_gmp + .sender + .clone() + .try_into() + .map_err(|_| anyhow::anyhow!("Invalid sender"))?; + let salt: solana_ibc_proto::Salt = abi_gmp + .salt + .to_vec() + .try_into() + .map_err(|_| anyhow::anyhow!("Invalid salt"))?; + let client_id = solana_ibc_types::ClientId::new(dest_client) + .map_err(|e| anyhow::anyhow!("Invalid client ID: {e:?}"))?; + + let gmp_account = + solana_ibc_types::GMPAccount::new(client_id, sender, salt, &gmp_program_id); + let (gmp_account_pda, _) = gmp_account.pda(); + + // 10. Build remaining accounts: [gmp_pda, target_program, hint_pda, execution_accounts...] + let mut gmp_accounts = vec![ + AccountMeta { + pubkey: gmp_account_pda, + is_signer: false, + is_writable: false, + }, + AccountMeta { + pubkey: ift_program_id, + is_signer: false, + is_writable: false, + }, + AccountMeta { + pubkey: hint_pda, + is_signer: false, + is_writable: false, + }, + ]; + + // Add execution accounts from GmpSolanaPayload + for account_meta in &gmp_solana_payload.accounts { + gmp_accounts.push(AccountMeta { + pubkey: account_meta.pubkey, + is_signer: false, + is_writable: account_meta.is_writable, + }); + } + + tracing::info!( + gmp_pda = %gmp_account_pda, + hint_pda = %hint_pda, + num_accounts = gmp_accounts.len(), + "Built ABI hint info" + ); + + Ok(AbiHintInfo { + store_hint_instruction: store_hint_ix, + gmp_accounts, + }) + } + + /// Derive the payload hint PDA address. + fn derive_hint_pda(&self, gmp_program_id: Pubkey) -> Pubkey { + let (pda, _) = Pubkey::find_program_address( + &[PAYLOAD_HINT_SEED, self.fee_payer.as_ref()], + &gmp_program_id, + ); + pda + } + + /// Build the `store_payload_hint` instruction for the GMP program. + fn build_store_hint_instruction( + &self, + gmp_program_id: Pubkey, + hint_pda: Pubkey, + payload_data: &[u8], + ) -> Instruction { + // Instruction data: discriminator + Borsh-encoded Vec + let mut data = STORE_PAYLOAD_HINT_DISCRIMINATOR.to_vec(); + let len = u32::try_from(payload_data.len()).expect("payload_data length fits in u32"); + data.extend_from_slice(&len.to_le_bytes()); + data.extend_from_slice(payload_data); + + Instruction { + program_id: gmp_program_id, + accounts: vec![ + AccountMeta::new(hint_pda, false), + AccountMeta::new(self.fee_payer, true), + AccountMeta::new_readonly(solana_sdk::system_program::id(), false), + ], + data, + } + } +} + +/// Partial deserialization of IFTBridge account to extract mint and client_id. +#[derive(AnchorDeserialize)] +struct IFTBridgePartial { + _version: u8, + _bump: u8, + mint: Pubkey, + client_id: String, +} + +/// Find the SPL token mint associated with an IFT bridge for a given client ID. +fn find_ift_mint_for_client( + solana_client: &Arc, + ift_program_id: &Pubkey, + client_id: &str, +) -> Result { + tracing::debug!( + %ift_program_id, + %client_id, + "Scanning IFT program accounts for bridge" + ); + + let accounts = solana_client + .get_program_accounts(ift_program_id) + .map_err(|e| anyhow::anyhow!("Failed to get IFT program accounts: {e}"))?; + + for (pubkey, account) in &accounts { + if account.data.len() < 8 || account.data[..8] != *IFT_BRIDGE_DISCRIMINATOR { + continue; + } + + let mut data = &account.data[8..]; + let bridge = match IFTBridgePartial::deserialize(&mut data) { + Ok(b) => b, + Err(e) => { + tracing::debug!(%pubkey, error = %e, "Failed to deserialize IFTBridge"); + continue; + } + }; + + if bridge.client_id == client_id { + tracing::debug!(mint = %bridge.mint, "Found IFT bridge"); + return Ok(bridge.mint); + } + } + + anyhow::bail!("No IFT bridge found for client '{client_id}' in program {ift_program_id}") +} diff --git a/packages/relayer/modules/solana-to-eth/Cargo.toml b/packages/relayer/modules/solana-to-eth/Cargo.toml new file mode 100644 index 000000000..436250728 --- /dev/null +++ b/packages/relayer/modules/solana-to-eth/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "ibc-eureka-relayer-solana-to-eth" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +ibc-eureka-relayer-core = { workspace = true, default-features = false } +ibc-eureka-relayer-lib = { workspace = true } + +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true + +tonic = { workspace = true, default-features = true } +anyhow = { workspace = true, features = ["std"] } +tracing = { workspace = true, default-features = true } + +alloy = { workspace = true, features = ["full", "node-bindings"] } + +# Solana specific dependencies +solana-sdk.workspace = true diff --git a/packages/relayer/modules/solana-to-eth/src/lib.rs b/packages/relayer/modules/solana-to-eth/src/lib.rs new file mode 100644 index 000000000..e561bc146 --- /dev/null +++ b/packages/relayer/modules/solana-to-eth/src/lib.rs @@ -0,0 +1,287 @@ +//! One-sided relayer module from Solana to Ethereum. +//! +//! Listens for IBC events on Solana, and builds ABI-encoded EVM multicall +//! transactions with attestation proofs. Payloads from Solana are already +//! ABI-encoded by the IFT program, so no translation is needed. + +#![deny(clippy::nursery, clippy::pedantic, warnings)] +#![allow(missing_docs, unused_crate_dependencies)] + +pub mod tx_builder; + +use std::collections::HashMap; + +use alloy::{ + primitives::{Address, TxHash}, + providers::{Provider, RootProvider}, +}; +use ibc_eureka_relayer_lib::{ + aggregator::Config as AggregatorConfig, + events::EurekaEventWithHeight, + listener::{eth_eureka, solana, ChainListenerService}, + service_utils::{parse_eth_tx_hashes, parse_solana_tx_hashes, to_tonic_status}, + utils::RelayEventsParams, +}; +use solana_sdk::commitment_config::CommitmentConfig; +use tonic::{Request, Response}; + +use ibc_eureka_relayer_core::{ + api::{self, relayer_service_server::RelayerService}, + modules::RelayerModule, +}; + +/// The `SolanaToEthRelayerModule` defines the Solana to Ethereum relayer module. +#[derive(Clone, Copy, Debug)] +pub struct SolanaToEthRelayerModule; + +/// The relayer service from Solana to Ethereum. +struct SolanaToEthRelayerModuleService { + /// Source chain listener for Solana. + src_listener: solana::ChainListener, + /// Target chain listener for Ethereum (for timeout events). + target_listener: eth_eureka::ChainListener, + /// Transaction builder. + tx_builder: SolanaToEthTxBuilder, + /// ICS26 contract address on Ethereum. + ics26_eth_address: Address, +} + +/// Enum wrapping transaction builders for different modes. +enum SolanaToEthTxBuilder { + /// Attestation light client mode. + Attested(tx_builder::AttestedTxBuilder), +} + +/// Configuration for the Solana to Ethereum relayer module. +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct SolanaToEthConfig { + /// The Solana RPC URL. + pub solana_rpc_url: String, + /// The Solana ICS26 router program ID. + pub solana_ics26_program_id: String, + /// The EVM RPC URL. + pub eth_rpc_url: String, + /// The ICS26 contract address on Ethereum. + pub ics26_address: Address, + /// Transaction builder mode. + pub mode: SolanaToEthTxBuilderMode, +} + +/// Transaction builder mode for Solana to Eth relay. +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SolanaToEthTxBuilderMode { + /// Attestation light client mode. + Attested(AggregatorConfig), +} + +impl SolanaToEthRelayerModuleService { + async fn new(config: SolanaToEthConfig) -> anyhow::Result { + let solana_ics26_program_id = config + .solana_ics26_program_id + .parse() + .map_err(|e| anyhow::anyhow!("Invalid Solana ICS26 program ID: {e}"))?; + + let src_listener = + solana::ChainListener::new(config.solana_rpc_url.clone(), solana_ics26_program_id); + + let provider = RootProvider::builder() + .connect(&config.eth_rpc_url) + .await + .map_err(|e| anyhow::anyhow!("failed to create EVM provider: {e}"))?; + + let target_listener = + eth_eureka::ChainListener::new(config.ics26_address, provider.clone()); + + let tx_builder = match config.mode { + SolanaToEthTxBuilderMode::Attested(aggregator_config) => { + let attested = + tx_builder::AttestedTxBuilder::new(aggregator_config, provider).await?; + SolanaToEthTxBuilder::Attested(attested) + } + }; + + Ok(Self { + src_listener, + target_listener, + tx_builder, + ics26_eth_address: config.ics26_address, + }) + } +} + +#[tonic::async_trait] +impl RelayerService for SolanaToEthRelayerModuleService { + async fn info( + &self, + _request: Request, + ) -> Result, tonic::Status> { + tracing::debug!("Handling info request for Solana to Eth"); + + Ok(Response::new(api::InfoResponse { + source_chain: Some(api::Chain { + chain_id: "solana-localnet".to_string(), + ibc_version: "2".to_string(), + ibc_contract: self.src_listener.ics26_program_id().to_string(), + }), + target_chain: Some(api::Chain { + chain_id: self + .target_listener + .chain_id() + .await + .map_err(to_tonic_status)?, + ibc_version: "2".to_string(), + ibc_contract: self.ics26_eth_address.to_string(), + }), + metadata: HashMap::default(), + })) + } + + async fn relay_by_tx( + &self, + request: Request, + ) -> Result, tonic::Status> { + let inner_req = request.into_inner(); + tracing::debug!( + "Relay request: {} source txs, {} timeout txs", + inner_req.source_tx_ids.len(), + inner_req.timeout_tx_ids.len() + ); + + let solana_tx_hashes = parse_solana_tx_hashes(inner_req.source_tx_ids)?; + let eth_timeout_tx_hashes = parse_eth_tx_hashes(inner_req.timeout_tx_ids)?; + let eth_timeout_txs = eth_timeout_tx_hashes + .into_iter() + .map(TxHash::from) + .collect(); + + let solana_src_events = self + .src_listener + .fetch_tx_events(solana_tx_hashes) + .await + .map_err(to_tonic_status)?; + + tracing::debug!("Fetched {} src events from Solana", solana_src_events.len()); + + let target_events = self + .target_listener + .fetch_tx_events(eth_timeout_txs) + .await + .map_err(|e| tonic::Status::from_error(e.into()))?; + + tracing::debug!("Fetched {} timeout events from EVM", target_events.len()); + + // Convert Solana events to generic EurekaEventWithHeight + let src_events: Vec = solana_src_events + .into_iter() + .map(EurekaEventWithHeight::from) + .collect(); + + // For timeouts in attested mode, get the current Solana slot + // where non-membership is proven + let timeout_relay_height = if !target_events.is_empty() { + let slot = self + .src_listener + .client() + .get_slot_with_commitment(CommitmentConfig::finalized()) + .map_err(|e| tonic::Status::internal(format!("Failed to get Solana slot: {e}")))?; + Some(slot) + } else { + None + }; + + let tx = self + .tx_builder + .relay_events(RelayEventsParams { + src_events, + target_events, + timeout_relay_height, + src_client_id: inner_req.src_client_id, + dst_client_id: inner_req.dst_client_id, + src_packet_seqs: inner_req.src_packet_sequences, + dst_packet_seqs: inner_req.dst_packet_sequences, + }) + .await + .map_err(|e| tonic::Status::from_error(e.into()))?; + + tracing::debug!("Relay completed"); + + Ok(Response::new(api::RelayByTxResponse { + tx, + address: self.ics26_eth_address.to_string(), + })) + } + + async fn create_client( + &self, + request: Request, + ) -> Result, tonic::Status> { + tracing::debug!("Handling create client request for Solana to Eth"); + + let inner_req = request.into_inner(); + let tx = self + .tx_builder + .create_client(&inner_req.parameters) + .map_err(|e| tonic::Status::from_error(e.into()))?; + + Ok(Response::new(api::CreateClientResponse { + tx, + address: self.ics26_eth_address.to_string(), + })) + } + + async fn update_client( + &self, + request: Request, + ) -> Result, tonic::Status> { + tracing::info!("Handling update client request for Solana to Eth"); + + let inner_req = request.into_inner(); + let tx = self + .tx_builder + .update_client(&inner_req.dst_client_id) + .await + .map_err(|e| tonic::Status::from_error(e.into()))?; + + Ok(Response::new(api::UpdateClientResponse { + tx, + address: self.ics26_eth_address.to_string(), + })) + } +} + +#[tonic::async_trait] +impl RelayerModule for SolanaToEthRelayerModule { + fn name(&self) -> &'static str { + "solana_to_eth" + } + + async fn create_service( + &self, + config: serde_json::Value, + ) -> anyhow::Result> { + let config: SolanaToEthConfig = serde_json::from_value(config)?; + let service = SolanaToEthRelayerModuleService::new(config).await?; + Ok(Box::new(service)) + } +} + +impl SolanaToEthTxBuilder { + async fn relay_events(&self, params: RelayEventsParams) -> anyhow::Result> { + match self { + Self::Attested(tb) => tb.relay_events(params).await, + } + } + + fn create_client(&self, parameters: &HashMap) -> anyhow::Result> { + match self { + Self::Attested(tb) => tb.create_client(parameters), + } + } + + async fn update_client(&self, dst_client_id: &str) -> anyhow::Result> { + match self { + Self::Attested(tb) => tb.update_client(dst_client_id).await, + } + } +} diff --git a/packages/relayer/modules/solana-to-eth/src/tx_builder.rs b/packages/relayer/modules/solana-to-eth/src/tx_builder.rs new file mode 100644 index 000000000..51cbe63cc --- /dev/null +++ b/packages/relayer/modules/solana-to-eth/src/tx_builder.rs @@ -0,0 +1,55 @@ +//! Transaction builder for Solana-to-Eth relay using attestation proofs. +//! +//! Delegates to `eth_attested` utilities for building ABI-encoded EVM +//! multicall transactions. Payloads from Solana are already ABI-encoded +//! by the IFT program, so no translation is needed (passthrough). + +use std::collections::HashMap; + +use alloy::providers::RootProvider; +use anyhow::Result; +use ibc_eureka_relayer_lib::{ + aggregator::{Aggregator, Config as AggregatorConfig}, + utils::{ + eth_attested::{ + build_eth_attestor_create_client_calldata, build_eth_attestor_relay_events_tx, + build_eth_attestor_update_client_calldata, + }, + RelayEventsParams, + }, +}; + +/// Transaction builder using attestation proofs for Solana-to-Eth relay. +pub struct AttestedTxBuilder { + aggregator: Aggregator, + provider: RootProvider, +} + +impl AttestedTxBuilder { + /// Create a new [`AttestedTxBuilder`] instance. + pub async fn new(aggregator_config: AggregatorConfig, provider: RootProvider) -> Result { + let aggregator = Aggregator::from_config(aggregator_config).await?; + Ok(Self { + aggregator, + provider, + }) + } + + /// Relay events from Solana to Ethereum using attestations. + /// + /// Builds an ABI-encoded multicall transaction containing update_client, + /// recv_packet, ack_packet, and timeout_packet calls. + pub async fn relay_events(&self, params: RelayEventsParams) -> Result> { + build_eth_attestor_relay_events_tx(&self.aggregator, params).await + } + + /// Build create_client calldata for an attestation light client on EVM. + pub fn create_client(&self, parameters: &HashMap) -> Result> { + build_eth_attestor_create_client_calldata(parameters, self.provider.clone()) + } + + /// Build update_client calldata for the attestation light client on EVM. + pub async fn update_client(&self, dst_client_id: &str) -> Result> { + build_eth_attestor_update_client_calldata(&self.aggregator, dst_client_id.to_string()).await + } +} diff --git a/programs/relayer/Cargo.toml b/programs/relayer/Cargo.toml index 2ab764c25..226bc6640 100644 --- a/programs/relayer/Cargo.toml +++ b/programs/relayer/Cargo.toml @@ -14,6 +14,8 @@ ibc-eureka-relayer-cosmos-to-cosmos = { workspace = true, default-features = ibc-eureka-relayer-eth-to-cosmos-compat = { workspace = true, default-features = false } ibc-eureka-relayer-solana-to-cosmos = { workspace = true, default-features = false } ibc-eureka-relayer-cosmos-to-solana = { workspace = true, default-features = false } +ibc-eureka-relayer-eth-to-solana = { workspace = true, default-features = false } +ibc-eureka-relayer-solana-to-eth = { workspace = true, default-features = false } tokio = { workspace = true, default-features = true } prometheus = { workspace = true, default-features = true } diff --git a/programs/relayer/src/bin/relayer.rs b/programs/relayer/src/bin/relayer.rs index 036472c90..68125419a 100644 --- a/programs/relayer/src/bin/relayer.rs +++ b/programs/relayer/src/bin/relayer.rs @@ -10,7 +10,9 @@ use ibc_eureka_relayer_cosmos_to_solana::CosmosToSolanaRelayerModule; use ibc_eureka_relayer_eth_to_cosmos::EthToCosmosRelayerModule; use ibc_eureka_relayer_eth_to_cosmos_compat::EthToCosmosCompatRelayerModule; use ibc_eureka_relayer_eth_to_eth::EthToEthRelayerModule; +use ibc_eureka_relayer_eth_to_solana::EthToSolanaRelayerModule; use ibc_eureka_relayer_solana_to_cosmos::SolanaToCosmosRelayerModule; +use ibc_eureka_relayer_solana_to_eth::SolanaToEthRelayerModule; use prometheus::{Encoder, TextEncoder}; use tracing::info; @@ -41,6 +43,8 @@ async fn main() -> anyhow::Result<()> { relayer_builder.add_module(EthToEthRelayerModule); relayer_builder.add_module(SolanaToCosmosRelayerModule); relayer_builder.add_module(CosmosToSolanaRelayerModule); + relayer_builder.add_module(EthToSolanaRelayerModule); + relayer_builder.add_module(SolanaToEthRelayerModule); // Start the metrics server. tokio::spawn(async { diff --git a/programs/solana/programs/ics27-gmp/src/abi.rs b/programs/solana/programs/ics27-gmp/src/abi.rs new file mode 100644 index 000000000..7568c4cae --- /dev/null +++ b/programs/solana/programs/ics27-gmp/src/abi.rs @@ -0,0 +1,265 @@ +//! ABI decoder for GMP packet data. +//! +//! Decodes ABI-encoded `GMPPacketData(string, string, bytes, bytes, string)` +//! from Ethereum's Solidity ABI encoding format. + +use anchor_lang::prelude::*; +use solana_ibc_proto::Protobuf; + +use crate::errors::GMPError; + +const WORD_SIZE: usize = 32; +const NUM_FIELDS: usize = 5; +const HEAD_SIZE: usize = NUM_FIELDS * WORD_SIZE; + +/// Decoded ABI GMP packet data (raw bytes, before constrained type validation). +pub struct AbiDecodedGmpPacket { + pub sender: Vec, + pub receiver: Vec, + pub salt: Vec, + pub payload: Vec, + pub memo: Vec, +} + +impl AbiDecodedGmpPacket { + /// Convert raw decoded fields into a validated `GmpPacketData`. + pub fn into_gmp_packet_data(self) -> Result { + let sender: solana_ibc_proto::Sender = core::str::from_utf8(&self.sender) + .map_err(|_| error!(GMPError::InvalidAbiEncoding))? + .to_string() + .try_into() + .map_err(|_| error!(GMPError::InvalidPacketData))?; + + let receiver: solana_ibc_proto::Receiver = core::str::from_utf8(&self.receiver) + .map_err(|_| error!(GMPError::InvalidAbiEncoding))? + .to_string() + .try_into() + .map_err(|_| error!(GMPError::InvalidPacketData))?; + + let salt: solana_ibc_proto::Salt = self + .salt + .try_into() + .map_err(|_| error!(GMPError::InvalidPacketData))?; + + let payload: solana_ibc_proto::Payload = self + .payload + .try_into() + .map_err(|_| error!(GMPError::InvalidPacketData))?; + + let memo: solana_ibc_proto::Memo = core::str::from_utf8(&self.memo) + .map_err(|_| error!(GMPError::InvalidAbiEncoding))? + .to_string() + .try_into() + .map_err(|_| error!(GMPError::InvalidPacketData))?; + + Ok(solana_ibc_proto::GmpPacketData { + sender, + receiver, + salt, + payload, + memo, + }) + } +} + +/// Read a big-endian uint256 as usize from a 32-byte word. +/// +/// Only the last 8 bytes are used; the upper 24 must be zero. +fn read_offset(data: &[u8], word_index: usize) -> Result { + let start = word_index * WORD_SIZE; + let end = start + WORD_SIZE; + require!(end <= data.len(), GMPError::InvalidAbiEncoding); + + let upper = &data[start..start + 24]; + require!(upper.iter().all(|&b| b == 0), GMPError::InvalidAbiEncoding); + + let bytes: [u8; 8] = data[start + 24..end] + .try_into() + .map_err(|_| error!(GMPError::InvalidAbiEncoding))?; + Ok(u64::from_be_bytes(bytes) as usize) +} + +/// Read a dynamic `bytes`/`string` field from the ABI data at the given offset. +fn read_dynamic_bytes(data: &[u8], offset: usize) -> Result> { + require!( + offset + WORD_SIZE <= data.len(), + GMPError::InvalidAbiEncoding + ); + + let len_bytes: [u8; 8] = data[offset + 24..offset + WORD_SIZE] + .try_into() + .map_err(|_| error!(GMPError::InvalidAbiEncoding))?; + let len = u64::from_be_bytes(len_bytes) as usize; + + let data_start = offset + WORD_SIZE; + require!(data_start + len <= data.len(), GMPError::InvalidAbiEncoding); + + Ok(data[data_start..data_start + len].to_vec()) +} + +/// Decode ABI-encoded `GMPPacketData(string, string, bytes, bytes, string)`. +/// +/// Solidity's `abi.encode(struct)` wraps the struct in an outer tuple offset: +/// - `[0..32]`: outer offset word (always 0x20, pointing to byte 32) +/// - `[32..192]`: 5 x 32-byte field offset words (relative to byte 32) +/// - At each offset (relative to byte 32): 32-byte length word + padded data +pub fn decode_abi_gmp_packet(data: &[u8]) -> Result { + // Skip the outer tuple offset word (first 32 bytes) + require!( + data.len() >= WORD_SIZE + HEAD_SIZE, + GMPError::InvalidAbiEncoding + ); + let tuple_data = &data[WORD_SIZE..]; + + let offset_sender = read_offset(tuple_data, 0)?; + let offset_receiver = read_offset(tuple_data, 1)?; + let offset_salt = read_offset(tuple_data, 2)?; + let offset_payload = read_offset(tuple_data, 3)?; + let offset_memo = read_offset(tuple_data, 4)?; + + Ok(AbiDecodedGmpPacket { + sender: read_dynamic_bytes(tuple_data, offset_sender)?, + receiver: read_dynamic_bytes(tuple_data, offset_receiver)?, + salt: read_dynamic_bytes(tuple_data, offset_salt)?, + payload: read_dynamic_bytes(tuple_data, offset_payload)?, + memo: read_dynamic_bytes(tuple_data, offset_memo)?, + }) +} + +/// Pad length up to the next 32-byte boundary. +const fn pad_to_32(len: usize) -> usize { + len.div_ceil(32) * 32 +} + +/// Encode a dynamic ABI field (bytes/string): 32-byte length word + padded data. +fn encode_dynamic(data: &[u8]) -> Vec { + let mut result = vec![0u8; 32]; + let len = data.len() as u64; + result[24..32].copy_from_slice(&len.to_be_bytes()); + result.extend_from_slice(data); + let padded_len = pad_to_32(data.len()); + result.resize(32 + padded_len, 0); + result +} + +/// Encode `GMPPacketData(string, string, bytes, bytes, string)` as ABI. +/// +/// Produces the same layout as Solidity's `abi.encode(GMPPacketData{...})`: +/// - `[0..32]`: outer tuple offset (0x20) +/// - `[32..192]`: 5 field offset words +/// - dynamic data for each field +pub fn encode_abi_gmp_packet( + sender: &str, + receiver: &str, + salt: &[u8], + payload: &[u8], + memo: &str, +) -> Vec { + let fields: Vec> = vec![ + encode_dynamic(sender.as_bytes()), + encode_dynamic(receiver.as_bytes()), + encode_dynamic(salt), + encode_dynamic(payload), + encode_dynamic(memo.as_bytes()), + ]; + + let mut offsets = vec![0u8; HEAD_SIZE]; + let mut current_offset = HEAD_SIZE; + for (i, field) in fields.iter().enumerate() { + let offset = current_offset as u64; + offsets[i * WORD_SIZE + 24..i * WORD_SIZE + 32].copy_from_slice(&offset.to_be_bytes()); + current_offset += field.len(); + } + + // Prepend outer tuple offset (0x20) to match abi.encode(struct) + let mut outer_offset = vec![0u8; WORD_SIZE]; + outer_offset[24..32].copy_from_slice(&(WORD_SIZE as u64).to_be_bytes()); + + let mut result = outer_offset; + result.extend_from_slice(&offsets); + for field in fields { + result.extend_from_slice(&field); + } + result +} + +/// Decode `GmpPacketData` from either protobuf or ABI encoding based on the encoding string. +/// +/// Used by `on_ack_packet` and `on_timeout_packet` to extract sender from the original packet. +pub fn decode_gmp_packet_data( + value: &[u8], + encoding: &str, +) -> Result { + match encoding { + crate::constants::ABI_ENCODING => decode_abi_gmp_packet(value)? + .into_gmp_packet_data() + .map_err(|e| { + msg!("GMP ABI packet validation failed: {}", e); + error!(GMPError::InvalidPacketData) + }), + crate::constants::ICS27_ENCODING => { + solana_ibc_proto::GmpPacketData::decode(value).map_err(|e| { + msg!("GMP protobuf decode failed: {}", e); + error!(GMPError::InvalidPacketData) + }) + } + _ => Err(error!(GMPError::InvalidEncoding)), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_decode_roundtrip() { + let sender = "0x1234567890abcdef"; + let receiver = "So1ana1111111111111111111111111111111111111"; + let salt = b"test-salt"; + let payload = b"some payload data"; + let memo = "hello memo"; + + let encoded = encode_abi_gmp_packet(sender, receiver, salt, payload, memo); + let decoded = decode_abi_gmp_packet(&encoded).unwrap(); + + assert_eq!(decoded.sender, sender.as_bytes()); + assert_eq!(decoded.receiver, receiver.as_bytes()); + assert_eq!(decoded.salt, salt); + assert_eq!(decoded.payload, payload); + assert_eq!(decoded.memo, memo.as_bytes()); + } + + #[test] + fn test_decode_empty_fields() { + let encoded = encode_abi_gmp_packet("sender", "", &[], &[1], ""); + let decoded = decode_abi_gmp_packet(&encoded).unwrap(); + + assert_eq!(decoded.sender, b"sender"); + assert_eq!(decoded.receiver, b""); + assert_eq!(decoded.salt, &[] as &[u8]); + assert_eq!(decoded.payload, &[1]); + assert_eq!(decoded.memo, b""); + } + + #[test] + fn test_decode_too_short() { + let data = vec![0u8; WORD_SIZE + HEAD_SIZE - 1]; + assert!(decode_abi_gmp_packet(&data).is_err()); + } + + #[test] + fn test_into_gmp_packet_data() { + let sender = "cosmos1sender"; + let receiver = "11111111111111111111111111111111"; // 32-char base58 pubkey + let payload = vec![1, 2, 3, 4]; + + let encoded = encode_abi_gmp_packet(sender, receiver, &[], &payload, ""); + let decoded = decode_abi_gmp_packet(&encoded).unwrap(); + let packet = decoded.into_gmp_packet_data().unwrap(); + + assert_eq!(&*packet.sender, sender); + assert_eq!(&*packet.receiver, receiver); + assert!(packet.salt.is_empty()); + assert_eq!(&*packet.payload, &payload); + } +} diff --git a/programs/solana/programs/ics27-gmp/src/constants.rs b/programs/solana/programs/ics27-gmp/src/constants.rs index 8214d151a..0bbcedc01 100644 --- a/programs/solana/programs/ics27-gmp/src/constants.rs +++ b/programs/solana/programs/ics27-gmp/src/constants.rs @@ -13,6 +13,9 @@ pub const ICS27_VERSION: &str = "ics27-2"; /// ICS27 encoding (must match Cosmos IBC-Go's `EncodingProtobuf` constant) pub const ICS27_ENCODING: &str = "application/x-protobuf"; +/// ABI encoding used by Ethereum ICS27 GMP +pub const ABI_ENCODING: &str = "application/x-solidity-abi"; + /// Maximum timeout duration (24 hours in seconds) pub const MAX_TIMEOUT_DURATION: i64 = 86400; diff --git a/programs/solana/programs/ics27-gmp/src/errors.rs b/programs/solana/programs/ics27-gmp/src/errors.rs index 17b80f37b..3f3162f13 100644 --- a/programs/solana/programs/ics27-gmp/src/errors.rs +++ b/programs/solana/programs/ics27-gmp/src/errors.rs @@ -135,6 +135,12 @@ pub enum GMPError { #[msg("Result account PDA mismatch")] ResultAccountPDAMismatch, + #[msg("Invalid ABI encoding")] + InvalidAbiEncoding, + + #[msg("Invalid payload hint account")] + InvalidHintAccount, + #[msg("Light client program does not match client registry")] InvalidLightClientProgram, diff --git a/programs/solana/programs/ics27-gmp/src/instructions/mod.rs b/programs/solana/programs/ics27-gmp/src/instructions/mod.rs index 73b95a7af..e5cab6033 100644 --- a/programs/solana/programs/ics27-gmp/src/instructions/mod.rs +++ b/programs/solana/programs/ics27-gmp/src/instructions/mod.rs @@ -3,6 +3,7 @@ pub mod initialize; pub mod on_ack_packet; pub mod on_recv_packet; pub mod on_timeout_packet; +pub mod payload_hint; pub mod send_call; pub mod send_call_cpi; pub mod set_access_manager; @@ -12,6 +13,7 @@ pub use initialize::*; pub use on_ack_packet::*; pub use on_recv_packet::*; pub use on_timeout_packet::*; +pub use payload_hint::*; pub use send_call::*; pub use send_call_cpi::*; pub use set_access_manager::*; diff --git a/programs/solana/programs/ics27-gmp/src/instructions/on_ack_packet.rs b/programs/solana/programs/ics27-gmp/src/instructions/on_ack_packet.rs index 55b856166..0d0754f57 100644 --- a/programs/solana/programs/ics27-gmp/src/instructions/on_ack_packet.rs +++ b/programs/solana/programs/ics27-gmp/src/instructions/on_ack_packet.rs @@ -2,7 +2,6 @@ use crate::errors::GMPError; use crate::events::GMPCallAcknowledgment; use crate::state::{GMPAppState, GMPCallResult, GMPCallResultAccount}; use anchor_lang::prelude::*; -use solana_ibc_proto::{GmpPacketData, ProstMessage, RawGmpPacketData}; /// Processes an IBC packet acknowledgement received from the router via CPI. /// Creates a `GMPCallResultAccount` PDA to store the acknowledgement outcome. @@ -52,10 +51,8 @@ pub fn on_acknowledgement_packet( ) .map_err(GMPError::from)?; - let raw_packet = RawGmpPacketData::decode(msg.payload.value.as_slice()) - .map_err(|_| GMPError::InvalidPacketData)?; let packet_data = - GmpPacketData::try_from(raw_packet).map_err(|_| GMPError::InvalidPacketData)?; + crate::abi::decode_gmp_packet_data(&msg.payload.value, &msg.payload.encoding)?; let clock = Clock::get()?; let sender: Pubkey = packet_data diff --git a/programs/solana/programs/ics27-gmp/src/instructions/on_recv_packet.rs b/programs/solana/programs/ics27-gmp/src/instructions/on_recv_packet.rs index bb3993dc5..5aea3d649 100644 --- a/programs/solana/programs/ics27-gmp/src/instructions/on_recv_packet.rs +++ b/programs/solana/programs/ics27-gmp/src/instructions/on_recv_packet.rs @@ -1,10 +1,10 @@ use crate::constants::*; use crate::errors::GMPError; use crate::proto::GmpSolanaPayload; -use crate::state::GMPAppState; +use crate::state::{GMPAppState, SolanaPayloadHint}; use anchor_lang::prelude::*; use anchor_lang::solana_program::instruction::Instruction; -use solana_ibc_proto::{GmpAcknowledgement, GmpPacketData, ProstMessage, Protobuf}; +use solana_ibc_proto::{GmpAcknowledgement, ProstMessage, Protobuf}; use solana_ibc_types::GMPAccount; /// Number of fixed accounts in `remaining_accounts` (before target program accounts) @@ -16,6 +16,9 @@ const GMP_ACCOUNT_INDEX: usize = 0; /// Index of target program in `remaining_accounts` const TARGET_PROGRAM_INDEX: usize = 1; +/// Index of hint account in `remaining_accounts` (ABI encoding only) +const HINT_ACCOUNT_INDEX: usize = 2; + /// Receives an IBC packet from the router via CPI and executes the target /// program call. /// @@ -83,10 +86,9 @@ pub fn on_recv_packet<'info>( GMPError::InvalidPort ); - require!( - msg.payload.encoding == ICS27_ENCODING, - GMPError::InvalidEncoding - ); + let is_abi = msg.payload.encoding == ABI_ENCODING; + let is_protobuf = msg.payload.encoding == ICS27_ENCODING; + require!(is_abi || is_protobuf, GMPError::InvalidEncoding); require!(msg.payload.dest_port == GMP_PORT_ID, GMPError::InvalidPort); @@ -98,12 +100,9 @@ pub fn on_recv_packet<'info>( require!(target_program.executable, GMPError::TargetNotExecutable); - // Decode and validate GMP packet data from protobuf payload - // Uses Protobuf::decode which internally validates all constraints - let packet_data = GmpPacketData::decode(msg.payload.value.as_slice()).map_err(|e| { - msg!("GMP packet validation failed: {}", e); - GMPError::InvalidPacketData - })?; + // Decode GMP packet data from either protobuf or ABI encoding + let packet_data = + crate::abi::decode_gmp_packet_data(&msg.payload.value, &msg.payload.encoding)?; // Parse receiver as Solana Pubkey (for incoming packets, receiver is a Solana address) let receiver_pubkey = @@ -139,18 +138,52 @@ pub fn on_recv_packet<'info>( GMPError::GMPAccountPDAMismatch ); - // Decode and validate the GMP Solana payload - // The payload contains all required accounts and instruction data - let solana_payload = GmpSolanaPayload::decode(&packet_data.payload[..]).map_err(|e| { - msg!("GMP Solana payload validation failed: {}", e); - GMPError::InvalidSolanaPayload - })?; + // Get GmpSolanaPayload: from hint account (ABI) or from packet payload (protobuf) + let (solana_payload, execution_start) = if is_abi { + // For ABI packets, read GmpSolanaPayload from the hint account at remaining_accounts[2] + let hint_ai = ctx + .remaining_accounts + .get(HINT_ACCOUNT_INDEX) + .ok_or(GMPError::InsufficientAccounts)?; + + // Validate hint account: owned by this program with correct discriminator + require!(hint_ai.owner == &crate::ID, GMPError::InvalidHintAccount); + + let hint_data = hint_ai.try_borrow_data()?; + require!(hint_data.len() >= 8, GMPError::InvalidHintAccount); + + // Verify Anchor discriminator + let expected_disc = SolanaPayloadHint::DISCRIMINATOR; + require!( + hint_data[..8] == *expected_disc, + GMPError::InvalidHintAccount + ); + + // Deserialize: skip discriminator(8) + bump(1) + vec_len(4) to get data + let hint = SolanaPayloadHint::try_deserialize(&mut &hint_data[..]) + .map_err(|_| error!(GMPError::InvalidHintAccount))?; + + let payload = GmpSolanaPayload::decode(hint.data.as_slice()).map_err(|e| { + msg!("GMP Solana payload from hint failed: {}", e); + error!(GMPError::InvalidSolanaPayload) + })?; + + (payload, FIXED_REMAINING_ACCOUNTS + 1) // +1 for hint account + } else { + // For protobuf packets, decode GmpSolanaPayload from the packet payload + let payload = GmpSolanaPayload::decode(&packet_data.payload[..]).map_err(|e| { + msg!("GMP Solana payload validation failed: {}", e); + GMPError::InvalidSolanaPayload + })?; + + (payload, FIXED_REMAINING_ACCOUNTS) + }; // Build account metas from GMP Solana payload let mut account_metas = solana_payload.to_account_metas(); - // Skip gmp_account_pda[0] and target_program[1] - let remaining_accounts_for_execution = &ctx.remaining_accounts[FIXED_REMAINING_ACCOUNTS..]; + // Skip fixed accounts: gmp_account[0], target_program[1], and optionally hint[2] for ABI + let remaining_accounts_for_execution = &ctx.remaining_accounts[execution_start..]; // Validate account count matches exactly (before payer injection) require!( diff --git a/programs/solana/programs/ics27-gmp/src/instructions/on_timeout_packet.rs b/programs/solana/programs/ics27-gmp/src/instructions/on_timeout_packet.rs index 733c30452..1dd6bcca7 100644 --- a/programs/solana/programs/ics27-gmp/src/instructions/on_timeout_packet.rs +++ b/programs/solana/programs/ics27-gmp/src/instructions/on_timeout_packet.rs @@ -2,7 +2,6 @@ use crate::errors::GMPError; use crate::events::GMPCallTimeout; use crate::state::{GMPAppState, GMPCallResult, GMPCallResultAccount}; use anchor_lang::prelude::*; -use solana_ibc_proto::{GmpPacketData, ProstMessage, RawGmpPacketData}; /// Processes an IBC packet timeout received from the router via CPI. /// Creates a `GMPCallResultAccount` PDA to record the timeout status. @@ -52,10 +51,8 @@ pub fn on_timeout_packet( ) .map_err(GMPError::from)?; - let raw_packet = RawGmpPacketData::decode(msg.payload.value.as_slice()) - .map_err(|_| GMPError::InvalidPacketData)?; let packet_data = - GmpPacketData::try_from(raw_packet).map_err(|_| GMPError::InvalidPacketData)?; + crate::abi::decode_gmp_packet_data(&msg.payload.value, &msg.payload.encoding)?; let clock = Clock::get()?; let sender: Pubkey = packet_data diff --git a/programs/solana/programs/ics27-gmp/src/instructions/payload_hint.rs b/programs/solana/programs/ics27-gmp/src/instructions/payload_hint.rs new file mode 100644 index 000000000..221d0d0d3 --- /dev/null +++ b/programs/solana/programs/ics27-gmp/src/instructions/payload_hint.rs @@ -0,0 +1,55 @@ +//! Store and close `SolanaPayloadHint` accounts. +//! +//! The relayer stores a protobuf-encoded `GmpSolanaPayload` in a hint account +//! before calling `recv_packet` for ABI-encoded packets. + +use anchor_lang::prelude::*; + +use crate::errors::GMPError; +use crate::state::SolanaPayloadHint; + +#[derive(Accounts)] +pub struct StorePayloadHint<'info> { + #[account( + init_if_needed, + payer = payer, + space = 8 + SolanaPayloadHint::INIT_SPACE, + seeds = [SolanaPayloadHint::SEED, payer.key().as_ref()], + bump, + )] + pub hint: Account<'info, SolanaPayloadHint>, + + #[account(mut)] + pub payer: Signer<'info>, + + pub system_program: Program<'info, System>, +} + +pub fn store_payload_hint(ctx: Context, data: Vec) -> Result<()> { + require!( + data.len() <= SolanaPayloadHint::MAX_DATA_LEN, + GMPError::InvalidSolanaPayload + ); + let hint = &mut ctx.accounts.hint; + hint.bump = ctx.bumps.hint; + hint.data = data; + Ok(()) +} + +#[derive(Accounts)] +pub struct ClosePayloadHint<'info> { + #[account( + mut, + close = payer, + seeds = [SolanaPayloadHint::SEED, payer.key().as_ref()], + bump = hint.bump, + )] + pub hint: Account<'info, SolanaPayloadHint>, + + #[account(mut)] + pub payer: Signer<'info>, +} + +pub const fn close_payload_hint(_ctx: Context) -> Result<()> { + Ok(()) +} diff --git a/programs/solana/programs/ics27-gmp/src/instructions/send_call.rs b/programs/solana/programs/ics27-gmp/src/instructions/send_call.rs index 7ba236d85..dfc7e38fd 100644 --- a/programs/solana/programs/ics27-gmp/src/instructions/send_call.rs +++ b/programs/solana/programs/ics27-gmp/src/instructions/send_call.rs @@ -1,7 +1,8 @@ +use crate::abi::encode_abi_gmp_packet; use crate::constants::*; use crate::errors::GMPError; use crate::events::GMPCallSent; -use crate::state::{GMPAppState, SendCallMsg}; +use crate::state::{GMPAppState, GmpEncoding, SendCallMsg}; use anchor_lang::prelude::*; use ics26_router::state::{Client, ClientSequence, IBCApp, RouterState}; use solana_ibc_proto::{Protobuf, RawGmpPacketData}; @@ -161,13 +162,27 @@ pub(crate) fn send_call_inner<'info>( GMPError::InvalidPacketData })?; - let packet_data_bytes = packet_data.encode_vec(); + // Encode packet data based on the requested encoding + let (encoding, packet_data_bytes) = if msg.encoding == GmpEncoding::Abi { + ( + ABI_ENCODING.to_string(), + encode_abi_gmp_packet( + &packet_data.sender, + &packet_data.receiver, + &packet_data.salt, + &packet_data.payload, + &packet_data.memo, + ), + ) + } else { + (ICS27_ENCODING.to_string(), packet_data.encode_vec()) + }; let ibc_payload = Payload { source_port: GMP_PORT_ID.to_string(), dest_port: GMP_PORT_ID.to_string(), version: ICS27_VERSION.to_string(), - encoding: ICS27_ENCODING.to_string(), + encoding, value: packet_data_bytes, }; @@ -287,6 +302,7 @@ mod tests { payload: vec![4, 5, 6], timeout_timestamp: 3600, // 1 hour from epoch (safe for Mollusk default clock=0) memo: String::new(), + encoding: GmpEncoding::default(), } } @@ -683,6 +699,7 @@ mod integration_tests { payload: vec![4, 5, 6], timeout_timestamp: 3600, memo: String::new(), + encoding: GmpEncoding::default(), }; let (router_state, _) = create_router_state_pda(); diff --git a/programs/solana/programs/ics27-gmp/src/instructions/send_call_cpi.rs b/programs/solana/programs/ics27-gmp/src/instructions/send_call_cpi.rs index fd8e29e2d..26dd4eb53 100644 --- a/programs/solana/programs/ics27-gmp/src/instructions/send_call_cpi.rs +++ b/programs/solana/programs/ics27-gmp/src/instructions/send_call_cpi.rs @@ -123,7 +123,7 @@ pub fn send_call_cpi(ctx: Context, msg: SendCallMsg) -> Result mod tests { use crate::constants::GMP_PORT_ID; use crate::errors::GMPError; - use crate::state::{GMPAppState, SendCallMsg}; + use crate::state::{GMPAppState, GmpEncoding, SendCallMsg}; use crate::test_utils::*; use anchor_lang::InstructionData; use mollusk_svm::Mollusk; @@ -190,6 +190,7 @@ mod tests { payload: vec![4, 5, 6], timeout_timestamp: 3600, memo: String::new(), + encoding: GmpEncoding::default(), } } @@ -490,7 +491,7 @@ mod tests { /// a syscall that returns 0 in Mollusk but works correctly under `ProgramTest`. #[cfg(test)] mod integration_tests { - use crate::state::{GMPAppState, SendCallMsg}; + use crate::state::{GMPAppState, GmpEncoding, SendCallMsg}; use crate::test_utils::*; use anchor_lang::InstructionData; use solana_sdk::{ @@ -509,6 +510,7 @@ mod integration_tests { payload: vec![4, 5, 6], timeout_timestamp: 3600, memo: String::new(), + encoding: GmpEncoding::default(), }; let (router_state, _) = create_router_state_pda(); diff --git a/programs/solana/programs/ics27-gmp/src/lib.rs b/programs/solana/programs/ics27-gmp/src/lib.rs index 5cbfe5ff8..829ee7dcc 100644 --- a/programs/solana/programs/ics27-gmp/src/lib.rs +++ b/programs/solana/programs/ics27-gmp/src/lib.rs @@ -1,6 +1,7 @@ use anchor_lang::prelude::*; use solana_ibc_macros::ibc_app; +pub mod abi; pub mod constants; pub mod errors; pub mod events; @@ -88,4 +89,14 @@ pub mod ics27_gmp { ) -> Result<()> { instructions::set_access_manager(ctx, new_access_manager) } + + /// Store a payload hint for ABI-encoded recv packets + pub fn store_payload_hint(ctx: Context, data: Vec) -> Result<()> { + instructions::store_payload_hint(ctx, data) + } + + /// Close a payload hint account and refund rent + pub const fn close_payload_hint(ctx: Context) -> Result<()> { + instructions::close_payload_hint(ctx) + } } diff --git a/programs/solana/programs/ics27-gmp/src/state.rs b/programs/solana/programs/ics27-gmp/src/state.rs index dc3ff55f0..95cdc81c1 100644 --- a/programs/solana/programs/ics27-gmp/src/state.rs +++ b/programs/solana/programs/ics27-gmp/src/state.rs @@ -42,6 +42,39 @@ impl GMPAppState { } } +/// Hint account storing a protobuf-encoded `GmpSolanaPayload`. +/// +/// Used by the relayer to provide the execution payload for ABI-encoded packets, +/// since ABI payloads don't contain the Solana-specific execution data. +/// +/// PDA seeds: `["payload_hint", payer_pubkey]` +#[account] +#[derive(InitSpace)] +pub struct SolanaPayloadHint { + pub bump: u8, + #[max_len(2048)] + pub data: Vec, +} + +impl SolanaPayloadHint { + pub const SEED: &'static [u8] = b"payload_hint"; + pub const MAX_DATA_LEN: usize = 2048; +} + +/// Encoding format for outbound GMP packets. +/// +/// Determines how the `GmpPacketData` is serialized and what encoding string +/// is set in the IBC payload. The sender must use the encoding the destination +/// chain's ICS27 GMP module expects. +#[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum GmpEncoding { + /// Protobuf encoding (`"application/x-protobuf"`). Used for Cosmos/Solana destinations. + #[default] + Protobuf, + /// ABI encoding (`"application/x-solidity-abi"`). Used for EVM destinations. + Abi, +} + /// Send call message (unvalidated input from user) #[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)] pub struct SendCallMsg { @@ -62,6 +95,9 @@ pub struct SendCallMsg { /// Optional memo pub memo: String, + + /// Encoding format for the IBC payload. + pub encoding: GmpEncoding, } // Re-export types from proto crate diff --git a/programs/solana/programs/ift/src/gmp_cpi.rs b/programs/solana/programs/ift/src/gmp_cpi.rs index 7bd582181..010ae0161 100644 --- a/programs/solana/programs/ift/src/gmp_cpi.rs +++ b/programs/solana/programs/ift/src/gmp_cpi.rs @@ -46,6 +46,8 @@ pub struct SendGmpCallMsg { pub timeout_timestamp: i64, pub receiver: String, pub payload: Vec, + /// Encoding format for the destination chain + pub encoding: ics27_gmp::state::GmpEncoding, } impl From for ics27_gmp::state::SendCallMsg { @@ -57,6 +59,7 @@ impl From for ics27_gmp::state::SendCallMsg { salt: vec![], // Empty salt for IFT payload: msg.payload, memo: String::new(), + encoding: msg.encoding, } } } diff --git a/programs/solana/programs/ift/src/instructions/ift_transfer.rs b/programs/solana/programs/ift/src/instructions/ift_transfer.rs index d61dd1bf7..8738d1adc 100644 --- a/programs/solana/programs/ift/src/instructions/ift_transfer.rs +++ b/programs/solana/programs/ift/src/instructions/ift_transfer.rs @@ -195,6 +195,10 @@ pub fn ift_transfer(ctx: Context, msg: IFTTransferMsg) -> Result ics27_gmp::state::GmpEncoding::Abi, + ChainOptions::Cosmos { .. } => ics27_gmp::state::GmpEncoding::Protobuf, + }, }; let sequence = crate::gmp_cpi::send_gmp_call(gmp_accounts, gmp_msg)?; diff --git a/scripts/E2ETestDeploy.s.sol b/scripts/E2ETestDeploy.s.sol index 4a1a77d44..8facbd2b1 100644 --- a/scripts/E2ETestDeploy.s.sol +++ b/scripts/E2ETestDeploy.s.sol @@ -25,6 +25,7 @@ import { Escrow } from "../contracts/utils/Escrow.sol"; import { ICS27Account } from "../contracts/utils/ICS27Account.sol"; import { TestIFT } from "../test/solidity-ibc/mocks/TestIFT.sol"; import { CosmosIFTSendCallConstructor } from "../contracts/utils/CosmosIFTSendCallConstructor.sol"; +import { SolanaIFTSendCallConstructor } from "../contracts/utils/SolanaIFTSendCallConstructor.sol"; import { SP1Verifier as SP1VerifierPlonk } from "@sp1-contracts/v5.0.0/SP1VerifierPlonk.sol"; import { SP1Verifier as SP1VerifierGroth16 } from "@sp1-contracts/v5.0.0/SP1VerifierGroth16.sol"; import { SP1MockVerifier } from "@sp1-contracts/SP1MockVerifier.sol"; @@ -53,6 +54,7 @@ contract E2ETestDeploy is Script, IICS07TendermintMsgs, DeployAccessManagerWithR address erc20; address ift; address cosmosIftConstructor; + address solanaIftConstructor; } function run() public returns (string memory) { @@ -114,6 +116,9 @@ contract E2ETestDeploy is Script, IICS07TendermintMsgs, DeployAccessManagerWithR address(new CosmosIFTSendCallConstructor(IFT_MINT_TYPE_URL, IFT_TEST_DENOM, iftIcaAddress)); } + // Deploy SolanaIFTSendCallConstructor (no constructor args) + d.solanaIftConstructor = address(new SolanaIFTSendCallConstructor()); + // Wire up access control and apps accessManagerSetTargetRoles(accessManager, d.ics26Router, d.ics20Transfer, d.ics27Gmp, true); accessManagerSetRoles( @@ -138,6 +143,7 @@ contract E2ETestDeploy is Script, IICS07TendermintMsgs, DeployAccessManagerWithR json.serialize("ics27Gmp", Strings.toHexString(d.ics27Gmp)); json.serialize("erc20", Strings.toHexString(d.erc20)); json.serialize("ift", Strings.toHexString(d.ift)); - return json.serialize("cosmosIftConstructor", Strings.toHexString(d.cosmosIftConstructor)); + json.serialize("cosmosIftConstructor", Strings.toHexString(d.cosmosIftConstructor)); + return json.serialize("solanaIftConstructor", Strings.toHexString(d.solanaIftConstructor)); } } diff --git a/test/solidity-ibc/SolanaIFTSendCallConstructorTest.t.sol b/test/solidity-ibc/SolanaIFTSendCallConstructorTest.t.sol new file mode 100644 index 000000000..a05d341d8 --- /dev/null +++ b/test/solidity-ibc/SolanaIFTSendCallConstructorTest.t.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.28; + +// solhint-disable custom-errors,gas-small-strings + +import { Test } from "forge-std/Test.sol"; + +import { SolanaIFTSendCallConstructor } from "../../contracts/utils/SolanaIFTSendCallConstructor.sol"; +import { IIFTSendCallConstructor } from "../../contracts/interfaces/IIFTSendCallConstructor.sol"; +import { IERC165 } from "@openzeppelin-contracts/utils/introspection/IERC165.sol"; + +contract SolanaIFTSendCallConstructorTest is Test { + SolanaIFTSendCallConstructor public constructor_; + + // Valid 32-byte Solana pubkey as hex + string internal constant VALID_RECEIVER = "0x06ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9"; + + function setUp() public { + constructor_ = new SolanaIFTSendCallConstructor(); + } + + function test_constructMintCall_validHex() public view { + bytes memory result = constructor_.constructMintCall(VALID_RECEIVER, 1_000_000); + (bytes32 receiver, uint256 amount) = abi.decode(result, (bytes32, uint256)); + assertEq(receiver, bytes32(hex"06ddf6e1d765a193d9cbe146ceeb79ac1cb485ed5f5b37913a8cf5857eff00a9")); + assertEq(amount, 1_000_000); + } + + function test_constructMintCall_invalidHex_reverts() public { + vm.expectRevert(); + constructor_.constructMintCall("0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", 1000); + } + + function test_constructMintCall_wrongLength_reverts() public { + vm.expectRevert(); + constructor_.constructMintCall("0x1234", 1000); + } + + function testFuzz_constructMintCall_anyAmount(uint256 amount) public view { + bytes memory result = constructor_.constructMintCall(VALID_RECEIVER, amount); + (, uint256 decoded) = abi.decode(result, (bytes32, uint256)); + assertEq(decoded, amount); + } + + function test_supportsInterface() public view { + assertTrue(constructor_.supportsInterface(type(IIFTSendCallConstructor).interfaceId)); + assertTrue(constructor_.supportsInterface(type(IERC165).interfaceId)); + } +}