Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
- Contract name:
- Gateway
- Optimization enabled
- false
- Compiler version
- v0.6.12+commit.27d51765
- EVM Version
- istanbul
- Verified at
- 2026-01-19T21:37:49.304201Z
src/gateway/Gateway.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
import "../common/libs/ECDSALib.sol";
import "../common/libs/SafeMathLib.sol";
import "../common/lifecycle/Initializable.sol";
import "../common/signature/SignatureValidator.sol";
import "../external/ExternalAccountRegistry.sol";
import "../personal/PersonalAccountRegistry.sol";
/**
* @title Gateway
*
* @notice GSN replacement
*
* @author Stanisław Głogowski <[email protected]>
*/
contract Gateway is Initializable, SignatureValidator {
using ECDSALib for bytes32;
using SafeMathLib for uint256;
struct DelegatedBatch {
address account;
uint256 nonce;
address[] to;
bytes[] data;
}
struct DelegatedBatchWithGasPrice {
address account;
uint256 nonce;
address[] to;
bytes[] data;
uint256 gasPrice;
}
bytes32 private constant HASH_PREFIX_DELEGATED_BATCH = keccak256(
"DelegatedBatch(address account,uint256 nonce,address[] to,bytes[] data)"
);
bytes32 private constant HASH_PREFIX_DELEGATED_BATCH_WITH_GAS_PRICE = keccak256(
"DelegatedBatchWithGasPrice(address account,uint256 nonce,address[] to,bytes[] data,uint256 gasPrice)"
);
ExternalAccountRegistry public externalAccountRegistry;
PersonalAccountRegistry public personalAccountRegistry;
mapping(address => uint256) private accountNonce;
// events
/**
* @dev Emitted when the single batch is delegated
* @param sender sender address
* @param batch batch
* @param succeeded if succeeded
*/
event BatchDelegated(
address sender,
bytes batch,
bool succeeded
);
/**
* @dev Public constructor
*/
constructor() public Initializable() SignatureValidator() {}
// external functions
/**
* @notice Initializes `Gateway` contract
* @param externalAccountRegistry_ `ExternalAccountRegistry` contract address
* @param personalAccountRegistry_ `PersonalAccountRegistry` contract address
*/
function initialize(
ExternalAccountRegistry externalAccountRegistry_,
PersonalAccountRegistry personalAccountRegistry_
)
external
onlyInitializer
{
externalAccountRegistry = externalAccountRegistry_;
personalAccountRegistry = personalAccountRegistry_;
}
// public functions
/**
* @notice Sends batch
* @dev `GatewayRecipient` context api:
* `_getContextAccount` will return `msg.sender`
* `_getContextSender` will return `msg.sender`
*
* @param to array of batch recipients contracts
* @param data array of batch data
*/
function sendBatch(
address[] memory to,
bytes[] memory data
)
public
{
_sendBatch(
msg.sender,
msg.sender,
to,
data
);
}
/**
* @notice Sends batch from the account
* @dev `GatewayRecipient` context api:
* `_getContextAccount` will return `account` arg
* `_getContextSender` will return `msg.sender`
*
* @param account account address
* @param to array of batch recipients contracts
* @param data array of batch data
*/
function sendBatchFromAccount(
address account,
address[] memory to,
bytes[] memory data
)
public
{
_sendBatch(
account,
msg.sender,
to,
data
);
}
/**
* @notice Delegates batch from the account
* @dev Use `hashDelegatedBatch` to create sender message payload.
*
* `GatewayRecipient` context api:
* `_getContextAccount` will return `account` arg
* `_getContextSender` will return recovered address from `senderSignature` arg
*
* @param account account address
* @param nonce next account nonce
* @param to array of batch recipients contracts
* @param data array of batch data
* @param senderSignature sender signature
*/
function delegateBatch(
address account,
uint256 nonce,
address[] memory to,
bytes[] memory data,
bytes memory senderSignature
)
public
{
require(
nonce > accountNonce[account],
"Gateway: nonce is lower than current account nonce"
);
address sender = _hashDelegatedBatch(
account,
nonce,
to,
data
).recoverAddress(senderSignature);
accountNonce[account] = nonce;
_sendBatch(
account,
sender,
to,
data
);
}
/**
* @notice Delegates batch from the account (with gas price)
*
* @dev Use `hashDelegatedBatchWithGasPrice` to create sender message payload (tx.gasprice as gasPrice)
*
* `GatewayRecipient` context api:
* `_getContextAccount` will return `account` arg
* `_getContextSender` will return recovered address from `senderSignature` arg
*
* @param account account address
* @param nonce next account nonce
* @param to array of batch recipients contracts
* @param data array of batch data
* @param senderSignature sender signature
*/
function delegateBatchWithGasPrice(
address account,
uint256 nonce,
address[] memory to,
bytes[] memory data,
bytes memory senderSignature
)
public
{
require(
nonce > accountNonce[account],
"Gateway: nonce is lower than current account nonce"
);
address sender = _hashDelegatedBatchWithGasPrice(
account,
nonce,
to,
data,
tx.gasprice
).recoverAddress(senderSignature);
accountNonce[account] = nonce;
_sendBatch(
account,
sender,
to,
data
);
}
/**
* @notice Delegates multiple batches
* @dev It will revert when all batches fail
* @param batches array of batches
* @param revertOnFailure reverts on any error
*/
function delegateBatches(
bytes[] memory batches,
bool revertOnFailure
)
public
{
require(
batches.length > 0,
"Gateway: cannot delegate empty batches"
);
bool anySucceeded;
for (uint256 i = 0; i < batches.length; i++) {
// solhint-disable-next-line avoid-low-level-calls
(bool succeeded,) = address(this).call(batches[i]);
if (revertOnFailure) {
require(
succeeded,
"Gateway: batch reverted"
);
} else if (succeeded && !anySucceeded) {
anySucceeded = true;
}
emit BatchDelegated(
msg.sender,
batches[i],
succeeded
);
}
if (!anySucceeded) {
revert("Gateway: all batches reverted");
}
}
// public functions (views)
/**
* @notice Hashes `DelegatedBatch` message payload
* @param delegatedBatch struct
* @return hash
*/
function hashDelegatedBatch(
DelegatedBatch memory delegatedBatch
)
public
view
returns (bytes32)
{
return _hashDelegatedBatch(
delegatedBatch.account,
delegatedBatch.nonce,
delegatedBatch.to,
delegatedBatch.data
);
}
/**
* @notice Hashes `DelegatedBatchWithGasPrice` message payload
* @param delegatedBatch struct
* @return hash
*/
function hashDelegatedBatchWithGasPrice(
DelegatedBatchWithGasPrice memory delegatedBatch
)
public
view
returns (bytes32)
{
return _hashDelegatedBatchWithGasPrice(
delegatedBatch.account,
delegatedBatch.nonce,
delegatedBatch.to,
delegatedBatch.data,
delegatedBatch.gasPrice
);
}
// external functions (views)
/**
* @notice Gets next account nonce
* @param account account address
* @return next nonce
*/
function getAccountNextNonce(
address account
)
external
view
returns (uint256)
{
return accountNonce[account].add(1);
}
// private functions
function _sendBatch(
address account,
address sender,
address[] memory to,
bytes[] memory data
)
private
{
require(
account != address(0),
"Gateway: cannot send from 0x0 account"
);
require(
to.length > 0,
"Gateway: cannot send empty batch"
);
require(
data.length == to.length,
"Gateway: invalid batch"
);
if (account != sender) {
require(
personalAccountRegistry.verifyAccountOwner(account, sender) ||
externalAccountRegistry.verifyAccountOwner(account, sender),
"Gateway: sender is not the account owner"
);
}
bool succeeded;
for (uint256 i = 0; i < data.length; i++) {
require(
to[i] != address(0),
"Gateway: cannot send to 0x0"
);
// solhint-disable-next-line avoid-low-level-calls
(succeeded,) = to[i].call(abi.encodePacked(data[i], account, sender));
require(
succeeded,
"Gateway: batch transaction reverted"
);
}
}
// private functions (views)
function _hashDelegatedBatch(
address account,
uint256 nonce,
address[] memory to,
bytes[] memory data
)
private
view
returns (bytes32)
{
return _hashMessagePayload(HASH_PREFIX_DELEGATED_BATCH, abi.encodePacked(
account,
nonce,
to,
_concatBytes(data)
));
}
function _hashDelegatedBatchWithGasPrice(
address account,
uint256 nonce,
address[] memory to,
bytes[] memory data,
uint256 gasPrice
)
private
view
returns (bytes32)
{
return _hashMessagePayload(HASH_PREFIX_DELEGATED_BATCH_WITH_GAS_PRICE, abi.encodePacked(
account,
nonce,
to,
_concatBytes(data),
gasPrice
));
}
// private functions (pure)
function _concatBytes(bytes[] memory data)
private
pure
returns (bytes memory)
{
bytes memory result;
uint dataLen = data.length;
for (uint i = 0 ; i < dataLen ; i++) {
result = abi.encodePacked(result, data[i]);
}
return result;
}
}
src/common/libs/ECDSAExtendedLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "./StringsLib.sol";
/**
* @title ECDSA extended library
*/
library ECDSAExtendedLib {
using StringsLib for uint;
function toEthereumSignedMessageHash(
bytes memory message
)
internal
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n",
message.length.toString(),
abi.encodePacked(message)
));
}
}
src/common/access/Controlled.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title Controlled
*
* @dev Contract module which provides an access control mechanism.
* It ensures there is only one controlling account of the smart contract
* and grants that account exclusive access to specific functions.
*
* The controller account will be the one that deploys the contract.
*
* @author Stanisław Głogowski <[email protected]>
*/
contract Controlled {
/**
* @return controller account address
*/
address public controller;
// modifiers
/**
* @dev Throws if msg.sender is not the controller
*/
modifier onlyController() {
require(
msg.sender == controller,
"Controlled: msg.sender is not the controller"
);
_;
}
/**
* @dev Internal constructor
*/
constructor()
internal
{
controller = msg.sender;
}
}
src/common/libs/ECDSALib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title ECDSA library
*
* @dev Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/cryptography/ECDSA.sol#L26
*/
library ECDSALib {
function recoverAddress(
bytes32 messageHash,
bytes memory signature
)
internal
pure
returns (address)
{
address result = address(0);
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
if (v < 27) {
v += 27;
}
if (v == 27 || v == 28) {
result = ecrecover(messageHash, v, r, s);
}
}
return result;
}
function toEthereumSignedMessageHash(
bytes32 messageHash
)
internal
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(
"\x19Ethereum Signed Message:\n32",
messageHash
));
}
}
src/common/access/Guarded.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "../libs/ECDSALib.sol";
/**
* @title Guarded
*
* @dev Contract module which provides a guardian-type control mechanism.
* It allows key accounts to have guardians and restricts specific methods to be accessible by guardians only.
*
* Each guardian account can remove other guardians
*
* Use `_initializeGuarded` to initialize the contract
*
* @author Stanisław Głogowski <[email protected]>
*/
contract Guarded {
using ECDSALib for bytes32;
mapping(address => bool) private guardians;
// events
/**
* @dev Emitted when a new guardian is added
* @param sender sender address
* @param guardian guardian address
*/
event GuardianAdded(
address sender,
address guardian
);
/**
* @dev Emitted when the existing guardian is removed
* @param sender sender address
* @param guardian guardian address
*/
event GuardianRemoved(
address sender,
address guardian
);
// modifiers
/**
* @dev Throws if tx.origin is not a guardian account
*/
modifier onlyGuardian() {
require(
// solhint-disable-next-line avoid-tx-origin
guardians[tx.origin],
"Guarded: tx.origin is not the guardian"
);
_;
}
/**
* @dev Internal constructor
*/
constructor() internal {}
// external functions
/**
* @notice Adds a new guardian
* @param guardian guardian address
*/
function addGuardian(
address guardian
)
external
onlyGuardian
{
_addGuardian(guardian);
}
/**
* @notice Removes the existing guardian
* @param guardian guardian address
*/
function removeGuardian(
address guardian
)
external
onlyGuardian
{
require(
// solhint-disable-next-line avoid-tx-origin
tx.origin != guardian,
"Guarded: cannot remove self"
);
require(
guardians[guardian],
"Guarded: guardian doesn't exist"
);
guardians[guardian] = false;
emit GuardianRemoved(
// solhint-disable-next-line avoid-tx-origin
tx.origin,
guardian
);
}
// external functions (views)
/**
* @notice Check if guardian exists
* @param guardian guardian address
* @return true when guardian exists
*/
function isGuardian(
address guardian
)
external
view
returns (bool)
{
return guardians[guardian];
}
/**
* @notice Verifies guardian signature
* @param messageHash message hash
* @param signature signature
* @return true on correct guardian signature
*/
function verifyGuardianSignature(
bytes32 messageHash,
bytes calldata signature
)
external
view
returns (bool)
{
return _verifyGuardianSignature(
messageHash,
signature
);
}
// internal functions
/**
* @notice Initializes `Guarded` contract
* @dev If `guardians_` array is empty `tx.origin` is added as guardian account
* @param guardians_ array of guardians addresses
*/
function _initializeGuarded(
address[] memory guardians_
)
internal
{
if (guardians_.length == 0) {
// solhint-disable-next-line avoid-tx-origin
_addGuardian(tx.origin);
} else {
uint guardiansLen = guardians_.length;
for (uint i = 0; i < guardiansLen; i++) {
_addGuardian(guardians_[i]);
}
}
}
// internal functions (views)
function _verifyGuardianSignature(
bytes32 messageHash,
bytes memory signature
)
internal
view
returns (bool)
{
address guardian = messageHash.recoverAddress(signature);
return guardians[guardian];
}
// private functions
function _addGuardian(
address guardian
)
private
{
require(
guardian != address(0),
"Guarded: cannot add 0x0 guardian"
);
require(
!guardians[guardian],
"Guarded: guardian already exists"
);
guardians[guardian] = true;
emit GuardianAdded(
// solhint-disable-next-line avoid-tx-origin
tx.origin,
guardian
);
}
}
src/common/account/Account.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "../access/Controlled.sol";
import "./AccountBase.sol";
/**
* @title Account
*
* @author Stanisław Głogowski <[email protected]>
*/
contract Account is Controlled, AccountBase {
address public implementation;
/**
* @dev Public constructor
* @param registry_ account registry address
* @param implementation_ account implementation address
*/
constructor(
address registry_,
address implementation_
)
public
Controlled()
{
registry = registry_;
implementation = implementation_;
}
// external functions
/**
* @notice Payable receive
*/
receive()
external
payable
{
//
}
/**
* @notice Fallback
*/
// solhint-disable-next-line payable-fallback
fallback()
external
{
if (msg.data.length != 0) {
address implementation_ = implementation;
// solhint-disable-next-line no-inline-assembly
assembly {
let calldedatasize := calldatasize()
calldatacopy(0, 0, calldedatasize)
let result := delegatecall(gas(), implementation_, 0, calldedatasize, 0, 0)
let returneddatasize := returndatasize()
returndatacopy(0, 0, returneddatasize)
switch result
case 0 { revert(0, returneddatasize) }
default { return(0, returneddatasize) }
}
}
}
/**
* @notice Sets implementation
* @param implementation_ implementation address
*/
function setImplementation(
address implementation_
)
external
onlyController
{
implementation = implementation_;
}
/**
* @notice Executes transaction
* @param to to address
* @param value value
* @param data data
* @return transaction result
*/
function executeTransaction(
address to,
uint256 value,
bytes calldata data
)
external
onlyController
returns (bytes memory)
{
bytes memory result;
bool succeeded;
// solhint-disable-next-line avoid-call-value, avoid-low-level-calls
(succeeded, result) = payable(to).call{value: value}(data);
require(
succeeded,
"Account: transaction reverted"
);
return result;
}
}
src/common/account/AccountBase.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title Account base
*
* @author Stanisław Głogowski <[email protected]>
*/
contract AccountBase {
address public registry;
}
src/common/account/AccountController.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "./Account.sol";
/**
* @title Account controller
*
* @dev Contract module which provides Account deployment mechanism
*
* @author Stanisław Głogowski <[email protected]>
*/
contract AccountController {
address public accountRegistry;
address public accountImplementation;
// events
/**
* @dev Emitted when the account registry is updated
* @param accountRegistry account registry address
*/
event AccountRegistryUpdated(
address accountRegistry
);
/**
* @dev Emitted when the account implementation is updated
* @param accountImplementation account implementation address
*/
event AccountImplementationUpdated(
address accountImplementation
);
/**
* @dev Emitted when the account is deployed
* @param account account address
* @param accountImplementation account implementation address
*/
event AccountDeployed(
address account,
address accountImplementation
);
/**
* @dev Emitted when the account is upgraded
* @param account account address
* @param accountImplementation account implementation address
*/
event AccountUpgraded(
address account,
address accountImplementation
);
/**
* @dev Emitted when the transaction is executed
* @param account account address
* @param to to address
* @param value value
* @param data data
* @param response response
*/
event AccountTransactionExecuted(
address account,
address to,
uint256 value,
bytes data,
bytes response
);
/**
* @dev Internal constructor
*/
constructor() internal {}
// internal functions
/**
* @notice Initializes `AccountController` contract
* @param accountRegistry_ account registry address
* @param accountImplementation_ account implementation address
*/
function _initializeAccountController(
address accountRegistry_,
address accountImplementation_
)
internal
{
_setAccountRegistry(accountRegistry_, false);
_setAccountImplementation(accountImplementation_, false);
}
/**
* @notice Sets account registry
* @param accountRegistry_ account registry address
* @param emitEvent it will emit event when flag is set to true
*/
function _setAccountRegistry(
address accountRegistry_,
bool emitEvent
)
internal
{
require(
accountRegistry_ != address(0),
"AccountController: cannot set account registry to 0x0"
);
accountRegistry = accountRegistry_;
if (emitEvent) {
emit AccountRegistryUpdated(accountRegistry);
}
}
/**
* @notice Sets account implementation
* @param accountImplementation_ account implementation address
* @param emitEvent it will emit event when flag is set to true
*/
function _setAccountImplementation(
address accountImplementation_,
bool emitEvent
)
internal
{
require(
accountImplementation_ != address(0),
"AccountController: cannot set account Implementation to 0x0"
);
accountImplementation = accountImplementation_;
if (emitEvent) {
emit AccountImplementationUpdated(accountImplementation);
}
}
/**
* @notice Deploys account
* @param salt CREATE2 salt
* @param emitEvent it will emit event when flag is set to true
* @return account address
*/
function _deployAccount(
bytes32 salt,
bool emitEvent
)
internal
returns (address)
{
address account = address(new Account{salt: salt}(
accountRegistry,
accountImplementation
));
if (emitEvent) {
emit AccountDeployed(
account,
accountImplementation
);
}
return account;
}
/**
* @notice Upgrades account
* @param account account address
* @param emitEvent it will emit event when flag is set to true
*/
function _upgradeAccount(
address account,
bool emitEvent
)
internal
{
require(
Account(payable(account)).implementation() != accountImplementation,
"AccountController: account already upgraded"
);
Account(payable(account)).setImplementation(accountImplementation);
if (emitEvent) {
emit AccountUpgraded(
account,
accountImplementation
);
}
}
/**
* @notice Executes transaction from the account
* @param account account address
* @param to to address
* @param value value
* @param data data
* @param emitEvent it will emit event when flag is set to true
* @return transaction result
*/
function _executeAccountTransaction(
address account,
address to,
uint256 value,
bytes memory data,
bool emitEvent
)
internal
returns (bytes memory)
{
require(
to != address(0),
"AccountController: cannot send to 0x0"
);
require(
to != address(this),
"AccountController: cannot send to controller"
);
require(
to != account,
"AccountController: cannot send to self"
);
bytes memory response = Account(payable(account)).executeTransaction(
to,
value,
data
);
if (emitEvent) {
emit AccountTransactionExecuted(
account,
to,
value,
data,
response
);
}
return response;
}
// internal functions (views)
/**
* @notice Computes account CREATE2 address
* @param salt CREATE2 salt
* @return account address
*/
function _computeAccountAddress(
bytes32 salt
)
internal
view
returns (address)
{
bytes memory creationCode = abi.encodePacked(
type(Account).creationCode,
bytes12(0),
accountRegistry,
bytes12(0),
accountImplementation
);
bytes32 data = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(creationCode)
)
);
return address(uint160(uint256(data)));
}
}
src/common/account/AccountRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "./Account.sol";
/**
* @title Account registry
*
* @author Stanisław Głogowski <[email protected]>
*/
abstract contract AccountRegistry {
/**
* @notice Verifies account signature
* @param account account address
* @param messageHash message hash
* @param signature signature
* @return true if valid
*/
function isValidAccountSignature(
address account,
bytes32 messageHash,
bytes calldata signature
)
virtual
external
view
returns (bool);
/**
* @notice Verifies account signature
* @param account account address
* @param message message
* @param signature signature
* @return true if valid
*/
function isValidAccountSignature(
address account,
bytes calldata message,
bytes calldata signature
)
virtual
external
view
returns (bool);
}
src/common/libs/BlockLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title Block library
*
* @author Stanisław Głogowski <[email protected]>
*/
library BlockLib {
struct BlockRelated {
bool added;
uint256 removedAtBlockNumber;
}
/**
* @notice Verifies self struct at current block
* @param self self struct
* @return true on correct self struct
*/
function verifyAtCurrentBlock(
BlockRelated memory self
)
internal
view
returns (bool)
{
return verifyAtBlock(self, block.number);
}
/**
* @notice Verifies self struct at any block
* @param self self struct
* @return true on correct self struct
*/
function verifyAtAnyBlock(
BlockRelated memory self
)
internal
pure
returns (bool)
{
return verifyAtBlock(self, 0);
}
/**
* @notice Verifies self struct at specific block
* @param self self struct
* @param blockNumber block number to verify
* @return true on correct self struct
*/
function verifyAtBlock(
BlockRelated memory self,
uint256 blockNumber
)
internal
pure
returns (bool)
{
bool result = false;
if (self.added) {
if (self.removedAtBlockNumber == 0) {
result = true;
} else if (blockNumber == 0) {
result = true;
} else {
result = self.removedAtBlockNumber > blockNumber;
}
}
return result;
}
}
src/common/libs/BytesLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title Bytes library
*
* @author Stanisław Głogowski <[email protected]>
*/
library BytesLib {
/**
* @notice Converts bytes to address
* @param data data
* @return address
*/
function toAddress(
bytes memory data
)
internal
pure
returns (address)
{
address result;
require(
data.length == 20,
"BytesLib: invalid data length"
);
// solhint-disable-next-line no-inline-assembly
assembly {
result := div(mload(add(data, 0x20)), 0x1000000000000000000000000)
}
return result;
}
}
src/common/libs/SafeMathLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title Safe math library
*
* @dev Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/math/SafeMath.sol
*/
library SafeMathLib {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMathLib: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMathLib: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMathLib: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMathLib: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMathLib: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
src/common/libs/StringsLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title Strings library
*
* @dev Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/utils/Strings.sol#L12
*/
library StringsLib {
function toString(
uint256 value
)
internal
pure
returns (string memory)
{
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
uint256 index = digits - 1;
temp = value;
while (temp != 0) {
buffer[index--] = byte(uint8(48 + temp % 10));
temp /= 10;
}
return string(buffer);
}
}
src/common/lifecycle/Initializable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
/**
* @title Initializable
*
* @dev Contract module which provides access control mechanism, where
* there is the initializer account that can be granted exclusive access to
* specific functions.
*
* The initializer account will be tx.origin during contract deployment and will be removed on first use.
* Use `onlyInitializer` modifier on contract initialize process.
*
* @author Stanisław Głogowski <[email protected]>
*/
contract Initializable {
address private initializer;
// events
/**
* @dev Emitted after `onlyInitializer`
* @param initializer initializer address
*/
event Initialized(
address initializer
);
// modifiers
/**
* @dev Throws if tx.origin is not the initializer
*/
modifier onlyInitializer() {
require(
// solhint-disable-next-line avoid-tx-origin
tx.origin == initializer,
"Initializable: tx.origin is not the initializer"
);
/// @dev removes initializer
initializer = address(0);
_;
emit Initialized(
// solhint-disable-next-line avoid-tx-origin
tx.origin
);
}
/**
* @dev Internal constructor
*/
constructor()
internal
{
// solhint-disable-next-line avoid-tx-origin
initializer = tx.origin;
}
// external functions (views)
/**
* @notice Check if contract is initialized
* @return true when contract is initialized
*/
function isInitialized()
external
view
returns (bool)
{
return initializer == address(0);
}
}
src/common/signature/SignatureValidator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "../libs/ECDSALib.sol";
/**
* @title Signature validator
*
* @author Stanisław Głogowski <[email protected]>
*/
contract SignatureValidator {
using ECDSALib for bytes32;
uint256 public chainId;
/**
* @dev internal constructor
*/
constructor() internal {
uint256 chainId_;
// solhint-disable-next-line no-inline-assembly
assembly {
chainId_ := chainid()
}
chainId = chainId_;
}
// internal functions
function _hashMessagePayload(
bytes32 messagePrefix,
bytes memory messagePayload
)
internal
view
returns (bytes32)
{
return keccak256(abi.encodePacked(
chainId,
address(this),
messagePrefix,
messagePayload
)).toEthereumSignedMessageHash();
}
}
src/common/token/ERC20Token.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "../libs/SafeMathLib.sol";
/**
* @title ERC20 token
*
* @dev Based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.3.0/contracts/token/ERC20/ERC20.sol
*/
contract ERC20Token {
using SafeMathLib for uint256;
string public name;
string public symbol;
uint8 public decimals;
uint256 public totalSupply;
mapping(address => uint256) internal balances;
mapping(address => mapping(address => uint256)) internal allowances;
// events
event Transfer(
address indexed from,
address indexed to,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
/**
* @dev internal constructor
*/
constructor() internal {}
// external functions
function transfer(
address to,
uint256 value
)
external
returns (bool)
{
_transfer(_getSender(), to, value);
return true;
}
function transferFrom(
address from,
address to,
uint256 value
)
virtual
external
returns (bool)
{
address sender = _getSender();
_transfer(from, to, value);
_approve(from, sender, allowances[from][sender].sub(value));
return true;
}
function approve(
address spender,
uint256 value
)
virtual
external
returns (bool)
{
_approve(_getSender(), spender, value);
return true;
}
// external functions (views)
function balanceOf(
address owner
)
virtual
external
view
returns (uint256)
{
return balances[owner];
}
function allowance(
address owner,
address spender
)
virtual
external
view
returns (uint256)
{
return allowances[owner][spender];
}
// internal functions
function _transfer(
address from,
address to,
uint256 value
)
virtual
internal
{
require(
from != address(0),
"ERC20Token: cannot transfer from 0x0 address"
);
require(
to != address(0),
"ERC20Token: cannot transfer to 0x0 address"
);
balances[from] = balances[from].sub(value);
balances[to] = balances[to].add(value);
emit Transfer(from, to, value);
}
function _approve(
address owner,
address spender,
uint256 value
)
virtual
internal
{
require(
owner != address(0),
"ERC20Token: cannot approve from 0x0 address"
);
require(
spender != address(0),
"ERC20Token: cannot approve to 0x0 address"
);
allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _mint(
address owner,
uint256 value
)
virtual
internal
{
require(
owner != address(0),
"ERC20Token: cannot mint to 0x0 address"
);
require(
value > 0,
"ERC20Token: cannot mint 0 value"
);
balances[owner] = balances[owner].add(value);
totalSupply = totalSupply.add(value);
emit Transfer(address(0), owner, value);
}
function _burn(
address owner,
uint256 value
)
virtual
internal
{
require(
owner != address(0),
"ERC20Token: cannot burn from 0x0 address"
);
balances[owner] = balances[owner].sub(
value,
"ERC20Token: burn value exceeds balance"
);
totalSupply = totalSupply.sub(value);
emit Transfer(owner, address(0), value);
}
// internal functions (views)
function _getSender()
virtual
internal
view
returns (address)
{
return msg.sender;
}
}
src/external/ExternalAccountRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "../common/libs/BlockLib.sol";
/**
* @title External account registry
*
* @notice Global registry for keys and external (outside of the platform) contract based wallets
*
* @dev An account can call the registry to add (`addAccountOwner`) or remove (`removeAccountOwner`) its own owners.
* When the owner has been added, information about that fact will live in the registry forever.
* Removing an owner only affects the future blocks (until the owner is re-added).
*
* Given the fact, there is no way to sign the data using a contract based wallet,
* we created a registry to store signed by the key wallet proofs.
* ERC-1271 allows removing a signer after the signature was created. Thus store the signature for the later use
* doesn't guarantee the signer is still has access to that smart account.
* Because of that, the ERC1271's `isValidSignature()` cannot be used in e.g. `PaymentRegistry`.*
*
* An account can call the registry to add (`addAccountProof`) or remove (`removeAccountProof`) proof hash.
* When the proof has been added, information about that fact will live in the registry forever.
* Removing a proof only affects the future blocks (until the proof is re-added).
*
* @author Stanisław Głogowski <[email protected]>
*/
contract ExternalAccountRegistry {
using BlockLib for BlockLib.BlockRelated;
struct Account {
mapping(address => BlockLib.BlockRelated) owners;
mapping(bytes32 => BlockLib.BlockRelated) proofs;
}
mapping(address => Account) private accounts;
// events
/**
* @dev Emitted when the new owner is added
* @param account account address
* @param owner owner address
*/
event AccountOwnerAdded(
address account,
address owner
);
/**
* @dev Emitted when the existing owner is removed
* @param account account address
* @param owner owner address
*/
event AccountOwnerRemoved(
address account,
address owner
);
/**
* @dev Emitted when the new proof is added
* @param account account address
* @param hash proof hash
*/
event AccountProofAdded(
address account,
bytes32 hash
);
/**
* @dev Emitted when the existing proof is removed
* @param account account address
* @param hash proof hash
*/
event AccountProofRemoved(
address account,
bytes32 hash
);
// external functions
/**
* @notice Adds a new account owner
* @param owner owner address
*/
function addAccountOwner(
address owner
)
external
{
require(
owner != address(0),
"ExternalAccountRegistry: cannot add 0x0 owner"
);
require(
!accounts[msg.sender].owners[owner].verifyAtCurrentBlock(),
"ExternalAccountRegistry: owner already exists"
);
accounts[msg.sender].owners[owner].added = true;
accounts[msg.sender].owners[owner].removedAtBlockNumber = 0;
emit AccountOwnerAdded(
msg.sender,
owner
);
}
/**
* @notice Removes existing account owner
* @param owner owner address
*/
function removeAccountOwner(
address owner
)
external
{
require(
accounts[msg.sender].owners[owner].verifyAtCurrentBlock(),
"ExternalAccountRegistry: owner doesn't exist"
);
accounts[msg.sender].owners[owner].removedAtBlockNumber = block.number;
emit AccountOwnerRemoved(
msg.sender,
owner
);
}
/**
* @notice Adds a new account proof
* @param hash proof hash
*/
function addAccountProof(
bytes32 hash
)
external
{
require(
!accounts[msg.sender].proofs[hash].verifyAtCurrentBlock(),
"ExternalAccountRegistry: proof already exists"
);
accounts[msg.sender].proofs[hash].added = true;
accounts[msg.sender].proofs[hash].removedAtBlockNumber = 0;
emit AccountProofAdded(
msg.sender,
hash
);
}
/**
* @notice Removes existing account proof
* @param hash proof hash
*/
function removeAccountProof(
bytes32 hash
)
external
{
require(
accounts[msg.sender].proofs[hash].verifyAtCurrentBlock(),
"ExternalAccountRegistry: proof doesn't exist"
);
accounts[msg.sender].proofs[hash].removedAtBlockNumber = block.number;
emit AccountProofRemoved(
msg.sender,
hash
);
}
// external functions (views)
/**
* @notice Verifies the owner of the account at current block
* @param account account address
* @param owner owner address
* @return true on correct account owner
*/
function verifyAccountOwner(
address account,
address owner
)
external
view
returns (bool)
{
return accounts[account].owners[owner].verifyAtCurrentBlock();
}
/**
* @notice Verifies the owner of the account at specific block
* @param account account address
* @param owner owner address
* @param blockNumber block number to verify
* @return true on correct account owner
*/
function verifyAccountOwnerAtBlock(
address account,
address owner,
uint256 blockNumber
)
external
view
returns (bool)
{
return accounts[account].owners[owner].verifyAtBlock(blockNumber);
}
/**
* @notice Verifies the proof of the account at current block
* @param account account address
* @param hash proof hash
* @return true on correct account proof
*/
function verifyAccountProof(
address account,
bytes32 hash
)
external
view
returns (bool)
{
return accounts[account].proofs[hash].verifyAtCurrentBlock();
}
/**
* @notice Verifies the proof of the account at specific block
* @param account account address
* @param hash proof hash
* @param blockNumber block number to verify
* @return true on correct account proof
*/
function verifyAccountProofAtBlock(
address account,
bytes32 hash,
uint256 blockNumber
)
external
view
returns (bool)
{
return accounts[account].proofs[hash].verifyAtBlock(blockNumber);
}
}
src/gateway/GatewayRecipient.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "../common/libs/BytesLib.sol";
/**
* @title Gateway recipient
*
* @notice Gateway target contract
*
* @author Stanisław Głogowski <[email protected]>
*/
contract GatewayRecipient {
using BytesLib for bytes;
address public gateway;
/**
* @dev internal constructor
*/
constructor() internal {}
// internal functions
/**
* @notice Initializes `GatewayRecipient` contract
* @param gateway_ `Gateway` contract address
*/
function _initializeGatewayRecipient(
address gateway_
)
internal
{
gateway = gateway_;
}
// internal functions (views)
/**
* @notice Gets gateway context account
* @return context account address
*/
function _getContextAccount()
internal
view
returns (address)
{
return _getContextAddress(40);
}
/**
* @notice Gets gateway context sender
* @return context sender address
*/
function _getContextSender()
internal
view
returns (address)
{
return _getContextAddress(20);
}
/**
* @notice Gets gateway context data
* @return context data
*/
function _getContextData()
internal
view
returns (bytes calldata)
{
bytes calldata result;
if (_isGatewaySender()) {
result = msg.data[:msg.data.length - 40];
} else {
result = msg.data;
}
return result;
}
// private functions (views)
function _getContextAddress(
uint256 offset
)
private
view
returns (address)
{
address result = address(0);
if (_isGatewaySender()) {
uint from = msg.data.length - offset;
result = bytes(msg.data[from:from + 20]).toAddress();
} else {
result = msg.sender;
}
return result;
}
function _isGatewaySender()
private
view
returns (bool)
{
bool result;
if (msg.sender == gateway) {
require(
msg.data.length >= 44,
"GatewayRecipient: invalid msg.data"
);
result = true;
}
return result;
}
}
src/personal/PersonalAccountRegistry.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
import "../common/access/Guarded.sol";
import "../common/account/AccountController.sol";
import "../common/account/AccountRegistry.sol";
import "../common/libs/BlockLib.sol";
import "../common/libs/ECDSALib.sol";
import "../common/libs/ECDSAExtendedLib.sol";
import "../common/libs/SafeMathLib.sol";
import "../common/lifecycle/Initializable.sol";
import "../common/token/ERC20Token.sol";
import "../gateway/GatewayRecipient.sol";
/**
* @title Personal account registry
*
* @notice A registry for personal (controlled by owners) accounts
*
* @author Stanisław Głogowski <[email protected]>
*/
contract PersonalAccountRegistry is Guarded, AccountController, AccountRegistry, Initializable, GatewayRecipient {
using BlockLib for BlockLib.BlockRelated;
using SafeMathLib for uint256;
using ECDSALib for bytes32;
using ECDSAExtendedLib for bytes;
struct Account {
bool deployed;
bytes32 salt;
mapping(address => BlockLib.BlockRelated) owners;
}
mapping(address => Account) private accounts;
// events
/**
* @dev Emitted when the new owner is added
* @param account account address
* @param owner owner address
*/
event AccountOwnerAdded(
address account,
address owner
);
/**
* @dev Emitted when the existing owner is removed
* @param account account address
* @param owner owner address
*/
event AccountOwnerRemoved(
address account,
address owner
);
/**
* @dev Emitted when the call is refunded
* @param account account address
* @param beneficiary beneficiary address
* @param token token address
* @param value value
*/
event AccountCallRefunded(
address account,
address beneficiary,
address token,
uint256 value
);
/**
* @dev Public constructor
*/
constructor() public Initializable() {}
// external functions
/**
* @notice Initializes `PersonalAccountRegistry` contract
* @param guardians_ array of guardians addresses
* @param accountImplementation_ account implementation address
* @param gateway_ `Gateway` contract address
*/
function initialize(
address[] calldata guardians_,
address accountImplementation_,
address gateway_
)
external
onlyInitializer
{
// Guarded
_initializeGuarded(guardians_);
// AccountController
_initializeAccountController(address(this), accountImplementation_);
// GatewayRecipient
_initializeGatewayRecipient(gateway_);
}
/**
* @notice Upgrades `PersonalAccountRegistry` contract
* @param accountImplementation_ account implementation address
*/
function upgrade(
address accountImplementation_
)
external
onlyGuardian
{
_setAccountImplementation(accountImplementation_, true);
}
/**
* @notice Deploys account
* @param account account address
*/
function deployAccount(
address account
)
external
{
_verifySender(account);
_deployAccount(account);
}
/**
* @notice Upgrades account
* @param account account address
*/
function upgradeAccount(
address account
)
external
{
_verifySender(account);
_upgradeAccount(account, true);
}
/**
* @notice Adds a new account owner
* @param account account address
* @param owner owner address
*/
function addAccountOwner(
address account,
address owner
)
external
{
_verifySender(account);
require(
owner != address(0),
"PersonalAccountRegistry: cannot add 0x0 owner"
);
require(
!accounts[account].owners[owner].verifyAtCurrentBlock(),
"PersonalAccountRegistry: owner already exists"
);
accounts[account].owners[owner].added = true;
accounts[account].owners[owner].removedAtBlockNumber = 0;
emit AccountOwnerAdded(
account,
owner
);
}
/**
* @notice Removes the existing account owner
* @param account account address
* @param owner owner address
*/
function removeAccountOwner(
address account,
address owner
)
external
{
address sender = _verifySender(account);
require(
owner != sender,
"PersonalAccountRegistry: cannot remove self"
);
require(
accounts[account].owners[owner].verifyAtCurrentBlock(),
"PersonalAccountRegistry: owner doesn't exist"
);
accounts[account].owners[owner].removedAtBlockNumber = block.number;
emit AccountOwnerRemoved(
account,
owner
);
}
/**
* @notice Executes account transaction
* @dev Deploys an account if not deployed yet
* @param account account address
* @param to to address
* @param value value
* @param data data
*/
function executeAccountTransaction(
address account,
address to,
uint256 value,
bytes calldata data
)
external
{
_verifySender(account);
_deployAccount(account);
_executeAccountTransaction(
account,
to,
value,
data,
true
);
}
/**
* @notice Refunds account call
* @dev Deploys an account if not deployed yet
* @param account account address
* @param token token address
* @param value value
*/
function refundAccountCall(
address account,
address token,
uint256 value
)
external
{
_verifySender(account);
_deployAccount(account);
/* solhint-disable avoid-tx-origin */
if (token == address(0)) {
_executeAccountTransaction(
account,
tx.origin,
value,
new bytes(0),
false
);
} else {
bytes memory response = _executeAccountTransaction(
account,
token,
0,
abi.encodeWithSelector(
ERC20Token(token).transfer.selector,
tx.origin,
value
),
false
);
if (response.length > 0) {
require(
abi.decode(response, (bool)),
"PersonalAccountRegistry: ERC20Token transfer reverted"
);
}
}
emit AccountCallRefunded(
account,
tx.origin,
token,
value
);
/* solhint-enable avoid-tx-origin */
}
// external functions (views)
/**
* @notice Computes account address
* @param saltOwner salt owner address
* @return account address
*/
function computeAccountAddress(
address saltOwner
)
external
view
returns (address)
{
return _computeAccountAddress(saltOwner);
}
/**
* @notice Checks if account is deployed
* @param account account address
* @return true when account is deployed
*/
function isAccountDeployed(
address account
)
external
view
returns (bool)
{
return accounts[account].deployed;
}
/**
* @notice Verifies the owner of the account at the current block
* @param account account address
* @param owner owner address
* @return true on correct account owner
*/
function verifyAccountOwner(
address account,
address owner
)
external
view
returns (bool)
{
return _verifyAccountOwner(account, owner);
}
/**
* @notice Verifies the owner of the account at a specific block
* @param account account address
* @param owner owner address
* @param blockNumber block number to verify
* @return true on correct account owner
*/
function verifyAccountOwnerAtBlock(
address account,
address owner,
uint256 blockNumber
)
external
view
returns (bool)
{
bool result = false;
if (_verifyAccountOwner(account, owner)) {
result = true;
} else {
result = accounts[account].owners[owner].verifyAtBlock(blockNumber);
}
return result;
}
/**
* @notice Verifies account signature
* @param account account address
* @param messageHash message hash
* @param signature signature
* @return magic hash if valid
*/
function isValidAccountSignature(
address account,
bytes32 messageHash,
bytes calldata signature
)
override
external
view
returns (bool)
{
return _verifyAccountOwner(
account,
messageHash.recoverAddress(signature)
);
}
/**
* @notice Verifies account signature
* @param account account address
* @param message message
* @param signature signature
* @return magic hash if valid
*/
function isValidAccountSignature(
address account,
bytes calldata message,
bytes calldata signature
)
override
external
view
returns (bool)
{
return _verifyAccountOwner(
account,
message.toEthereumSignedMessageHash().recoverAddress(signature)
);
}
// private functions
function _verifySender(
address account
)
private
returns (address)
{
address sender = _getContextSender();
if (accounts[account].owners[sender].added) {
require(
accounts[account].owners[sender].removedAtBlockNumber == 0,
"PersonalAccountRegistry: sender is not the account owner"
);
} else {
require(
accounts[account].salt == 0,
"PersonalAccountRegistry: sender is not the account owner"
);
bytes32 salt = keccak256(
abi.encodePacked(sender)
);
require(
account == _computeAccountAddress(salt),
"PersonalAccountRegistry: sender is not the account owner"
);
accounts[account].salt = salt;
accounts[account].owners[sender].added = true;
emit AccountOwnerAdded(
account,
sender
);
}
return sender;
}
function _deployAccount(
address account
)
internal
{
if (!accounts[account].deployed) {
_deployAccount(
accounts[account].salt,
true
);
accounts[account].deployed = true;
}
}
// private functions (views)
function _computeAccountAddress(
address saltOwner
)
private
view
returns (address)
{
bytes32 salt = keccak256(
abi.encodePacked(saltOwner)
);
return _computeAccountAddress(salt);
}
function _verifyAccountOwner(
address account,
address owner
)
private
view
returns (bool)
{
bool result;
if (accounts[account].owners[owner].added) {
result = accounts[account].owners[owner].removedAtBlockNumber == 0;
} else if (accounts[account].salt == 0) {
result = account == _computeAccountAddress(owner);
}
return result;
}
}
Compiler Settings
{"outputSelection":{"*":{"*":["*"],"":["*"]}},"optimizer":{"runs":200,"enabled":false},"metadata":{"useLiteralContent":true,"bytecodeHash":"none"},"libraries":{},"evmVersion":"istanbul"}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"event","name":"BatchDelegated","inputs":[{"type":"address","name":"sender","internalType":"address","indexed":false},{"type":"bytes","name":"batch","internalType":"bytes","indexed":false},{"type":"bool","name":"succeeded","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"type":"address","name":"initializer","internalType":"address","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"chainId","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"delegateBatch","inputs":[{"type":"address","name":"account","internalType":"address"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"address[]","name":"to","internalType":"address[]"},{"type":"bytes[]","name":"data","internalType":"bytes[]"},{"type":"bytes","name":"senderSignature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"delegateBatchWithGasPrice","inputs":[{"type":"address","name":"account","internalType":"address"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"address[]","name":"to","internalType":"address[]"},{"type":"bytes[]","name":"data","internalType":"bytes[]"},{"type":"bytes","name":"senderSignature","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"delegateBatches","inputs":[{"type":"bytes[]","name":"batches","internalType":"bytes[]"},{"type":"bool","name":"revertOnFailure","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ExternalAccountRegistry"}],"name":"externalAccountRegistry","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAccountNextNonce","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"hashDelegatedBatch","inputs":[{"type":"tuple","name":"delegatedBatch","internalType":"struct Gateway.DelegatedBatch","components":[{"type":"address","name":"account","internalType":"address"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"address[]","name":"to","internalType":"address[]"},{"type":"bytes[]","name":"data","internalType":"bytes[]"}]}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bytes32","name":"","internalType":"bytes32"}],"name":"hashDelegatedBatchWithGasPrice","inputs":[{"type":"tuple","name":"delegatedBatch","internalType":"struct Gateway.DelegatedBatchWithGasPrice","components":[{"type":"address","name":"account","internalType":"address"},{"type":"uint256","name":"nonce","internalType":"uint256"},{"type":"address[]","name":"to","internalType":"address[]"},{"type":"bytes[]","name":"data","internalType":"bytes[]"},{"type":"uint256","name":"gasPrice","internalType":"uint256"}]}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"externalAccountRegistry_","internalType":"contract ExternalAccountRegistry"},{"type":"address","name":"personalAccountRegistry_","internalType":"contract PersonalAccountRegistry"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isInitialized","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract PersonalAccountRegistry"}],"name":"personalAccountRegistry","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"sendBatch","inputs":[{"type":"address[]","name":"to","internalType":"address[]"},{"type":"bytes[]","name":"data","internalType":"bytes[]"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"sendBatchFromAccount","inputs":[{"type":"address","name":"account","internalType":"address"},{"type":"address[]","name":"to","internalType":"address[]"},{"type":"bytes[]","name":"data","internalType":"bytes[]"}]}]
Contract Creation Code
0x608060405234801561001057600080fd5b50326000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600046905080600181905550506123f58061006d6000396000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063867519c61161008c5780639f255626116100665780639f255626146101fe578063b5021b161461021a578063d2c83b9a14610236578063f92c5f7c14610254576100cf565b8063867519c6146101a657806387d31313146101c25780639a8a0592146101e0576100cf565b8063231badaf146100d4578063392e53cd146100f0578063485cc9551461010e5780635afaa7bb1461012a57806373e5a13f1461014657806376db2b4c14610176575b600080fd5b6100ee60048036038101906100e99190611438565b610284565b005b6100f861037f565b6040516101059190611e71565b60405180910390f35b610128600480360381019061012391906115e0565b6103d5565b005b610144600480360381019061013f9190611563565b610561565b005b610160600480360381019061015b919061165d565b610739565b60405161016d9190611e8c565b60405180910390f35b610190600480360381019061018b919061161c565b61075e565b60405161019d9190611e8c565b60405180910390f35b6101c060048036038101906101bb91906113b9565b610788565b005b6101ca610799565b6040516101d79190611f07565b60405180910390f35b6101e86107bf565b6040516101f591906120a2565b60405180910390f35b610218600480360381019061021391906114f7565b6107c5565b005b610234600480360381019061022f9190611438565b6107d5565b005b61023e6108d1565b60405161024b9190611eec565b60405180910390f35b61026e60048036038101906102699190611390565b6108f7565b60405161027b91906120a2565b60405180910390f35b600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548411610305576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102fc90611f82565b60405180910390fd5b60006103258261031788888888610953565b6109b690919063ffffffff16565b905084600460008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061037786828686610a71565b505050505050565b60008073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163273ffffffffffffffffffffffffffffffffffffffff1614610463576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045a90611f42565b60405180910390fd5b60008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e6326040516105559190611def565b60405180910390a15050565b60008251116105a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161059c90611fc2565b60405180910390fd5b600080600090505b83518110156106f35760003073ffffffffffffffffffffffffffffffffffffffff168583815181106105db57fe5b60200260200101516040516105f09190611d0b565b6000604051808303816000865af19150503d806000811461062d576040519150601f19603f3d011682016040523d82523d6000602084013e610632565b606091505b505090508315610681578061067c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067390611f62565b60405180910390fd5b610697565b80801561068c575082155b1561069657600192505b5b7f361c14722cc344132c73396113f7164232448b09c544a149f09048648b43d872338684815181106106c557fe5b6020026020010151836040516106dd93929190611e0a565b60405180910390a15080806001019150506105ad565b5080610734576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161072b90612082565b60405180910390fd5b505050565b60006107578260000151836020015184604001518560600151610953565b9050919050565b600061078182600001518360200151846040015185606001518660800151610edc565b9050919050565b61079483338484610a71565b505050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b6107d133338484610a71565b5050565b600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548411610856576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161084d90611f82565b60405180910390fd5b600061087782610869888888883a610edc565b6109b690919063ffffffff16565b905084600460008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506108c986828686610a71565b505050505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061094c6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610f4290919063ffffffff16565b9050919050565b60006109ac7f6848d0622081db2451400280dead7a739a080cb93852607c381af11e289769b286868661098587610f97565b6040516020016109989493929190611c6e565b604051602081830303815290604052610ffa565b9050949350505050565b60008060009050604183511415610a675760008060006020860151925060408601519150606086015160001a9050601b8160ff1610156109f757601b810190505b601b8160ff161480610a0c5750601c8160ff16145b15610a635760018782858560405160008152602001604052604051610a349493929190611ea7565b6020604051602081039080840390855afa158015610a56573d6000803e3d6000fd5b5050506020604051035193505b5050505b8091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415610ae1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ad890611fa2565b60405180910390fd5b6000825111610b25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1c90612062565b60405180910390fd5b8151815114610b69576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b6090612002565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614610d3d57600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb890d3f85856040518363ffffffff1660e01b8152600401610bf9929190611e48565b60206040518083038186803b158015610c1157600080fd5b505afa158015610c25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4991906115b7565b80610cfd5750600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb890d3f85856040518363ffffffff1660e01b8152600401610cac929190611e48565b60206040518083038186803b158015610cc457600080fd5b505afa158015610cd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cfc91906115b7565b5b610d3c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d3390611fe2565b60405180910390fd5b5b600080600090505b8251811015610ed457600073ffffffffffffffffffffffffffffffffffffffff16848281518110610d7257fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415610dd1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dc890611f22565b60405180910390fd5b838181518110610ddd57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16838281518110610e0757fe5b60200260200101518787604051602001610e2393929190611d22565b604051602081830303815290604052604051610e3f9190611d0b565b6000604051808303816000865af19150503d8060008114610e7c576040519150601f19603f3d011682016040523d82523d6000602084013e610e81565b606091505b50508092505081610ec7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ebe90612022565b60405180910390fd5b8080600101915050610d45565b505050505050565b6000610f377f6f4e1b2b1e5e49f4269e19e16e67a00cb0a796d96d30be3e4b540d3732e8bcad878787610f0e88610f97565b87604051602001610f23959493929190611cb4565b604051602081830303815290604052610ffa565b905095945050505050565b600080828401905083811015610f8d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f8490612042565b60405180910390fd5b8091505092915050565b60608060008351905060005b81811015610fef5782858281518110610fb857fe5b6020026020010151604051602001610fd1929190611d5b565b60405160208183030381529060405292508080600101915050610fa3565b508192505050919050565b60006110336001543085856040516020016110189493929190611da5565b6040516020818303038152906040528051906020012061103b565b905092915050565b60008160405160200161104e9190611d7f565b604051602081830303815290604052805190602001209050919050565b60008135905061107a81612375565b92915050565b600082601f83011261109157600080fd5b81356110a461109f826120ea565b6120bd565b915081818352602084019350602081019050838560208402820111156110c957600080fd5b60005b838110156110f957816110df888261106b565b8452602084019350602083019250506001810190506110cc565b5050505092915050565b600082601f83011261111457600080fd5b813561112761112282612112565b6120bd565b9150818183526020840193506020810190508360005b8381101561116d578135860161115388826111a1565b84526020840193506020830192505060018101905061113d565b5050505092915050565b6000813590506111868161238c565b92915050565b60008151905061119b8161238c565b92915050565b600082601f8301126111b257600080fd5b81356111c56111c08261213a565b6120bd565b915080825260208301602083018583830111156111e157600080fd5b6111ec8382846122dd565b50505092915050565b600081359050611204816123a3565b92915050565b600081359050611219816123ba565b92915050565b600060a0828403121561123157600080fd5b61123b60a06120bd565b9050600061124b8482850161106b565b600083015250602061125f8482850161137b565b602083015250604082013567ffffffffffffffff81111561127f57600080fd5b61128b84828501611080565b604083015250606082013567ffffffffffffffff8111156112ab57600080fd5b6112b784828501611103565b60608301525060806112cb8482850161137b565b60808301525092915050565b6000608082840312156112e957600080fd5b6112f360806120bd565b905060006113038482850161106b565b60008301525060206113178482850161137b565b602083015250604082013567ffffffffffffffff81111561133757600080fd5b61134384828501611080565b604083015250606082013567ffffffffffffffff81111561136357600080fd5b61136f84828501611103565b60608301525092915050565b60008135905061138a816123d1565b92915050565b6000602082840312156113a257600080fd5b60006113b08482850161106b565b91505092915050565b6000806000606084860312156113ce57600080fd5b60006113dc8682870161106b565b935050602084013567ffffffffffffffff8111156113f957600080fd5b61140586828701611080565b925050604084013567ffffffffffffffff81111561142257600080fd5b61142e86828701611103565b9150509250925092565b600080600080600060a0868803121561145057600080fd5b600061145e8882890161106b565b955050602061146f8882890161137b565b945050604086013567ffffffffffffffff81111561148c57600080fd5b61149888828901611080565b935050606086013567ffffffffffffffff8111156114b557600080fd5b6114c188828901611103565b925050608086013567ffffffffffffffff8111156114de57600080fd5b6114ea888289016111a1565b9150509295509295909350565b6000806040838503121561150a57600080fd5b600083013567ffffffffffffffff81111561152457600080fd5b61153085828601611080565b925050602083013567ffffffffffffffff81111561154d57600080fd5b61155985828601611103565b9150509250929050565b6000806040838503121561157657600080fd5b600083013567ffffffffffffffff81111561159057600080fd5b61159c85828601611103565b92505060206115ad85828601611177565b9150509250929050565b6000602082840312156115c957600080fd5b60006115d78482850161118c565b91505092915050565b600080604083850312156115f357600080fd5b6000611601858286016111f5565b92505060206116128582860161120a565b9150509250929050565b60006020828403121561162e57600080fd5b600082013567ffffffffffffffff81111561164857600080fd5b6116548482850161121f565b91505092915050565b60006020828403121561166f57600080fd5b600082013567ffffffffffffffff81111561168957600080fd5b611695848285016112d7565b91505092915050565b60006116aa83836116d4565b60208301905092915050565b6116bf8161225f565b82525050565b6116ce816121dc565b82525050565b6116dd816121dc565b82525050565b6116f46116ef826121dc565b61231f565b82525050565b600061170582612176565b61170f8185612199565b935061171a83612166565b8060005b8381101561174b578151611732888261169e565b975061173d8361218c565b92505060018101905061171e565b5085935050505092915050565b611761816121ee565b82525050565b611770816121fa565b82525050565b611787611782826121fa565b612331565b82525050565b600061179882612181565b6117a281856121a4565b93506117b28185602086016122ec565b6117bb81612357565b840191505092915050565b60006117d182612181565b6117db81856121b5565b93506117eb8185602086016122ec565b80840191505092915050565b61180081612271565b82525050565b61180f81612295565b82525050565b6000611822601b836121c0565b91507f476174657761793a2063616e6e6f742073656e6420746f2030783000000000006000830152602082019050919050565b6000611862601c836121d1565b91507f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000830152601c82019050919050565b60006118a2602f836121c0565b91507f496e697469616c697a61626c653a2074782e6f726967696e206973206e6f742060008301527f74686520696e697469616c697a657200000000000000000000000000000000006020830152604082019050919050565b60006119086017836121c0565b91507f476174657761793a2062617463682072657665727465640000000000000000006000830152602082019050919050565b60006119486032836121c0565b91507f476174657761793a206e6f6e6365206973206c6f776572207468616e2063757260008301527f72656e74206163636f756e74206e6f6e636500000000000000000000000000006020830152604082019050919050565b60006119ae6025836121c0565b91507f476174657761793a2063616e6e6f742073656e642066726f6d2030783020616360008301527f636f756e740000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611a146026836121c0565b91507f476174657761793a2063616e6e6f742064656c656761746520656d707479206260008301527f61746368657300000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611a7a6028836121c0565b91507f476174657761793a2073656e646572206973206e6f7420746865206163636f7560008301527f6e74206f776e65720000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611ae06016836121c0565b91507f476174657761793a20696e76616c6964206261746368000000000000000000006000830152602082019050919050565b6000611b206023836121c0565b91507f476174657761793a206261746368207472616e73616374696f6e20726576657260008301527f74656400000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611b86601e836121c0565b91507f536166654d6174684c69623a206164646974696f6e206f766572666c6f7700006000830152602082019050919050565b6000611bc66020836121c0565b91507f476174657761793a2063616e6e6f742073656e6420656d7074792062617463686000830152602082019050919050565b6000611c06601d836121c0565b91507f476174657761793a20616c6c20626174636865732072657665727465640000006000830152602082019050919050565b611c4281612248565b82525050565b611c59611c5482612248565b61234d565b82525050565b611c6881612252565b82525050565b6000611c7a82876116e3565b601482019150611c8a8286611c48565b602082019150611c9a82856116fa565b9150611ca682846117c6565b915081905095945050505050565b6000611cc082886116e3565b601482019150611cd08287611c48565b602082019150611ce082866116fa565b9150611cec82856117c6565b9150611cf88284611c48565b6020820191508190509695505050505050565b6000611d1782846117c6565b915081905092915050565b6000611d2e82866117c6565b9150611d3a82856116e3565b601482019150611d4a82846116e3565b601482019150819050949350505050565b6000611d6782856117c6565b9150611d7382846117c6565b91508190509392505050565b6000611d8a82611855565b9150611d968284611776565b60208201915081905092915050565b6000611db18287611c48565b602082019150611dc182866116e3565b601482019150611dd18285611776565b602082019150611de182846117c6565b915081905095945050505050565b6000602082019050611e0460008301846116b6565b92915050565b6000606082019050611e1f60008301866116b6565b8181036020830152611e31818561178d565b9050611e406040830184611758565b949350505050565b6000604082019050611e5d60008301856116c5565b611e6a60208301846116c5565b9392505050565b6000602082019050611e866000830184611758565b92915050565b6000602082019050611ea16000830184611767565b92915050565b6000608082019050611ebc6000830187611767565b611ec96020830186611c5f565b611ed66040830185611767565b611ee36060830184611767565b95945050505050565b6000602082019050611f0160008301846117f7565b92915050565b6000602082019050611f1c6000830184611806565b92915050565b60006020820190508181036000830152611f3b81611815565b9050919050565b60006020820190508181036000830152611f5b81611895565b9050919050565b60006020820190508181036000830152611f7b816118fb565b9050919050565b60006020820190508181036000830152611f9b8161193b565b9050919050565b60006020820190508181036000830152611fbb816119a1565b9050919050565b60006020820190508181036000830152611fdb81611a07565b9050919050565b60006020820190508181036000830152611ffb81611a6d565b9050919050565b6000602082019050818103600083015261201b81611ad3565b9050919050565b6000602082019050818103600083015261203b81611b13565b9050919050565b6000602082019050818103600083015261205b81611b79565b9050919050565b6000602082019050818103600083015261207b81611bb9565b9050919050565b6000602082019050818103600083015261209b81611bf9565b9050919050565b60006020820190506120b76000830184611c39565b92915050565b6000604051905081810181811067ffffffffffffffff821117156120e057600080fd5b8060405250919050565b600067ffffffffffffffff82111561210157600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561212957600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561215157600080fd5b601f19601f8301169050602081019050919050565b6000819050602082019050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b60006121e782612228565b9050919050565b60008115159050919050565b6000819050919050565b600061220f826121dc565b9050919050565b6000612221826121dc565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b600061226a826122b9565b9050919050565b600061227c82612283565b9050919050565b600061228e82612228565b9050919050565b60006122a0826122a7565b9050919050565b60006122b282612228565b9050919050565b60006122c4826122cb565b9050919050565b60006122d682612228565b9050919050565b82818337600083830152505050565b60005b8381101561230a5780820151818401526020810190506122ef565b83811115612319576000848401525b50505050565b600061232a8261233b565b9050919050565b6000819050919050565b600061234682612368565b9050919050565b6000819050919050565b6000601f19601f8301169050919050565b60008160601b9050919050565b61237e816121dc565b811461238957600080fd5b50565b612395816121ee565b81146123a057600080fd5b50565b6123ac81612204565b81146123b757600080fd5b50565b6123c381612216565b81146123ce57600080fd5b50565b6123da81612248565b81146123e557600080fd5b5056fea164736f6c634300060c000a
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106100cf5760003560e01c8063867519c61161008c5780639f255626116100665780639f255626146101fe578063b5021b161461021a578063d2c83b9a14610236578063f92c5f7c14610254576100cf565b8063867519c6146101a657806387d31313146101c25780639a8a0592146101e0576100cf565b8063231badaf146100d4578063392e53cd146100f0578063485cc9551461010e5780635afaa7bb1461012a57806373e5a13f1461014657806376db2b4c14610176575b600080fd5b6100ee60048036038101906100e99190611438565b610284565b005b6100f861037f565b6040516101059190611e71565b60405180910390f35b610128600480360381019061012391906115e0565b6103d5565b005b610144600480360381019061013f9190611563565b610561565b005b610160600480360381019061015b919061165d565b610739565b60405161016d9190611e8c565b60405180910390f35b610190600480360381019061018b919061161c565b61075e565b60405161019d9190611e8c565b60405180910390f35b6101c060048036038101906101bb91906113b9565b610788565b005b6101ca610799565b6040516101d79190611f07565b60405180910390f35b6101e86107bf565b6040516101f591906120a2565b60405180910390f35b610218600480360381019061021391906114f7565b6107c5565b005b610234600480360381019061022f9190611438565b6107d5565b005b61023e6108d1565b60405161024b9190611eec565b60405180910390f35b61026e60048036038101906102699190611390565b6108f7565b60405161027b91906120a2565b60405180910390f35b600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548411610305576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102fc90611f82565b60405180910390fd5b60006103258261031788888888610953565b6109b690919063ffffffff16565b905084600460008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555061037786828686610a71565b505050505050565b60008073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163273ffffffffffffffffffffffffffffffffffffffff1614610463576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045a90611f42565b60405180910390fd5b60008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555081600260006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055507f908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e6326040516105559190611def565b60405180910390a15050565b60008251116105a5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161059c90611fc2565b60405180910390fd5b600080600090505b83518110156106f35760003073ffffffffffffffffffffffffffffffffffffffff168583815181106105db57fe5b60200260200101516040516105f09190611d0b565b6000604051808303816000865af19150503d806000811461062d576040519150601f19603f3d011682016040523d82523d6000602084013e610632565b606091505b505090508315610681578061067c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161067390611f62565b60405180910390fd5b610697565b80801561068c575082155b1561069657600192505b5b7f361c14722cc344132c73396113f7164232448b09c544a149f09048648b43d872338684815181106106c557fe5b6020026020010151836040516106dd93929190611e0a565b60405180910390a15080806001019150506105ad565b5080610734576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161072b90612082565b60405180910390fd5b505050565b60006107578260000151836020015184604001518560600151610953565b9050919050565b600061078182600001518360200151846040015185606001518660800151610edc565b9050919050565b61079483338484610a71565b505050565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b6107d133338484610a71565b5050565b600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548411610856576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161084d90611f82565b60405180910390fd5b600061087782610869888888883a610edc565b6109b690919063ffffffff16565b905084600460008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506108c986828686610a71565b505050505050565b600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600061094c6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054610f4290919063ffffffff16565b9050919050565b60006109ac7f6848d0622081db2451400280dead7a739a080cb93852607c381af11e289769b286868661098587610f97565b6040516020016109989493929190611c6e565b604051602081830303815290604052610ffa565b9050949350505050565b60008060009050604183511415610a675760008060006020860151925060408601519150606086015160001a9050601b8160ff1610156109f757601b810190505b601b8160ff161480610a0c5750601c8160ff16145b15610a635760018782858560405160008152602001604052604051610a349493929190611ea7565b6020604051602081039080840390855afa158015610a56573d6000803e3d6000fd5b5050506020604051035193505b5050505b8091505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff161415610ae1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ad890611fa2565b60405180910390fd5b6000825111610b25576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b1c90612062565b60405180910390fd5b8151815114610b69576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610b6090612002565b60405180910390fd5b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614610d3d57600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb890d3f85856040518363ffffffff1660e01b8152600401610bf9929190611e48565b60206040518083038186803b158015610c1157600080fd5b505afa158015610c25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c4991906115b7565b80610cfd5750600260009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663bb890d3f85856040518363ffffffff1660e01b8152600401610cac929190611e48565b60206040518083038186803b158015610cc457600080fd5b505afa158015610cd8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cfc91906115b7565b5b610d3c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d3390611fe2565b60405180910390fd5b5b600080600090505b8251811015610ed457600073ffffffffffffffffffffffffffffffffffffffff16848281518110610d7257fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff161415610dd1576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610dc890611f22565b60405180910390fd5b838181518110610ddd57fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff16838281518110610e0757fe5b60200260200101518787604051602001610e2393929190611d22565b604051602081830303815290604052604051610e3f9190611d0b565b6000604051808303816000865af19150503d8060008114610e7c576040519150601f19603f3d011682016040523d82523d6000602084013e610e81565b606091505b50508092505081610ec7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ebe90612022565b60405180910390fd5b8080600101915050610d45565b505050505050565b6000610f377f6f4e1b2b1e5e49f4269e19e16e67a00cb0a796d96d30be3e4b540d3732e8bcad878787610f0e88610f97565b87604051602001610f23959493929190611cb4565b604051602081830303815290604052610ffa565b905095945050505050565b600080828401905083811015610f8d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610f8490612042565b60405180910390fd5b8091505092915050565b60608060008351905060005b81811015610fef5782858281518110610fb857fe5b6020026020010151604051602001610fd1929190611d5b565b60405160208183030381529060405292508080600101915050610fa3565b508192505050919050565b60006110336001543085856040516020016110189493929190611da5565b6040516020818303038152906040528051906020012061103b565b905092915050565b60008160405160200161104e9190611d7f565b604051602081830303815290604052805190602001209050919050565b60008135905061107a81612375565b92915050565b600082601f83011261109157600080fd5b81356110a461109f826120ea565b6120bd565b915081818352602084019350602081019050838560208402820111156110c957600080fd5b60005b838110156110f957816110df888261106b565b8452602084019350602083019250506001810190506110cc565b5050505092915050565b600082601f83011261111457600080fd5b813561112761112282612112565b6120bd565b9150818183526020840193506020810190508360005b8381101561116d578135860161115388826111a1565b84526020840193506020830192505060018101905061113d565b5050505092915050565b6000813590506111868161238c565b92915050565b60008151905061119b8161238c565b92915050565b600082601f8301126111b257600080fd5b81356111c56111c08261213a565b6120bd565b915080825260208301602083018583830111156111e157600080fd5b6111ec8382846122dd565b50505092915050565b600081359050611204816123a3565b92915050565b600081359050611219816123ba565b92915050565b600060a0828403121561123157600080fd5b61123b60a06120bd565b9050600061124b8482850161106b565b600083015250602061125f8482850161137b565b602083015250604082013567ffffffffffffffff81111561127f57600080fd5b61128b84828501611080565b604083015250606082013567ffffffffffffffff8111156112ab57600080fd5b6112b784828501611103565b60608301525060806112cb8482850161137b565b60808301525092915050565b6000608082840312156112e957600080fd5b6112f360806120bd565b905060006113038482850161106b565b60008301525060206113178482850161137b565b602083015250604082013567ffffffffffffffff81111561133757600080fd5b61134384828501611080565b604083015250606082013567ffffffffffffffff81111561136357600080fd5b61136f84828501611103565b60608301525092915050565b60008135905061138a816123d1565b92915050565b6000602082840312156113a257600080fd5b60006113b08482850161106b565b91505092915050565b6000806000606084860312156113ce57600080fd5b60006113dc8682870161106b565b935050602084013567ffffffffffffffff8111156113f957600080fd5b61140586828701611080565b925050604084013567ffffffffffffffff81111561142257600080fd5b61142e86828701611103565b9150509250925092565b600080600080600060a0868803121561145057600080fd5b600061145e8882890161106b565b955050602061146f8882890161137b565b945050604086013567ffffffffffffffff81111561148c57600080fd5b61149888828901611080565b935050606086013567ffffffffffffffff8111156114b557600080fd5b6114c188828901611103565b925050608086013567ffffffffffffffff8111156114de57600080fd5b6114ea888289016111a1565b9150509295509295909350565b6000806040838503121561150a57600080fd5b600083013567ffffffffffffffff81111561152457600080fd5b61153085828601611080565b925050602083013567ffffffffffffffff81111561154d57600080fd5b61155985828601611103565b9150509250929050565b6000806040838503121561157657600080fd5b600083013567ffffffffffffffff81111561159057600080fd5b61159c85828601611103565b92505060206115ad85828601611177565b9150509250929050565b6000602082840312156115c957600080fd5b60006115d78482850161118c565b91505092915050565b600080604083850312156115f357600080fd5b6000611601858286016111f5565b92505060206116128582860161120a565b9150509250929050565b60006020828403121561162e57600080fd5b600082013567ffffffffffffffff81111561164857600080fd5b6116548482850161121f565b91505092915050565b60006020828403121561166f57600080fd5b600082013567ffffffffffffffff81111561168957600080fd5b611695848285016112d7565b91505092915050565b60006116aa83836116d4565b60208301905092915050565b6116bf8161225f565b82525050565b6116ce816121dc565b82525050565b6116dd816121dc565b82525050565b6116f46116ef826121dc565b61231f565b82525050565b600061170582612176565b61170f8185612199565b935061171a83612166565b8060005b8381101561174b578151611732888261169e565b975061173d8361218c565b92505060018101905061171e565b5085935050505092915050565b611761816121ee565b82525050565b611770816121fa565b82525050565b611787611782826121fa565b612331565b82525050565b600061179882612181565b6117a281856121a4565b93506117b28185602086016122ec565b6117bb81612357565b840191505092915050565b60006117d182612181565b6117db81856121b5565b93506117eb8185602086016122ec565b80840191505092915050565b61180081612271565b82525050565b61180f81612295565b82525050565b6000611822601b836121c0565b91507f476174657761793a2063616e6e6f742073656e6420746f2030783000000000006000830152602082019050919050565b6000611862601c836121d1565b91507f19457468657265756d205369676e6564204d6573736167653a0a3332000000006000830152601c82019050919050565b60006118a2602f836121c0565b91507f496e697469616c697a61626c653a2074782e6f726967696e206973206e6f742060008301527f74686520696e697469616c697a657200000000000000000000000000000000006020830152604082019050919050565b60006119086017836121c0565b91507f476174657761793a2062617463682072657665727465640000000000000000006000830152602082019050919050565b60006119486032836121c0565b91507f476174657761793a206e6f6e6365206973206c6f776572207468616e2063757260008301527f72656e74206163636f756e74206e6f6e636500000000000000000000000000006020830152604082019050919050565b60006119ae6025836121c0565b91507f476174657761793a2063616e6e6f742073656e642066726f6d2030783020616360008301527f636f756e740000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611a146026836121c0565b91507f476174657761793a2063616e6e6f742064656c656761746520656d707479206260008301527f61746368657300000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611a7a6028836121c0565b91507f476174657761793a2073656e646572206973206e6f7420746865206163636f7560008301527f6e74206f776e65720000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611ae06016836121c0565b91507f476174657761793a20696e76616c6964206261746368000000000000000000006000830152602082019050919050565b6000611b206023836121c0565b91507f476174657761793a206261746368207472616e73616374696f6e20726576657260008301527f74656400000000000000000000000000000000000000000000000000000000006020830152604082019050919050565b6000611b86601e836121c0565b91507f536166654d6174684c69623a206164646974696f6e206f766572666c6f7700006000830152602082019050919050565b6000611bc66020836121c0565b91507f476174657761793a2063616e6e6f742073656e6420656d7074792062617463686000830152602082019050919050565b6000611c06601d836121c0565b91507f476174657761793a20616c6c20626174636865732072657665727465640000006000830152602082019050919050565b611c4281612248565b82525050565b611c59611c5482612248565b61234d565b82525050565b611c6881612252565b82525050565b6000611c7a82876116e3565b601482019150611c8a8286611c48565b602082019150611c9a82856116fa565b9150611ca682846117c6565b915081905095945050505050565b6000611cc082886116e3565b601482019150611cd08287611c48565b602082019150611ce082866116fa565b9150611cec82856117c6565b9150611cf88284611c48565b6020820191508190509695505050505050565b6000611d1782846117c6565b915081905092915050565b6000611d2e82866117c6565b9150611d3a82856116e3565b601482019150611d4a82846116e3565b601482019150819050949350505050565b6000611d6782856117c6565b9150611d7382846117c6565b91508190509392505050565b6000611d8a82611855565b9150611d968284611776565b60208201915081905092915050565b6000611db18287611c48565b602082019150611dc182866116e3565b601482019150611dd18285611776565b602082019150611de182846117c6565b915081905095945050505050565b6000602082019050611e0460008301846116b6565b92915050565b6000606082019050611e1f60008301866116b6565b8181036020830152611e31818561178d565b9050611e406040830184611758565b949350505050565b6000604082019050611e5d60008301856116c5565b611e6a60208301846116c5565b9392505050565b6000602082019050611e866000830184611758565b92915050565b6000602082019050611ea16000830184611767565b92915050565b6000608082019050611ebc6000830187611767565b611ec96020830186611c5f565b611ed66040830185611767565b611ee36060830184611767565b95945050505050565b6000602082019050611f0160008301846117f7565b92915050565b6000602082019050611f1c6000830184611806565b92915050565b60006020820190508181036000830152611f3b81611815565b9050919050565b60006020820190508181036000830152611f5b81611895565b9050919050565b60006020820190508181036000830152611f7b816118fb565b9050919050565b60006020820190508181036000830152611f9b8161193b565b9050919050565b60006020820190508181036000830152611fbb816119a1565b9050919050565b60006020820190508181036000830152611fdb81611a07565b9050919050565b60006020820190508181036000830152611ffb81611a6d565b9050919050565b6000602082019050818103600083015261201b81611ad3565b9050919050565b6000602082019050818103600083015261203b81611b13565b9050919050565b6000602082019050818103600083015261205b81611b79565b9050919050565b6000602082019050818103600083015261207b81611bb9565b9050919050565b6000602082019050818103600083015261209b81611bf9565b9050919050565b60006020820190506120b76000830184611c39565b92915050565b6000604051905081810181811067ffffffffffffffff821117156120e057600080fd5b8060405250919050565b600067ffffffffffffffff82111561210157600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561212957600080fd5b602082029050602081019050919050565b600067ffffffffffffffff82111561215157600080fd5b601f19601f8301169050602081019050919050565b6000819050602082019050919050565b600081519050919050565b600081519050919050565b6000602082019050919050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b600082825260208201905092915050565b600081905092915050565b60006121e782612228565b9050919050565b60008115159050919050565b6000819050919050565b600061220f826121dc565b9050919050565b6000612221826121dc565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b600061226a826122b9565b9050919050565b600061227c82612283565b9050919050565b600061228e82612228565b9050919050565b60006122a0826122a7565b9050919050565b60006122b282612228565b9050919050565b60006122c4826122cb565b9050919050565b60006122d682612228565b9050919050565b82818337600083830152505050565b60005b8381101561230a5780820151818401526020810190506122ef565b83811115612319576000848401525b50505050565b600061232a8261233b565b9050919050565b6000819050919050565b600061234682612368565b9050919050565b6000819050919050565b6000601f19601f8301169050919050565b60008160601b9050919050565b61237e816121dc565b811461238957600080fd5b50565b612395816121ee565b81146123a057600080fd5b50565b6123ac81612204565b81146123b757600080fd5b50565b6123c381612216565b81146123ce57600080fd5b50565b6123da81612248565b81146123e557600080fd5b5056fea164736f6c634300060c000a