// Contract: DharmaTransferFacilitatorV1Staging
// Target function: function callGeneric(
73d6000803e3d6000fd5b50506102be565b60405160009081906060906eb45d6593312ac9fde193f3d06336449083818181855afa9150503d80600081146101f0576040519150601f19603f3d011682016040523d82523d6000602084013e6101f5565b606091505b509150915081819061029f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561026457818101518382015260200161024c565b50505050905090810190601f1680156102915780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b508080602001905160208110156102b557600080fd5b50519392505050565b610175806102cd6000396000f3fe608060405261001461000f610016565b61011c565b005b60405160009081906060906eb45d6593312ac9fde193f3d06336449083818181855afa9150503d8060008114610068576040519150601f19603f3d011682016040523d82523d6000602084013e61006d565b606091505b50915091508181906100fd5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156100c25781810151838201526020016100aa565b50505050905090810190601f1680156100ef5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5080806020019051602081101561011357600080fd5b50519392505050565b3660008037600080366000845af43d6000803e80801561013b573d6000f35b3d6000fdfea265627a7a723158203c578cc1552f1d1b48134a72934fe12fb89a29ff396bd514b9a4cebcacc5cacc64736f6c634300050b003200000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024c4d66de8000000000000000000000000";
  bytes28 internal constant _WALLET_CREATION_CODE_FOOTER = bytes28(
    0x00000000000000000000000000000000000000000000000000000000
  );

  DharmaSmartWalletFactoryV1Interface internal constant _WALLET_FACTORY = (
    DharmaSmartWalletFactoryV1Interface(
      0x8D1e00b000e56d5BcB006F3a008Ca6003b9F0033
    )
  );
  
  DharmaKeyRingFactoryV2Interface internal constant _KEYRING_FACTORY = (
    DharmaKeyRingFactoryV2Interface(
      0x7d849544F5cb797AE84aDD18a15A6DE1f12Df5f9
    )
  );

  /**
   * @notice In the constructor, set the initial owner to the transaction
   * submitter and cap the size of transfers to 300 dai worth of dDai.
   */
  constructor() public {    
    // Set the initial limit to 300 Dai.
    _limit = 300 * 1e18;
  }

  /**
   * @notice Prove that `recipientSmartWallet` is a Dharma smart wallet address
   * using `recipientInitialSigningKey`, attempt to modify the allowance of this
   * contract for the sender using the meta-transaction via `senderSmartWallet`,
   * `value`, `expiration`, `salt`, and `signatures`, then transfer `value` dDai
   * from the sender to the recipient, reverting on failure. Only the owner or
   * the transferrer role may call this function.
   * @param senderSmartWallet address The wallet of the sender.
   * @param recipientInitialSigningKey address The input to recipient's wallet.
   * @param recipientSmartWallet address The wallet of the recipient.
   * @param value uint256 The amount of dDai to approve and transfer.
   * @param expiration uint256 The timestamp where the meta-transaction expires.
   * @param salt bytes32 The salt associated with the meta-transaction.
   * @param signatures bytes The signature or signatures associated with the
   * meta-transaction.
   */
  function enactDDaiTransfer(
    address senderSmartWallet,
    address recipientInitialSigningKey, // the receiver's key ring
    address recipientSmartWallet,
    uint256 value,
    uint256 expiration,
    bytes32 salt,
    bytes calldata signatures
  ) external onlyOwnerOr(Role.TRANSFERRER) {
    // Ensure that the receiver has a Dharma Smart Wallet (deployed or not).
    require(
      _isSmartWallet(recipientSmartWallet, recipientInitialSigningKey),
      "Could not resolve receiver's smart wallet using provided signing key."
    );

    // Attempt to modify the allowance.
    _tryApprovalViaMetaTransaction(
      senderSmartWallet, value, expiration, salt, signatures
    );

    // Make the transfer from the sender's smart wallet to the receiver's.
    bool ok = _DDAI.transferFrom(senderSmartWallet, recipientSmartWallet, value);
    require(ok, "Dharma Dai transfer failed.");
  }
  
  /**
   * @notice Prove that `recipientSmartWallet` is a Dharma smart wallet address
   * using `recipientInitialSigningKey`, deploy sender's smart wallet (unless it
   * is already deployed) using `senderInitialSigningKey`, attempt to modify the
   * allowance of this contract for the sender using the meta-transaction via
   * `senderSmartWallet`, `value`, `expiration`, `salt`, and `signatures`, then
   * transfer `value` dDai from the sender to the recipient, reverting on
   * failure. Only the owner or the transferrer role may call this function.
   * @param senderInitialSigningKey address The sender's key ring, used as the
   * input to the smart wallet deployment.
   * @param senderSmartWallet address The wallet of the sender.
   * @param recipientInitialSigningKey address The input to recipient's wallet.
   * @param recipientSmartWallet address The wallet of the recipient.
   * @param value uint256 The amount of dDai to approve and transfer.
   * @param expiration uint256 The timestamp where the meta-transaction expires.
   * @param salt bytes32 The salt associated with the meta-transaction.
   * @param signatures bytes The signature or signatures associated with the
   * meta-transaction.
   */
  function deploySenderWalletAndEnactDDaiTransfer(
    address senderInitialSigningKey, // the sender's key ring
    address senderSmartWallet,
    address recipientInitialSigningKey, // the receiver's key ring
    address recipientSmartWallet,
    uint256 value,
    uint256 expiration,
    bytes32 salt,
    bytes calldata signatures
  ) external onlyOwnerOr(Role.TRANSFERRER) {
    // Ensure the recipient is valid and that dDai amount is under the limit.
    _enforceRecipientAndValue(
      recipientSmartWallet, recipientInitialSigningKey, value
    );

    // Deploy the sender's smart wallet if necessary.
    _deployNewSmartWalletIfNeeded(senderInitialSigningKey, senderSmartWallet);

    // Attempt to modify the allowance.
    _tryApprovalViaMetaTransaction(
      senderSmartWallet, value, expiration, salt, signatures
    );

    // Make the transfer from the sender's smart wallet to the receiver's.
    bool ok = _DDAI.transferFrom(senderSmartWallet, recipientSmartWallet, value);
    require(ok, "Dharma Dai transfer failed.");
  }

  /**
   * @notice Prove that `recipientSmartWallet` is a Dharma smart wallet address
   * using `recipientInitialSigningKey`, deploy sender's key ring (unless it is
   * already deployed) using `senderInitialKeyRingSigningKey`, attempt to modify
   * the allowance of this contract for the sender using the meta-transaction
   * via `senderSmartWallet`, `value`, `expiration`, `salt`, and `signatures`,
   * then transfer `value` dDai from the sender to the recipient, reverting on
   * failure. Only the owner or the transferrer role may call this function.
   * @param senderInitialKeyRingSigningKey address The input to the key ring.
   * @param senderKeyRing address The sender's keyring; the smart wallet input.
   * @param senderSmartWallet address The wallet of the sender.
   * @param recipientInitialSigningKey address The input to recipient's wallet.
   * @param recipientSmartWallet address The wallet of the recipient.
   * @param value uint256 The amount of dDai to approve and transfer.
   * @param expiration uint256 The timestamp where the meta-transaction expires.
   * @param salt bytes32 The salt associated with the meta-transaction.
   * @param signatures bytes The signature or signatures associated with the
   * meta-transaction.
   */
  function deploySenderKeyRingAndEnactDDaiTransfer(
    address senderInitialKeyRingSigningKey, // initial key on sender's key ring
    address senderKeyRing, // the sender's key ring
    address senderSmartWallet,
    address recipientInitialSigningKey, // the receiver's key ring
    address recipientSmartWallet,
    uint256 value,
    uint256 expiration,
    bytes32 salt,
    bytes calldata signatures
  ) external onlyOwnerOr(Role.TRANSFERRER) {
    // Ensure the recipient is valid and that dDai amount is under the limit.
    _enforceRecipientAndValue(
      recipientSmartWallet, recipientInitialSigningKey, value
    );

    // Deploy the sender's key ring if necessary.
    _deployNewKeyRingIfNeeded(senderInitialKeyRingSigningKey, senderKeyRing);

    // Attempt to modify the allowance.
    _tryApprovalViaMetaTransaction(
      senderSmartWallet, value, expiration, salt, signatures
    );

    // Make the transfer from the sender's smart wallet to the receiver's.
    bool ok = _DDAI.transferFrom(senderSmartWallet, recipientSmartWallet, value);
    require(ok, "Dharma Dai transfer failed.");
  }

  /**
   * @notice Prove that `recipientSmartWallet` is a Dharma smart wallet address
   * using `recipientInitialSigningKey`, deploy sender's key ring (unless it is
   * already deployed) using `senderInitialKeyRingSigningKey`, deploy sender's
   * smart wallet (unless it is already deployed) using `senderKeyRing`, attempt
   * to modify the allowance of this contract for the sender using the
   * meta-transaction via `senderSmartWallet`, `value`, `expiration`, `salt`,
   * and `signatures`, then transfer `value` dDai from the sender to the
   * recipient, reverting on failure. Only the owner or the transferrer role may
   * call this function.
   * @param senderInitialKeyRingSigningKey address The input to the key ring.
   * @param senderKeyRing address The sender's keyring; the smart wallet input.
   * @param senderSmartWallet address The wallet of the sender.
   * @param recipientInitialSigningKey address The input to recipient's wallet.
   * @param recipientSmartWallet address The wallet of the recipient.
   * @param value uint256 The amount of dDai to approve and transfer.
   * @param expiration uint256 The timestamp where the meta-transaction expires.
   * @param salt bytes32 The salt associated with the meta-transaction.
   * @param signatures bytes The signature or signatures associated with the
   * meta-transaction.
   */
  function deploySenderKeyRingAndWalletAndEnactDDaiTransfer(
    address senderInitialKeyRingSigningKey, // initial key on sender's key ring
    address senderKeyRing, // the sender's key ring
    address senderSmartWallet,
    address recipientInitialSigningKey, // the receiver's key ring
    address recipientSmartWallet,
    uint256 value,
    uint256 expiration,
    bytes32 salt,
    bytes calldata signatures
  ) external onlyOwnerOr(Role.TRANSFERRER) {
    // Ensure the recipient is valid and that dDai amount is under the limit.
    _enforceRecipientAndValue(
      recipientSmartWallet, recipientInitialSigningKey, value
    );

    // Deploy the sender's key ring if necessary.
    _deployNewKeyRingIfNeeded(senderInitialKeyRingSigningKey, senderKeyRing);

    // Deploy the sender's smart wallet if necessary.
    _deployNewSmartWalletIfNeeded(senderKeyRing, senderSmartWallet);

    // Attempt to modify the allowance.
    _tryApprovalViaMetaTransaction(
      senderSmartWallet, value, expiration, salt, signatures
    );

    // Make the transfer from the sender's smart wallet to the receiver's.
    bool ok = _DDAI.transferFrom(senderSmartWallet, recipientSmartWallet, value);
    require(ok, "Dharma Dai transfer failed.");
  }

  /**
   * @notice Transfer `amount` of ERC20 token `token` to `recipient`. Only the
   * owner may call this function.
   * @param token ERC20Interface The ERC20 token to transfer.
   * @param recipient address The account to transfer the tokens to.
   * @param amount uint256 The amount of tokens to transfer.
   * @return A boolean to indicate if the transfer was successful - note that
   * unsuccessful ERC20 transfers will usually revert.
   */
  function withdraw(
    ERC20Interface token, address recipient, uint256 amount
  ) external onlyOwner returns (bool success) {
    // Transfer the token to the specified recipient.
    success = token.transfer(recipient, amount);
  }