Skip to content

App Integration

Aaron Li edited this page Sep 9, 2021 · 5 revisions

1wallet App Integration

Currently, 1wallet supports app integration via in-browser redirect or embedded iframe (explained below). Demo code is available at https://github.com/hypnagonia/1wallet-integration-demo. The demo app is hosted at https://onewallet-integration.web.app/.

Several other integration methods are being evaluated and implemented. Some of them are discussed in #73. We are also planning to add integrations to common libraries such as via a web3 provider. Note that, since 1wallet requires the user to provide 6-digit (or 12-digit) code from the authenticator for every transaction, the integration flow would require the user to do so at some point in the 1wallet UI.

This should not be a problem for most apps that work by sending a request to the wallet (i.e. MetaMask) for every transaction. But for a small category of apps that intend to act a wallet (typically by getting a copy of the user's private key and initialize a wallet instance in the app), it should be understood that the same thing cannot be done with 1wallet. There is no private key, so there is nothing to load. The local data stored by 1wallet is 64MB, so passing it into your app would be non-trivial if your app intends to act as a wallet.

Redirect

The simplest way to support 1wallet with your app is through redirect integration. Using this method, your app will need to redirect the user to a URL similar to https://1wallet.crazy.one/auth/[action]?[params=values], where action and its corresponding params and values are one of the following. The redirect could be through a new browser tab, or a pop up window, so the user does not need to leave your app to complete the flow. The user will have a chance to review your request at their 1wallet UI, and make a decision on whether they would approve or reject your request. In either case, the user would be redirected back to your app at a callback URL you specified in params and values. The state of your request (e.g. approved / rejected, transaction id, user wallet address, and others) will be passed to the the callback URL.

Base URL

Unless otherwise specified, all APIs should have the base URL https://1wallet.crazy.one/auth prepended to the routes. For example, the URL for connect would be https://1wallet.crazy.one/auth/connect. All requests are GET requests. Parameters and values are passed in as query strings. e.g. https://1wallet.crazy.one/auth/connect?caller=Bob&callback=....

Common Request Parameters

  • caller: URL encoded string (e.g. Tip%20Jar), the name of the app that made the request.
  • callback: Base64 encoded string, the callback URL which the user would be redirected back to, after they made a decision on whether to approve or reject the request. Example: aHR0cHM6Ly9nb29nbGUuY29t (decodes to https://google.com)
  • network: (optional) the network the app intends to use. Must be one of "harmony-mainnet" or "harmony-testnet". In local testing environment you may use "eth-ganache" as well, provided that a local relayer and web client are running with ganache enabled.

For address-like parameters, hexidecimal checksum is expected (e.g. 0x37CCbeAa1d176f77227AEa39BE5888BF8768Bf85) unless otherwise specified. Addresses in bech32 and hexidecimal lowercase formats are also supported, but are not advised.

All parameters are required, unless otherwise specified as (optional).

Common Callback Parameters

  • success: 0 or 1, indicates whether the user has rejected (0) or approved (1) the request from the app.

Connect

Allows the app to retrieve a 1wallet address owned by the user. Note that, this endpoint does not verify ownership. Technically the user can spoof an address and call the callback URL with any address they want. If your app needs verification of address ownership, you should consider sending a Payment Request or Signing Request (see below sections).

Endpoint: /connect

Request Parameters

None (only common parameters are used)

Callback Parameters

  • address: the address of the wallet the user chooses to connect with

Screenshots

Payment Request

Allows the app to ask the user to send payment to a specific address. The app may optionally specify an address which the user must make the payment from. The payment may be in the form of tokens (ERC-20, ERC-721, ERC-1155).

Endpoint: /pay

Request Parameters

  • amount: the amount requested, in wei or otherwise lowest divisible unit (if a payment using a token is requested). Example: 1000000000000000000 (=1.0 ONE)
  • dest: the address you want the user to make payment to.
  • from: (optional) the address of the user's wallet which the payment must be made from. If the user does not have this 1wallet address, the request cannot proceed (thus the user must reject the request). If this parameter is unspecified, the user would be allowed to pick any 1wallet address they own to complete the payment request.

At this time, payment by token is not supported, but they will be added very soon. You will need to provide the token's contract address in the parameter tokenContractAddress. If the token is an NFT, you will also need to specify token ID in tokenId and token type (ERC721 or ERC1155) in tokenType.

Callback Parameters

  • txId: the transaction id of the payment. The app should verify whether the transaction was successful and whether payment was received. Since a 1wallet does not revert unless the user provides invalid proof (authenticator codes and local partial proofs), you should check the internal calls of the transaction and determine whether the internal call that actually sends the funds was successful and indeed delivered the specified amount of funds to the destination address. Errors are emitted as events. The easiest way to ensure the transaction was successful is to check balances on the destination address.

Screenshots

Select wallet and pay

Cannot find specified wallet

Pay from specific address

User authorizing payment with authenticator code

Call Request

Allows the app to ask the user to call a function at a specific contract address using their 1wallet, and optionally send payment to that contract (via the function, which must be payable in this case). The caller (i.e msg.sender) of that function will be the user's 1wallet address.

The function, parameters, and contract address will be shown to the user. The app may provide a human-readable comment to explain to the user what it is trying to do.

Request Parameters

  • calldata: a base64 encoded stringified JSON object describing the calldata1. The JSON object has three fields:
    • method: a string, corresponds to the function's signature, for example, commit(bytes32,bytes32,bytes32) is the signature for the smart contract function function commit(bytes32 hash, bytes32 paramsHash, bytes32 verificationHash) external;
    • parameters: an array of name-value pairs correspond to the parameters of the called function. Using above example, the parameters could be [{"name":"hash", "value":"0x1234..."},{"name":"paramsHash", "value":"0xffff..."},{"name":"verificationHash", "value":"0xffff..."}]
    • comment: (optional) a string, provides a human-readable explanation to show the user what you are asking for.
  • dest: the address of the contract you want to call.
  • amount: (optional) the amount you want the user to pay to the contract at the same time, in wei. Example: 1000000000000000000 (=1.0 ONE). If the amount is non-zero, the method on the dest contract must be payable, otherwise the call would be reverted by the dest contract, even though the 1wallet transaction itself would still be successful (while emitting a log indicating the dest contract reverted).
  • from: (optional) the address of the user's 1wallet which the call must be made from. If the user does not have this 1wallet address, the request cannot proceed (thus the user must reject the request). If this parameter is unspecified, the user would be allowed to pick any 1wallet address they own to complete the payment request.

[1] Here is an example of calldata: eyJtZXRob2QiOiJyZW5ldyh1aW50MzIsYnl0ZXM0KSIsInBhcmFtZXRlcnMiOlt7Im5hbWUiOiJwZXJpb2QiLCJ2YWx1ZSI6MX0seyJuYW1lIjoic2lnbmF0dXJlIiwidmFsdWUiOiIweDEyMzQ1Njc4In1dLCJjb21tZW50IjoidGVzdGluZyJ9

Callback Parameters

  • txId: the transaction id of the call. The app should verify whether the call was successful and whether payment was received. 1wallet does not revert a transaction unless the user provides invalid proof. You should check the internal calls of the transaction, find the related external calls, and determine whether the calls are successful. Two types of events can be emitted from the call. See code for more information:
    • event ExternalCallCompleted(address contractAddress, uint256 amount, bytes data, bytes ret): here, data is the ABI encoded data (with function signatures and parameters) for the call, and ret is the raw data returned by the call in ABI encoded bytes format, according to ABI specification.
    • event ExternalCallFailed(address contractAddress, uint256 amount, bytes data, bytes ret): the format is same as above

Screenshots

Request Details

User Authorizing Request

Signing Request

Allows the app the ask the user to sign a message using their 1wallet. You may choose whether to add standard Ethereum signed message header to the message (see EIP-191). The signature will be verifiable using isValidSignature(bytes32,bytes) method on 1wallet's contract, as described in EIP-1271.

Request Parameters

  • message: a base64 encoded string of the message itself (not including the EIP-191 header)
  • raw: (optional, defaults to false) a boolean string (0 or false indicates false value) indicating whether standard signing header (EIP-191) should be prepended to the message or not. If the value is true, the header would not be prepended
  • duration (optional) how long the signature should remain valid, in milliseconds. If unspecified, the signature will remain valid permanently.
  • from: (optional) the address of the user's 1wallet which the signing must be done from. If the user does not have this 1wallet address, the request cannot proceed (thus the user must reject the request). If this parameter is unspecified, the user would be allowed to pick any 1wallet address they own to complete the payment request.

Callback Parameters

  • hash: the 32-byte keccak256 hash of the message (with or without header) which you provided, in hexidecimal representation (0x...)
  • signature: the 32-byte signature of the 1wallet corresponds to the message, such that isValidSignature(hash, signature) call on the 1wallet address will return true before the signature expires (valid for the duration you specified)
  • txId: the transaction id of the call. Again, a successful transaction does not indicate 1wallet succesfully recorded the signature. You should call isValidSignature(hash, signature) on the 1wallet to verify the signature is indeed valid, and optionally calling lookupSignature(hash) to check the expiry time. You could also use listSignatures(start, end) to show all the signatures on the 1wallet for debugging. If things went wrong, you could check the internal calls of the transaction and identify the relevant external calls to determine the cause. Possible events for a signing request are provided in this section of the code

Screenshots

Request Details

User Authorizing Request