Address: 0x652d9299715E22820222247E8b780144771404Fe
Balance (XRP): 0 XRP
Bytecode: 0x608060405234801561000f575f5ffd5b50600436106100e8575f3560e01c8063a25acb251161008a578063d9acbe6b11610064578063d9acbe6b14610240578063dac6c58714610270578063ebc27b821461028c578063f4364de6146102bc576100e8565b8063a25acb25146101c4578063d4c9c5c4146101f4578063d4df101814610210576100e8565b80633808a90b116100c65780633808a90b14610140578063520994f11461017057806356cd2b921461018c5780639630aff8146101a8576100e8565b8063065d6c69146100ec5780631b31f01b146101085780632f28c69d14610124575b5f5ffd5b610106600480360381019061010191906113ac565b6102ec565b005b610122600480360381019061011d9190611439565b6103c3565b005b61013e60048036038101906101399190611539565b610469565b005b61015a6004803603810190610155919061160f565b610515565b604051610167919061168a565b60405180910390f35b61018a600480360381019061018591906116a3565b610610565b005b6101a660048036038101906101a191906117bf565b6106b4565b005b6101c260048036038101906101bd9190611819565b610793565b005b6101de60048036038101906101d991906116a3565b610924565b6040516101eb9190611871565b60405180910390f35b61020e6004803603810190610209919061194a565b61094e565b005b61022a60048036038101906102259190611a27565b610b1b565b604051610237919061168a565b60405180910390f35b61025a60048036038101906102559190611a52565b610cde565b604051610267919061168a565b60405180910390f35b61028a60048036038101906102859190611539565b610d09565b005b6102a660048036038101906102a19190611a90565b610e51565b6040516102b39190611aca565b60405180910390f35b6102d660048036038101906102d19190611a90565b610e80565b6040516102e39190611b9a565b60405180910390f35b813373ffffffffffffffffffffffffffffffffffffffff165f5f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610381576040517f7c2411e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b82518110156103bd576103b0848483815181106103a3576103a2611bba565b5b6020026020010151610ee7565b8080600101915050610383565b50505050565b823373ffffffffffffffffffffffffffffffffffffffff165f5f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610458576040517f7c2411e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610463848484610fe9565b50505050565b813373ffffffffffffffffffffffffffffffffffffffff165f5f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146104fe576040517f7c2411e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105108361050b84611187565b610ee7565b505050565b5f5f61057f848060a0019061052a9190611bf3565b805f01906105389190611c1a565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f81840152601f19601f82011690508083019250505050505050611187565b905060015f8481526020019081526020015f205f8281526020019081526020015f205f9054906101000a900460ff16156105d7576105cf85856105c190611ef2565b6111b690919063ffffffff16565b915050610609565b6040517fd7abef4e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b9392505050565b813373ffffffffffffffffffffffffffffffffffffffff165f5f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146106a5576040517f7c2411e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6106af8383610ee7565b505050565b813373ffffffffffffffffffffffffffffffffffffffff165f5f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610749576040517f7c2411e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b825181101561078d576107808461077b85848151811061076e5761076d611bba565b5b6020026020010151611187565b610ee7565b808060010191505061074b565b50505050565b813373ffffffffffffffffffffffffffffffffffffffff165f5f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610828576040517f7c2411e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff160361088d576040517fff7d580d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b815f5f8581526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff16837f7869f0fa3d7efaa83c69bf6de987567787178b8d7e41fea0cadfea68a2e27de560405160405180910390a3505050565b6001602052815f5260405f20602052805f5260405f205f915091509054906101000a900460ff1681565b823373ffffffffffffffffffffffffffffffffffffffff165f5f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16146109e3576040517f7c2411e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8151835114610a1e576040517f526c768700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f5b60018351610a2e9190611f31565b811015610abc5782600182610a439190611f64565b81518110610a5457610a53611bba565b5b6020026020010151838281518110610a6f57610a6e611bba565b5b60200260200101511015610aaf576040517f526c768700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8080600101915050610a20565b505f5b8351811015610b1457610b0785858381518110610adf57610ade611bba565b5b6020026020010151858481518110610afa57610af9611bba565b5b6020026020010151610fe9565b8080600101915050610abf565b5050505050565b5f5f3390505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610b85576040517fff7d580d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8281604051602001610b98929190611ffc565b604051602081830303815290604052610bb090612060565b91505f73ffffffffffffffffffffffffffffffffffffffff165f5f8481526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610c46576040517ffeabeb4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805f5f8481526020019081526020015f205f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16827f0f01cd0734cc8ed416ac6ca25cb4fe02dbab63f1236af3858deb14a56663623860405160405180910390a350919050565b6002602052815f5260405f208181548110610cf7575f80fd5b905f5260205f20015f91509150505481565b813373ffffffffffffffffffffffffffffffffffffffff165f5f8381526020019081526020015f205f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614610d9e576040517f7c2411e900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f610da883611187565b90505f5b60025f8681526020019081526020015f2080549050811015610e19578160025f8781526020019081526020015f208281548110610dec57610deb611bba565b5b905f5260205f20015403610e0c57610e05858383610fe9565b5050610e4c565b8080600101915050610dac565b506040517f4745a4ee00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b5f602052805f5260405f205f915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b606060025f8381526020019081526020015f20805480602002602001604051908101604052809291908181526020018280548015610edb57602002820191905f5260205f20905b815481526020019060010190808311610ec7575b50505050509050919050565b60015f8381526020019081526020015f205f8281526020019081526020015f205f9054906101000a900460ff1615610f4b576040517f2d86c03a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001805f8481526020019081526020015f205f8381526020019081526020015f205f6101000a81548160ff02191690831515021790555060025f8381526020019081526020015f2081908060018154018082558091505060019003905f5260205f20015f909190919091505580827fceac3080d7874c4bba80ceae14b6aa96e0df9d4f673d9e5a352d66444f50206760405160405180910390a35050565b60025f8481526020019081526020015f20805490508110158061103757508160025f8581526020019081526020015f20828154811061102b5761102a611bba565b5b905f5260205f20015414155b1561106e576040517f526c768700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f60015f8581526020019081526020015f205f8481526020019081526020015f205f6101000a81548160ff02191690831515021790555060025f8481526020019081526020015f20600160025f8681526020019081526020015f20805490506110d79190611f31565b815481106110e8576110e7611bba565b5b905f5260205f20015460025f8581526020019081526020015f20828154811061111457611113611bba565b5b905f5260205f20018190555060025f8481526020019081526020015f20805480611141576111406120c6565b5b600190038181905f5260205f20015f9055905581837f13d94de535e503661de3c84adf637590f269f37c8d4ed5daabae98376a442a6a60405160405180910390a3505050565b5f816040516020016111999190612145565b604051602081830303815290604052805190602001209050919050565b5f6040516020016111c6906121a5565b6040516020818303038152906040528051906020012082846060015185604001516040516020016111fa94939291906121d9565b60405160208183030381529060405280519060200120905092915050565b5f604051905090565b5f5ffd5b5f5ffd5b5f819050919050565b61123b81611229565b8114611245575f5ffd5b50565b5f8135905061125681611232565b92915050565b5f5ffd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6112a682611260565b810181811067ffffffffffffffff821117156112c5576112c4611270565b5b80604052505050565b5f6112d7611218565b90506112e3828261129d565b919050565b5f67ffffffffffffffff82111561130257611301611270565b5b602082029050602081019050919050565b5f5ffd5b5f611329611324846112e8565b6112ce565b9050808382526020820190506020840283018581111561134c5761134b611313565b5b835b8181101561137557806113618882611248565b84526020840193505060208101905061134e565b5050509392505050565b5f82601f8301126113935761139261125c565b5b81356113a3848260208601611317565b91505092915050565b5f5f604083850312156113c2576113c1611221565b5b5f6113cf85828601611248565b925050602083013567ffffffffffffffff8111156113f0576113ef611225565b5b6113fc8582860161137f565b9150509250929050565b5f819050919050565b61141881611406565b8114611422575f5ffd5b50565b5f813590506114338161140f565b92915050565b5f5f5f606084860312156114505761144f611221565b5b5f61145d86828701611248565b935050602061146e86828701611248565b925050604061147f86828701611425565b9150509250925092565b5f5ffd5b5f67ffffffffffffffff8211156114a7576114a6611270565b5b6114b082611260565b9050602081019050919050565b828183375f83830152505050565b5f6114dd6114d88461148d565b6112ce565b9050828152602081018484840111156114f9576114f8611489565b5b6115048482856114bd565b509392505050565b5f82601f8301126115205761151f61125c565b5b81356115308482602086016114cb565b91505092915050565b5f5f6040838503121561154f5761154e611221565b5b5f61155c85828601611248565b925050602083013567ffffffffffffffff81111561157d5761157c611225565b5b6115898582860161150c565b9150509250929050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6115bc82611593565b9050919050565b6115cc816115b2565b81146115d6575f5ffd5b50565b5f813590506115e7816115c3565b92915050565b5f5ffd5b5f60c08284031215611606576116056115ed565b5b81905092915050565b5f5f5f6060848603121561162657611625611221565b5b5f611633868287016115d9565b935050602084013567ffffffffffffffff81111561165457611653611225565b5b611660868287016115f1565b925050604061167186828701611248565b9150509250925092565b61168481611229565b82525050565b5f60208201905061169d5f83018461167b565b92915050565b5f5f604083850312156116b9576116b8611221565b5b5f6116c685828601611248565b92505060206116d785828601611248565b9150509250929050565b5f67ffffffffffffffff8211156116fb576116fa611270565b5b602082029050602081019050919050565b5f61171e611719846116e1565b6112ce565b9050808382526020820190506020840283018581111561174157611740611313565b5b835b8181101561178857803567ffffffffffffffff8111156117665761176561125c565b5b808601611773898261150c565b85526020850194505050602081019050611743565b5050509392505050565b5f82601f8301126117a6576117a561125c565b5b81356117b684826020860161170c565b91505092915050565b5f5f604083850312156117d5576117d4611221565b5b5f6117e285828601611248565b925050602083013567ffffffffffffffff81111561180357611802611225565b5b61180f85828601611792565b9150509250929050565b5f5f6040838503121561182f5761182e611221565b5b5f61183c85828601611248565b925050602061184d858286016115d9565b9150509250929050565b5f8115159050919050565b61186b81611857565b82525050565b5f6020820190506118845f830184611862565b92915050565b5f67ffffffffffffffff8211156118a4576118a3611270565b5b602082029050602081019050919050565b5f6118c76118c28461188a565b6112ce565b905080838252602082019050602084028301858111156118ea576118e9611313565b5b835b8181101561191357806118ff8882611425565b8452602084019350506020810190506118ec565b5050509392505050565b5f82601f8301126119315761193061125c565b5b81356119418482602086016118b5565b91505092915050565b5f5f5f6060848603121561196157611960611221565b5b5f61196e86828701611248565b935050602084013567ffffffffffffffff81111561198f5761198e611225565b5b61199b8682870161137f565b925050604084013567ffffffffffffffff8111156119bc576119bb611225565b5b6119c88682870161191d565b9150509250925092565b5f7fffffffffffffffffffffffff000000000000000000000000000000000000000082169050919050565b611a06816119d2565b8114611a10575f5ffd5b50565b5f81359050611a21816119fd565b92915050565b5f60208284031215611a3c57611a3b611221565b5b5f611a4984828501611a13565b91505092915050565b5f5f60408385031215611a6857611a67611221565b5b5f611a7585828601611248565b9250506020611a8685828601611425565b9150509250929050565b5f60208284031215611aa557611aa4611221565b5b5f611ab284828501611248565b91505092915050565b611ac4816115b2565b82525050565b5f602082019050611add5f830184611abb565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b611b1581611229565b82525050565b5f611b268383611b0c565b60208301905092915050565b5f602082019050919050565b5f611b4882611ae3565b611b528185611aed565b9350611b5d83611afd565b805f5b83811015611b8d578151611b748882611b1b565b9750611b7f83611b32565b925050600181019050611b60565b5085935050505092915050565b5f6020820190508181035f830152611bb28184611b3e565b905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5f5ffd5b5f5ffd5b5f5ffd5b5f82356001604003833603038112611c0e57611c0d611be7565b5b80830191505092915050565b5f5f83356001602003843603038112611c3657611c35611be7565b5b80840192508235915067ffffffffffffffff821115611c5857611c57611beb565b5b602083019250600182023603831315611c7457611c73611bef565b5b509250929050565b5f5ffd5b5f5ffd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b611cb881611c84565b8114611cc2575f5ffd5b50565b5f81359050611cd381611caf565b92915050565b5f67ffffffffffffffff821115611cf357611cf2611270565b5b611cfc82611260565b9050602081019050919050565b5f611d1b611d1684611cd9565b6112ce565b905082815260208101848484011115611d3757611d36611489565b5b611d428482856114bd565b509392505050565b5f82601f830112611d5e57611d5d61125c565b5b8135611d6e848260208601611d09565b91505092915050565b5f67ffffffffffffffff82169050919050565b611d9381611d77565b8114611d9d575f5ffd5b50565b5f81359050611dae81611d8a565b92915050565b5f60408284031215611dc957611dc8611c7c565b5b611dd360406112ce565b90505f82013567ffffffffffffffff811115611df257611df1611c80565b5b611dfe8482850161150c565b5f830152506020611e1184828501611da0565b60208301525092915050565b5f60c08284031215611e3257611e31611c7c565b5b611e3c60c06112ce565b90505f611e4b848285016115d9565b5f830152506020611e5e84828501611cc5565b6020830152506040611e7284828501611248565b6040830152506060611e8684828501611248565b606083015250608082013567ffffffffffffffff811115611eaa57611ea9611c80565b5b611eb684828501611d4a565b60808301525060a082013567ffffffffffffffff811115611eda57611ed9611c80565b5b611ee684828501611db4565b60a08301525092915050565b5f611efd3683611e1d565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f611f3b82611406565b9150611f4683611406565b9250828203905081811115611f5e57611f5d611f04565b5b92915050565b5f611f6e82611406565b9150611f7983611406565b9250828201905080821115611f9157611f90611f04565b5b92915050565b5f819050919050565b611fb1611fac826119d2565b611f97565b82525050565b5f8160601b9050919050565b5f611fcd82611fb7565b9050919050565b5f611fde82611fc3565b9050919050565b611ff6611ff1826115b2565b611fd4565b82525050565b5f6120078285611fa0565b600c820191506120178284611fe5565b6014820191508190509392505050565b5f81519050919050565b5f819050602082019050919050565b5f61204b8251611229565b80915050919050565b5f82821b905092915050565b5f61206a82612027565b8261207484612031565b905061207f81612040565b925060208210156120bf576120ba7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83602003600802612054565b831692505b5050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b5f81519050919050565b5f81905092915050565b8281835e5f83830152505050565b5f61211f826120f3565b61212981856120fd565b9350612139818560208601612107565b80840191505092915050565b5f6121508284612115565b915081905092915050565b7f616363657074496d706c696369745265717565737400000000000000000000005f82015250565b5f61218f6015836120fd565b915061219a8261215b565b601582019050919050565b5f6121af82612183565b9150819050919050565b5f819050919050565b6121d36121ce82611229565b6121b9565b82525050565b5f6121e482876121c2565b6020820191506121f48286611fe5565b60148201915061220482856121c2565b60208201915061221482846121c2565b6020820191508190509594505050505056fea26469706673582212201c1c5f937a09cefe946e373a867e5691109f49a41dec6c6dd2b14c92e28398bd64736f6c634300081c0033
Attestation.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { LibBytes } from "../../../utils/LibBytes.sol"; import { ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX } from "./ISignalsImplicitMode.sol"; using LibBytes for bytes; /// @notice Attestation for a specific session /// @param approvedSigner Address of the approved signer /// @param identityType Identity type /// @param issuerHash Hash of the issuer /// @param audienceHash Hash of the audience /// @param applicationData Unspecified application data /// @param authData Auth data struct Attestation { address approvedSigner; bytes4 identityType; bytes32 issuerHash; bytes32 audienceHash; bytes applicationData; AuthData authData; } /// @notice Auth data for an attestation /// @param redirectUrl Authorization redirect URL /// @param issuedAt Timestamp of the attestation issuance struct AuthData { string redirectUrl; uint64 issuedAt; } /// @title LibAttestation /// @author Michael Standen /// @notice Library for attestation management library LibAttestation { /// @notice Hashes an attestation function toHash( Attestation memory attestation ) internal pure returns (bytes32) { return keccak256(toPacked(attestation)); } /// @notice Decodes an attestation from a packed bytes array /// @param encoded The packed bytes array /// @param pointer The pointer to the start of the attestation /// @return attestation The decoded attestation /// @return newPointer The new pointer to the end of the attestation function fromPacked( bytes calldata encoded, uint256 pointer ) internal pure returns (Attestation memory attestation, uint256 newPointer) { newPointer = pointer; (attestation.approvedSigner, newPointer) = encoded.readAddress(newPointer); (attestation.identityType, newPointer) = encoded.readBytes4(newPointer); (attestation.issuerHash, newPointer) = encoded.readBytes32(newPointer); (attestation.audienceHash, newPointer) = encoded.readBytes32(newPointer); // Application data (arbitrary bytes) uint256 dataSize; (dataSize, newPointer) = encoded.readUint24(newPointer); attestation.applicationData = encoded[newPointer:newPointer + dataSize]; newPointer += dataSize; // Auth data (attestation.authData, newPointer) = fromPackedAuthData(encoded, newPointer); return (attestation, newPointer); } /// @notice Decodes the auth data from a packed bytes /// @param encoded The packed bytes containing the auth data /// @param pointer The pointer to the start of the auth data within the encoded data /// @return authData The decoded auth data /// @return newPointer The pointer to the end of the auth data within the encoded data function fromPackedAuthData( bytes calldata encoded, uint256 pointer ) internal pure returns (AuthData memory authData, uint256 newPointer) { uint24 redirectUrlLength; (redirectUrlLength, pointer) = encoded.readUint24(pointer); authData.redirectUrl = string(encoded[pointer:pointer + redirectUrlLength]); pointer += redirectUrlLength; (authData.issuedAt, pointer) = encoded.readUint64(pointer); return (authData, pointer); } /// @notice Encodes an attestation into a packed bytes array /// @param attestation The attestation to encode /// @return encoded The packed bytes array function toPacked( Attestation memory attestation ) internal pure returns (bytes memory encoded) { return abi.encodePacked( attestation.approvedSigner, attestation.identityType, attestation.issuerHash, attestation.audienceHash, uint24(attestation.applicationData.length), attestation.applicationData, toPackAuthData(attestation.authData) ); } /// @notice Encodes the auth data into a packed bytes array /// @param authData The auth data to encode /// @return encoded The packed bytes array function toPackAuthData( AuthData memory authData ) internal pure returns (bytes memory encoded) { return abi.encodePacked(uint24(bytes(authData.redirectUrl).length), bytes(authData.redirectUrl), authData.issuedAt); } /// @notice Generates the implicit request magic return value /// @param attestation The attestation /// @param wallet The wallet /// @return magic The expected implicit request magic function generateImplicitRequestMagic(Attestation memory attestation, address wallet) internal pure returns (bytes32) { return keccak256( abi.encodePacked(ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX, wallet, attestation.audienceHash, attestation.issuerHash) ); } }
ISignalsImplicitMode.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { Payload } from "../../../modules/Payload.sol"; import { Attestation } from "./Attestation.sol"; /// @dev Magic prefix for the implicit request bytes32 constant ACCEPT_IMPLICIT_REQUEST_MAGIC_PREFIX = keccak256(abi.encodePacked("acceptImplicitRequest")); /// @title ISignalsImplicitMode /// @author Agustin Aguilar, Michael Standen /// @notice Interface for the contracts that support implicit mode validation interface ISignalsImplicitMode { /// @notice Determines if an implicit request is valid /// @param wallet The wallet's address /// @param attestation The attestation data /// @param call The call to validate /// @return magic The hash of the implicit request if valid function acceptImplicitRequest( address wallet, Attestation calldata attestation, Payload.Call calldata call ) external view returns (bytes32 magic); }
Payload.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.27; import { LibBytes } from "../utils/LibBytes.sol"; using LibBytes for bytes; /// @title Payload /// @author Agustin Aguilar, Michael Standen, William Hua /// @notice Library for encoding and decoding payloads library Payload { /// @notice Error thrown when the kind is invalid error InvalidKind(uint8 kind); /// @dev keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") bytes32 private constant EIP712_DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; /// @dev keccak256("Sequence Wallet") bytes32 private constant EIP712_DOMAIN_NAME_SEQUENCE = 0x4aa45ca7ad825ceb1bf35643f0a58c295239df563b1b565c2485f96477c56318; /// @dev keccak256("3") bytes32 private constant EIP712_DOMAIN_VERSION_SEQUENCE = 0x2a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de; function domainSeparator(bool _noChainId, address _wallet) internal view returns (bytes32 _domainSeparator) { return keccak256( abi.encode( EIP712_DOMAIN_TYPEHASH, EIP712_DOMAIN_NAME_SEQUENCE, EIP712_DOMAIN_VERSION_SEQUENCE, _noChainId ? uint256(0) : uint256(block.chainid), _wallet ) ); } /// @dev keccak256("Call(address to,uint256 value,bytes data,uint256 gasLimit,bool delegateCall,bool onlyFallback,uint256 behaviorOnError)") bytes32 private constant CALL_TYPEHASH = 0x0603985259a953da1f65a522f589c17bd1d0117ec1d3abb7c0788aef251ef437; /// @dev keccak256("Calls(Call[] calls,uint256 space,uint256 nonce,address[] wallets)Call(address to,uint256 value,bytes data,uint256 gasLimit,bool delegateCall,bool onlyFallback,uint256 behaviorOnError)") bytes32 private constant CALLS_TYPEHASH = 0x11e1e4079a79a66e4ade50033cfe2678cdd5341d2dfe5ef9513edb1a0be147a2; /// @dev keccak256("Message(bytes message,address[] wallets)") bytes32 private constant MESSAGE_TYPEHASH = 0xe19a3b94fc3c7ece3f890d98a99bc422615537a08dea0603fa8425867d87d466; /// @dev keccak256("ConfigUpdate(bytes32 imageHash,address[] wallets)") bytes32 private constant CONFIG_UPDATE_TYPEHASH = 0x11fdeb7e8373a1aa96bfac8d0ea91526b2c5d15e5cee20e0543e780258f3e8e4; /// @notice Kind of transaction uint8 public constant KIND_TRANSACTIONS = 0x00; /// @notice Kind of digest uint8 public constant KIND_MESSAGE = 0x01; /// @notice Kind of config update uint8 public constant KIND_CONFIG_UPDATE = 0x02; /// @notice Kind of message uint8 public constant KIND_DIGEST = 0x03; /// @notice Behavior on error: ignore error uint8 public constant BEHAVIOR_IGNORE_ERROR = 0x00; /// @notice Behavior on error: revert on error uint8 public constant BEHAVIOR_REVERT_ON_ERROR = 0x01; /// @notice Behavior on error: abort on error uint8 public constant BEHAVIOR_ABORT_ON_ERROR = 0x02; /// @notice Payload call information /// @param to Address of the target contract /// @param value Value to send with the call /// @param data Data to send with the call /// @param gasLimit Gas limit for the call /// @param delegateCall If the call is a delegate call /// @param onlyFallback If the call should only be executed in an error scenario /// @param behaviorOnError Behavior on error struct Call { address to; uint256 value; bytes data; uint256 gasLimit; bool delegateCall; bool onlyFallback; uint256 behaviorOnError; } /// @notice Decoded payload /// @param kind Kind of payload /// @param noChainId If the chain ID should be omitted /// @param calls Array of calls (transaction kind) /// @param space Nonce space for the calls (transaction kind) /// @param nonce Nonce value for the calls (transaction kind) /// @param message Message to validate (message kind) /// @param imageHash Image hash to update to (config update kind) /// @param digest Digest to validate (digest kind) /// @param parentWallets Parent wallets struct Decoded { uint8 kind; bool noChainId; // Transaction kind Call[] calls; uint256 space; uint256 nonce; // Message kind // TODO: Maybe native 721 ? bytes message; // Config update kind bytes32 imageHash; // Digest kind for 1271 bytes32 digest; // Parent wallets address[] parentWallets; } function fromMessage( bytes memory message ) internal pure returns (Decoded memory _decoded) { _decoded.kind = KIND_MESSAGE; _decoded.message = message; } function fromConfigUpdate( bytes32 imageHash ) internal pure returns (Decoded memory _decoded) { _decoded.kind = KIND_CONFIG_UPDATE; _decoded.imageHash = imageHash; } function fromDigest( bytes32 digest ) internal pure returns (Decoded memory _decoded) { _decoded.kind = KIND_DIGEST; _decoded.digest = digest; } function fromPackedCalls( bytes calldata packed ) internal view returns (Decoded memory _decoded) { _decoded.kind = KIND_TRANSACTIONS; // Read the global flag (uint256 globalFlag, uint256 pointer) = packed.readFirstUint8(); // First bit determines if space is zero or not if (globalFlag & 0x01 == 0x01) { _decoded.space = 0; } else { (_decoded.space, pointer) = packed.readUint160(pointer); } // Next 3 bits determine the size of the nonce uint256 nonceSize = (globalFlag >> 1) & 0x07; if (nonceSize > 0) { // Read the nonce (_decoded.nonce, pointer) = packed.readUintX(pointer, nonceSize); } uint256 numCalls; // Bit 5 determines if the batch contains a single call if (globalFlag & 0x10 == 0x10) { numCalls = 1; } else { // Bit 6 determines if the number of calls uses 1 byte or 2 bytes if (globalFlag & 0x20 == 0x20) { (numCalls, pointer) = packed.readUint16(pointer); } else { (numCalls, pointer) = packed.readUint8(pointer); } } // Read the calls _decoded.calls = new Call[](numCalls); for (uint256 i = 0; i < numCalls; i++) { uint8 flags; (flags, pointer) = packed.readUint8(pointer); // First bit determines if this is a call to self // or a call to another address if (flags & 0x01 == 0x01) { // Call to self _decoded.calls[i].to = address(this); } else { // Call to another address (_decoded.calls[i].to, pointer) = packed.readAddress(pointer); } // Second bit determines if the call has value or not if (flags & 0x02 == 0x02) { (_decoded.calls[i].value, pointer) = packed.readUint256(pointer); } // Third bit determines if the call has data or not if (flags & 0x04 == 0x04) { // 3 bytes determine the size of the calldata uint256 calldataSize; (calldataSize, pointer) = packed.readUint24(pointer); _decoded.calls[i].data = packed[pointer:pointer + calldataSize]; pointer += calldataSize; } // Fourth bit determines if the call has a gas limit or not if (flags & 0x08 == 0x08) { (_decoded.calls[i].gasLimit, pointer) = packed.readUint256(pointer); } // Fifth bit determines if the call is a delegate call or not _decoded.calls[i].delegateCall = (flags & 0x10 == 0x10); // Sixth bit determines if the call is fallback only _decoded.calls[i].onlyFallback = (flags & 0x20 == 0x20); // Last 2 bits are directly mapped to the behavior on error _decoded.calls[i].behaviorOnError = (flags & 0xC0) >> 6; } } function hashCall( Call memory c ) internal pure returns (bytes32) { return keccak256( abi.encode( CALL_TYPEHASH, c.to, c.value, keccak256(c.data), c.gasLimit, c.delegateCall, c.onlyFallback, c.behaviorOnError ) ); } function hashCalls( Call[] memory calls ) internal pure returns (bytes32) { // In EIP712, an array is often hashed as the keccak256 of the concatenated // hashes of each item. So we hash each Call, pack them, and hash again. bytes32[] memory callHashes = new bytes32[](calls.length); for (uint256 i = 0; i < calls.length; i++) { callHashes[i] = hashCall(calls[i]); } return keccak256(abi.encodePacked(callHashes)); } function toEIP712( Decoded memory _decoded ) internal pure returns (bytes32) { bytes32 walletsHash = keccak256(abi.encodePacked(_decoded.parentWallets)); if (_decoded.kind == KIND_TRANSACTIONS) { bytes32 callsHash = hashCalls(_decoded.calls); // The top-level struct for Calls might be something like: // Calls(bytes32 callsHash,uint256 space,uint256 nonce,bytes32 walletsHash) return keccak256(abi.encode(CALLS_TYPEHASH, callsHash, _decoded.space, _decoded.nonce, walletsHash)); } else if (_decoded.kind == KIND_MESSAGE) { // If you define your top-level as: Message(bytes32 messageHash,bytes32 walletsHash) return keccak256(abi.encode(MESSAGE_TYPEHASH, keccak256(_decoded.message), walletsHash)); } else if (_decoded.kind == KIND_CONFIG_UPDATE) { // Top-level: ConfigUpdate(bytes32 imageHash,bytes32 walletsHash) return keccak256(abi.encode(CONFIG_UPDATE_TYPEHASH, _decoded.imageHash, walletsHash)); } else if (_decoded.kind == KIND_DIGEST) { // Top-level: Use MESSAGE_TYPEHASH but assume the digest is already the hashed message return keccak256(abi.encode(MESSAGE_TYPEHASH, _decoded.digest, walletsHash)); } else { // Unknown kind revert InvalidKind(_decoded.kind); } } function hash( Decoded memory _decoded ) internal view returns (bytes32) { bytes32 domain = domainSeparator(_decoded.noChainId, address(this)); bytes32 structHash = toEIP712(_decoded); return keccak256(abi.encodePacked("\x19\x01", domain, structHash)); } function hashFor(Decoded memory _decoded, address _wallet) internal view returns (bytes32) { bytes32 domain = domainSeparator(_decoded.noChainId, _wallet); bytes32 structHash = toEIP712(_decoded); return keccak256(abi.encodePacked("\x19\x01", domain, structHash)); } }
LibBytes.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.18; /// @title Library for reading data from bytes arrays /// @author Agustin Aguilar (aa@horizon.io), Michael Standen (mstan@horizon.io) /// @notice This library contains functions for reading data from bytes arrays. /// @dev These functions do not check if the input index is within the bounds of the data array. /// @dev Reading out of bounds may return dirty values. library LibBytes { function readFirstUint8( bytes calldata _data ) internal pure returns (uint8 a, uint256 newPointer) { assembly { let word := calldataload(_data.offset) a := shr(248, word) newPointer := 1 } } function readUint8(bytes calldata _data, uint256 _index) internal pure returns (uint8 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(248, word) newPointer := add(_index, 1) } } function readUint16(bytes calldata _data, uint256 _index) internal pure returns (uint16 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(240, word) newPointer := add(_index, 2) } } function readUint24(bytes calldata _data, uint256 _index) internal pure returns (uint24 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(232, word) newPointer := add(_index, 3) } } function readUint64(bytes calldata _data, uint256 _index) internal pure returns (uint64 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(192, word) newPointer := add(_index, 8) } } function readUint160(bytes calldata _data, uint256 _index) internal pure returns (uint160 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := shr(96, word) newPointer := add(_index, 20) } } function readUint256(bytes calldata _data, uint256 _index) internal pure returns (uint256 a, uint256 newPointer) { assembly { a := calldataload(add(_index, _data.offset)) newPointer := add(_index, 32) } } function readUintX( bytes calldata _data, uint256 _index, uint256 _length ) internal pure returns (uint256 a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) let shift := sub(256, mul(_length, 8)) a := and(shr(shift, word), sub(shl(mul(8, _length), 1), 1)) newPointer := add(_index, _length) } } function readBytes4(bytes calldata _data, uint256 _pointer) internal pure returns (bytes4 a, uint256 newPointer) { assembly { let word := calldataload(add(_pointer, _data.offset)) a := and(word, 0xffffffff00000000000000000000000000000000000000000000000000000000) newPointer := add(_pointer, 4) } } function readBytes32(bytes calldata _data, uint256 _pointer) internal pure returns (bytes32 a, uint256 newPointer) { assembly { a := calldataload(add(_pointer, _data.offset)) newPointer := add(_pointer, 32) } } function readAddress(bytes calldata _data, uint256 _index) internal pure returns (address a, uint256 newPointer) { assembly { let word := calldataload(add(_index, _data.offset)) a := and(shr(96, word), 0xffffffffffffffffffffffffffffffffffffffff) newPointer := add(_index, 20) } } /// @dev ERC-2098 Compact Signature function readRSVCompact( bytes calldata _data, uint256 _index ) internal pure returns (bytes32 r, bytes32 s, uint8 v, uint256 newPointer) { uint256 yParityAndS; assembly { r := calldataload(add(_index, _data.offset)) yParityAndS := calldataload(add(_index, add(_data.offset, 32))) newPointer := add(_index, 64) } uint256 yParity = uint256(yParityAndS >> 255); s = bytes32(uint256(yParityAndS) & ((1 << 255) - 1)); v = uint8(yParity) + 27; } }
IImplicitProjectRegistry.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import { IImplicitProjectValidation } from "./IImplicitProjectValidation.sol"; /// @title IImplicitProjectRegistry /// @author Michael Standen /// @notice Interface for the registry of projects supporting implicit sessions interface IImplicitProjectRegistry is IImplicitProjectValidation { /// @notice Claim a project /// @param projectIdUpper The project id upper /// @return projectId The concatenation of the `projectIdUpper` and the `msg.sender` function claimProject( bytes12 projectIdUpper ) external returns (bytes32 projectId); /// @notice Transfer a project /// @param projectId The project id /// @param newOwner The new owner function transferProject(bytes32 projectId, address newOwner) external; /// @notice Add a project URL /// @param projectId The project id /// @param projectUrl The project URL function addProjectUrl(bytes32 projectId, string memory projectUrl) external; /// @notice Remove a project URL /// @param projectId The project id /// @param projectUrl The project URL function removeProjectUrl(bytes32 projectId, string memory projectUrl) external; /// @notice List project URLs /// @param projectId The project id /// @return projectUrls The project URLs function listProjectUrls( bytes32 projectId ) external view returns (bytes32[] memory); /// @notice Not project owner error error NotProjectOwner(); /// @notice Project already claimed error error ProjectAlreadyClaimed(); /// @notice Invalid project owner error error InvalidProjectOwner(); /// @notice Project URL not found error error ProjectUrlNotFound(); /// @notice Project URL already exists error error ProjectUrlAlreadyExists(); /// @notice Invalid project URL index error error InvalidProjectUrlIndex(); /// @notice Emitted when a project is claimed event ProjectClaimed(bytes32 indexed projectId, address indexed owner); /// @notice Emitted when a project owner is transferred event ProjectOwnerTransferred(bytes32 indexed projectId, address indexed newOwner); /// @notice Emitted when a project URL is added event ProjectUrlAdded(bytes32 indexed projectId, bytes32 indexed urlHash); /// @notice Emitted when a project URL is removed event ProjectUrlRemoved(bytes32 indexed projectId, bytes32 indexed urlHash); }
IImplicitProjectValidation.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import { Attestation } from "sequence-v3/src/extensions/sessions/implicit/Attestation.sol"; /// @title IImplicitProjectValidation /// @author Michael Standen /// @notice Interface for contracts supporting validation of implicit sessions for projects interface IImplicitProjectValidation { /// @notice Invalid redirect url error error InvalidRedirectUrl(); /// @notice Check if a project has a code /// @param wallet The wallet address /// @param attestation The attestation /// @param projectId The project id /// @return magic The attestation magic bytes for the wallet address function validateAttestation( address wallet, Attestation calldata attestation, bytes32 projectId ) external view returns (bytes32); }
ImplicitProjectRegistry.sol
// SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.8.13; import { IImplicitProjectRegistry } from "./IImplicitProjectRegistry.sol"; import { IImplicitProjectValidation } from "./IImplicitProjectValidation.sol"; import { Attestation, LibAttestation } from "sequence-v3/src/extensions/sessions/implicit/Attestation.sol"; /// @title ImplicitProjectRegistry /// @author Michael Standen /// @notice Registry of projects supporting implicit sessions contract ImplicitProjectRegistry is IImplicitProjectRegistry { using LibAttestation for Attestation; /// @notice Project owner mapping(bytes32 => address) public projectOwner; /// @notice Project URLs mapping(bytes32 => mapping(bytes32 => bool)) public isProjectUrl; mapping(bytes32 => bytes32[]) public projectUrlsList; modifier onlyProjectOwner( bytes32 projectId ) { if (projectOwner[projectId] != msg.sender) { revert IImplicitProjectRegistry.NotProjectOwner(); } _; } /// @inheritdoc IImplicitProjectRegistry function claimProject( bytes12 projectIdUpper ) public returns (bytes32 projectId) { address owner = msg.sender; if (owner == address(0)) { revert IImplicitProjectRegistry.InvalidProjectOwner(); } projectId = bytes32(abi.encodePacked(projectIdUpper, owner)); if (projectOwner[projectId] != address(0)) { revert IImplicitProjectRegistry.ProjectAlreadyClaimed(); } projectOwner[projectId] = owner; emit IImplicitProjectRegistry.ProjectClaimed(projectId, owner); return projectId; } /// @inheritdoc IImplicitProjectRegistry function transferProject(bytes32 projectId, address newOwner) public onlyProjectOwner(projectId) { if (newOwner == address(0)) { revert IImplicitProjectRegistry.InvalidProjectOwner(); } projectOwner[projectId] = newOwner; emit IImplicitProjectRegistry.ProjectOwnerTransferred(projectId, newOwner); } function _addProjectUrlHash(bytes32 projectId, bytes32 projectUrlHash) internal { if (isProjectUrl[projectId][projectUrlHash]) { revert IImplicitProjectRegistry.ProjectUrlAlreadyExists(); } isProjectUrl[projectId][projectUrlHash] = true; projectUrlsList[projectId].push(projectUrlHash); emit IImplicitProjectRegistry.ProjectUrlAdded(projectId, projectUrlHash); } /// @notice Add a project URL hash /// @param projectId The project id /// @param projectUrlHash The project URL hash function addProjectUrlHash(bytes32 projectId, bytes32 projectUrlHash) external onlyProjectOwner(projectId) { _addProjectUrlHash(projectId, projectUrlHash); } /// @notice Add a list of project URL hashes /// @param projectId The project id /// @param projectUrlHashes The project URL hashes function addProjectUrlHashBatch( bytes32 projectId, bytes32[] memory projectUrlHashes ) external onlyProjectOwner(projectId) { for (uint256 i; i < projectUrlHashes.length; i++) { _addProjectUrlHash(projectId, projectUrlHashes[i]); } } /// @inheritdoc IImplicitProjectRegistry function addProjectUrl(bytes32 projectId, string memory projectUrl) public onlyProjectOwner(projectId) { _addProjectUrlHash(projectId, _hashUrl(projectUrl)); } /// @notice Add a list of project URLs /// @param projectId The project id /// @param projectUrls The project URLs function addProjectUrlBatch(bytes32 projectId, string[] memory projectUrls) external onlyProjectOwner(projectId) { for (uint256 i; i < projectUrls.length; i++) { _addProjectUrlHash(projectId, _hashUrl(projectUrls[i])); } } function _removeProjectUrlHash(bytes32 projectId, bytes32 projectUrlHash, uint256 urlIdx) internal { if (urlIdx >= projectUrlsList[projectId].length || projectUrlsList[projectId][urlIdx] != projectUrlHash) { revert IImplicitProjectRegistry.InvalidProjectUrlIndex(); } isProjectUrl[projectId][projectUrlHash] = false; projectUrlsList[projectId][urlIdx] = projectUrlsList[projectId][projectUrlsList[projectId].length - 1]; projectUrlsList[projectId].pop(); emit IImplicitProjectRegistry.ProjectUrlRemoved(projectId, projectUrlHash); } /// @notice Remove a project URL hash /// @param projectId The project id /// @param projectUrlHash The project URL hash /// @param urlIdx The index of the project URL hash to remove function removeProjectUrlHash( bytes32 projectId, bytes32 projectUrlHash, uint256 urlIdx ) external onlyProjectOwner(projectId) { _removeProjectUrlHash(projectId, projectUrlHash, urlIdx); } /// @notice Remove a list of project URL hashes /// @param projectId The project id /// @param projectUrlHashes The project URL hashes /// @param urlIdxs The indexes of the project URL hashes to remove /// @dev The urlIdxs must be sorted in descending order function removeProjectUrlHashBatch( bytes32 projectId, bytes32[] memory projectUrlHashes, uint256[] memory urlIdxs ) external onlyProjectOwner(projectId) { if (projectUrlHashes.length != urlIdxs.length) { revert IImplicitProjectRegistry.InvalidProjectUrlIndex(); } // Ensure the urlIdxs are sorted descending to prevent issues with reordering during removals for (uint256 i; i < urlIdxs.length - 1; i++) { if (urlIdxs[i] < urlIdxs[i + 1]) { revert IImplicitProjectRegistry.InvalidProjectUrlIndex(); } } for (uint256 i; i < projectUrlHashes.length; i++) { _removeProjectUrlHash(projectId, projectUrlHashes[i], urlIdxs[i]); } } /// @inheritdoc IImplicitProjectRegistry /// @dev This function is not optimized. Prefer to use removeProjectUrlHash. function removeProjectUrl(bytes32 projectId, string memory projectUrl) external onlyProjectOwner(projectId) { // Find the index of the project URL hash bytes32 projectUrlHash = _hashUrl(projectUrl); for (uint256 i; i < projectUrlsList[projectId].length; i++) { if (projectUrlsList[projectId][i] == projectUrlHash) { _removeProjectUrlHash(projectId, projectUrlHash, i); return; } } revert IImplicitProjectRegistry.ProjectUrlNotFound(); } /// @inheritdoc IImplicitProjectRegistry function listProjectUrls( bytes32 projectId ) external view returns (bytes32[] memory) { return projectUrlsList[projectId]; } /// @inheritdoc IImplicitProjectValidation function validateAttestation( address wallet, Attestation calldata attestation, bytes32 projectId ) external view returns (bytes32) { bytes32 hashedUrl = _hashUrl(attestation.authData.redirectUrl); if (isProjectUrl[projectId][hashedUrl]) { return attestation.generateImplicitRequestMagic(wallet); } revert IImplicitProjectValidation.InvalidRedirectUrl(); } function _hashUrl( string memory url ) internal pure returns (bytes32) { return keccak256(abi.encodePacked(url)); } }
Gas Token: